SIN210学习笔记__ADC模块

作者: liunian__92
上传时间为: 2015-03-10 03:05 PM
2015-03-10
阅读:

ADC,Analog to Digital Converter

ADC就是模拟信号转换成数字信号的转换器,这个东东和DAC是模拟世界和数字世界的桥梁,如今DAC在单片机上已经是标配了,一般都是10/12可配置的,若不是特殊需求,12bit的分辨率在日常使用中也是够用的。当然也有一些单片机竟然配备了24bit的ADC,一般用于医疗电子。

S5PV210 内部集成了10 通道 10/12-bit(可选) 多路复用 ADC

微分线性误差: ± 1.0 LSB (Max.)

积分线性误差: ± 4.0 LSB (Max.)

最大转换速率: 1 MSPS

低功耗

工作电压: 3.3V

模拟信号输入范围: 0 ~ 3.3V

片上集成采样和保持功能

普通转换模式

分离 X/Y 转换模式

自动(连续) X/Y 转换模式

等待中断模式

IDLE, DIDLE, STOP 和 DSTOP 唤醒

两个触摸屏接口

图10-1:AD和触摸屏框架图


AD 驱动,三星公司已经写好,路径是:kernel/arch/arm/mach-s5pv210/adc.c 代码如下:

#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
  
#include   
  
#include   
#include   
#include   
#include   
  
#define ADC_MINOR   131  
#define ADC_INPUT_PIN   _IOW('S', 0x0c, unsigned long)  
  
#define ADC_WITH_TOUCHSCREEN  
  
static struct clk   *adc_clock;  
  
static void __iomem     *base_addr;  
static int adc_port;  
struct s3c_adc_mach_info *plat_data;  
  
  
#ifdef ADC_WITH_TOUCHSCREEN  
static DEFINE_MUTEX(adc_mutex);  
  
static unsigned long data_for_ADCCON;  
static unsigned long data_for_ADCTSC;  
  
static void s3c_adc_save_SFR_on_ADC(void)  
{  
    data_for_ADCCON = readl(base_addr + S3C_ADCCON);  
    data_for_ADCTSC = readl(base_addr + S3C_ADCTSC);  
}  
  
static void s3c_adc_restore_SFR_on_ADC(void)  
{  
    writel(data_for_ADCCON, base_addr + S3C_ADCCON);  
    writel(data_for_ADCTSC, base_addr + S3C_ADCTSC);  
}  
#else  
static struct resource  *adc_mem;  
#endif  
  
static int s3c_adc_open(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
unsigned int s3c_adc_convert(void)  
{  
    unsigned int adc_return = 0;  
    unsigned long data0;  
    unsigned long data1;  
  
    writel((adc_port & 0x7), base_addr + S3C_ADCMUX);  
  
    udelay(10);  
  
    writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_ENABLE_START, base_addr + S3C_ADCCON);  
  
    do {  
        data0 = readl(base_addr + S3C_ADCCON);  
    } while (!(data0 & S3C_ADCCON_ECFLG));  
  
    data1 = readl(base_addr + S3C_ADCDAT0);  
  
    if (plat_data->resolution == 12)  
        adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK_12BIT;  
    else  
        adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK;  
  
    return adc_return;  
}  
  
  
int s3c_adc_get(struct s3c_adc_request *req)  
{  
    unsigned adc_channel = req->channel;  
    int adc_value_ret = 0;  
  
    adc_value_ret = s3c_adc_convert();  
  
    req->callback(adc_channel, req->param, adc_value_ret);  
  
    return 0;  
}  
EXPORT_SYMBOL(s3c_adc_get);  
  
