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.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运行效果图