static ssize_t  
s3c_adc_read(struct file *file, char __user *buffer,  
        size_t size, loff_t *pos)  
{  
//  printk("++++++adc read.\n");  
    int  adc_value = 0;  
  
#ifdef ADC_WITH_TOUCHSCREEN  
    mutex_lock(&adc_mutex);  
    s3c_adc_save_SFR_on_ADC();  
#endif  
    printk(KERN_INFO "## delay: %d\n", readl(base_addr + S3C_ADCDLY));  
    adc_value = s3c_adc_convert();  
  
#ifdef ADC_WITH_TOUCHSCREEN  
    s3c_adc_restore_SFR_on_ADC();  
    mutex_unlock(&adc_mutex);  
#endif  
      
//  printk("+++++%d\n",adc_value);  
    if (copy_to_user(buffer, &adc_value, sizeof(unsigned int)))  
        return -EFAULT;  
  
    return sizeof(unsigned int);  
}  
  
  
static int s3c_adc_ioctl(struct inode *inode, struct file *file,  
    unsigned int cmd, unsigned long arg)  
{  
    cmd = ADC_INPUT_PIN;  
    switch (cmd) {  
    case ADC_INPUT_PIN:  
        adc_port = (unsigned int) arg;  
        printk("port=%d\n",arg);  
        if (adc_port >= 4)  
            printk(KERN_WARNING  
                " %d is already reserved for TouchScreen\n",  
                adc_port);  
        return 0;  
  
    default:  
        return -ENOIOCTLCMD;  
    }  
}  
  
static const struct file_operations s3c_adc_fops = {  
    .owner      = THIS_MODULE,  
    .read       = s3c_adc_read,  
    .open       = s3c_adc_open,  
    .ioctl      = s3c_adc_ioctl,  
};  
  
static struct miscdevice s3c_adc_miscdev = {  
    .minor      = ADC_MINOR,  
    .name       = "adc",  
    .fops       = &s3c_adc_fops,  
};  
  
static struct s3c_adc_mach_info *s3c_adc_get_platdata(struct device *dev)  
{  
    if (dev->platform_data != NULL)  
        return (struct s3c_adc_mach_info *) dev->platform_data;  
    else  
        return 0;  
}  
  
/* 
 * The functions for inserting/removing us as a module. 
 */  
  
static int __init s3c_adc_probe(struct platform_device *pdev)  
{  
    printk("+++++++++++++s3c-adc probe.\n");  
    struct resource *res;  
    struct device *dev;  
    int ret;  
    int size;  
  
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
    dev = &pdev->dev;  
  
    if (res == NULL) {  
        dev_err(dev, "no memory resource specified\n");  
        return -ENOENT;  
    }  
  
    size = (res->end - res->start) + 1;  
  
#if !defined(ADC_WITH_TOUCHSCREEN)  
    adc_mem = request_mem_region(res->start, size, pdev->name);  
    if (adc_mem == NULL) {  
        dev_err(dev, "failed to get memory region\n");  
        ret = -ENOENT;  
        goto err_req;  
    }  
#endif  
  
    base_addr = ioremap(res->start, size);  
    if (base_addr ==  NULL) {  
        dev_err(dev, "fail to ioremap() region\n");  
        ret = -ENOENT;  
        goto err_map;  
    }  
  
    adc_clock = clk_get(&pdev->dev, "adc");  
  
    if (IS_ERR(adc_clock)) {  
        dev_err(dev, "failed to fine ADC clock source\n");  
        ret = PTR_ERR(adc_clock);  
        goto err_clk;  
    }  
  
    clk_enable(adc_clock);  
  
    /* read platform data from device struct */  
    plat_data = s3c_adc_get_platdata(&pdev->dev);  
  
    if ((plat_data->presc & 0xff) > 0)  
        writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(plat_data->presc & 0xff), base_addr + S3C_ADCCON);  
    else  
        writel(0, base_addr + S3C_ADCCON);  
  
    /* Initialise registers */  
    if ((plat_data->delay & 0xffff) > 0)  
        writel(plat_data->delay & 0xffff, base_addr + S3C_ADCDLY);  
  
    if (plat_data->resolution == 12)  
        writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT, base_addr + S3C_ADCCON);  
  
    ret = misc_register(&s3c_adc_miscdev);  
    if (ret) {  
        printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n",  
            ADC_MINOR, ret);  
        goto err_clk;  
    }  
      
    printk("+++++++++++++s3c-adc probe successful\n");  
    return 0;  
  
err_clk:  
    clk_disable(adc_clock);  
    clk_put(adc_clock);  
  
err_map:  
    iounmap(base_addr);  
  
#if !defined(ADC_WITH_TOUCHSCREEN)  
err_req:  
    release_resource(adc_mem);  
    kfree(adc_mem);  
#endif  
  
    return ret;  
}  
  
  
static int s3c_adc_remove(struct platform_device *dev)  
{  
    return 0;  
}  
  
#ifdef CONFIG_PM  
static unsigned int adccon, adctsc, adcdly;  
  
static int s3c_adc_suspend(struct platform_device *dev, pm_message_t state)  
{  
    adccon = readl(base_addr + S3C_ADCCON);  
    adctsc = readl(base_addr + S3C_ADCTSC);  
    adcdly = readl(base_addr + S3C_ADCDLY);  
  
    clk_disable(adc_clock);  
  
    return 0;  
}  
  
static int s3c_adc_resume(struct platform_device *pdev)  
{  
    clk_enable(adc_clock);  
  
    writel(adccon, base_addr + S3C_ADCCON);  
    writel(adctsc, base_addr + S3C_ADCTSC);  
    writel(adcdly, base_addr + S3C_ADCDLY);  
  
    return 0;  
}  
#else  
#define s3c_adc_suspend NULL  
#define s3c_adc_resume  NULL  
#endif  
  
static struct platform_driver s3c_adc_driver = {  
       .probe          = s3c_adc_probe,  
       .remove         = s3c_adc_remove,  
       .suspend        = s3c_adc_suspend,  
       .resume         = s3c_adc_resume,  
       .driver      = {  
        .owner  = THIS_MODULE,  
        .name   = "s3c-adc",  
    },  
};  
  
static char banner[] __initdata = KERN_INFO "S5PV210 ADC driver, (c) 2010 Samsung Electronics\n";  
  
int __init s3c_adc_init(void)  
{  
    printk(banner);  
    return platform_driver_register(&s3c_adc_driver);  
}  
  
void __exit s3c_adc_exit(void)  
{  
    platform_driver_unregister(&s3c_adc_driver);  
}  
  
module_init(s3c_adc_init);  
module_exit(s3c_adc_exit);  
  
MODULE_AUTHOR("dsfine.ha@samsung.com");  
MODULE_DESCRIPTION("S5PV210 ADC driver");  
MODULE_LICENSE("GPL");  

驱动已经解决,我们就可以直接写ADC的应用了。这次也是让ADC自动采集AD0端口的值,然后输出到Qt中显示

/*adc.h*/  
#ifndef AD_H  
#define AD_H  
#ifdef __cplusplus  
extern "C" {  
#endif  
  
extern int adc_open(const char *devname);  
extern int adc_ioctl(int ad_ch);  
extern int adc_read();  
extern int adc_close(void);  
extern int adc_fd;  
  
#ifdef __cplusplus  
}  
#endif  
#endif  
  
  
/*adc.c*/  
#include   
#include   
#include   
#include   
#include   
#include "adc.h"  
  
int adc_fd=0;  
  
  
int adc_open(const char*devname)  
{  
  adc_fd=open(devname,O_RDWR);  
  if(adc_fd<0)  
  {  
     printf("open device %s failed.\n",devname);  
     return -1;  
  }  
  printf("AD driver is ok\n");  
  return 0;  
}  
  
int adc_ioctl(int adc_ch)  
{  
  if(adc_ch > 4)  
  {  
    printf("AD Channel %d is reserved for TouchScreen!\n",adc_ch);  
    return -1;  
  }  
  ioctl(adc_fd,'s',adc_ch);  
  printf("AD Channel %d is open\n",adc_ch);  
  return 0;  
}  
int adc_read()  
{  
   usleep(500*1000);  
   int i,sum=0,j=10;  
   while(j--)  
   {  
     read(adc_fd, &i, sizeof(int));  
     sum+=i;  
   }  
  
   printf("AD value :%d\n",i);  
   return i;  
}  
  
  
int adc_close(void){  
 if(adc_fd)  
   close(adc_fd);  
}  

编写Qt应用程序即可通过adc_read()函数读取adc的值,通过adc_ioctl(int acd_ch) 来选择adc的通道(0~4可选,其它SIN210开发板用于触摸屏了,保留)。

adc_open(const char*devname)打开adc驱动文件。

下面是运行的效果图:

                                                   图10-2:AD运行效果图

相关经验
全部评论 ()
条评论
写评论

创建讨论帖子

登录 后参与评论
系统提示