====本文系本站原创,欢迎转载! 转载请注明出处:http://blog.csdn.net/yyplc====
硬件描述:
S3c2440有一个10-bit的CMOS ADC 模数转换器,支持8个模拟通道输入,10位的分辨率,最高速度可达500KSPS(500 千次/每秒)。
从图中可知:模拟ADC,包含了2部分功能,一部分是触屏功能,另一部分就是普通ADC功能,分别可以产生INT_TC和INT_ADC 两个中断。8个AIN模拟输入(A[3:0],YM,YP,XM,XP)通过一个8路模拟开关MUX进行通道片选。 ADC模块共有20个寄存器。对于普通ADC转换,使用ADCCON 和 ADCDAT0即可完成控制。ADCCON用于控制设置,ADCDAT0保存了转换结果。
驱动程序ADC_DEV.ko:
#include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/clk.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <mach/regs-clock.h> #include <plat/regs-adc.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "ADC_DEV" struct ADC_DEV { wait_queue_head_t wait; int ch; int pres; int data; int flag; }; static struct ADC_DEV adc_dev; static void __iomem *ADC_REG_BASE; static struct clk *adc_clk; #define ADCCON (*(volatile unsigned long *)(ADC_REG_BASE + S3C2410_ADCCON)) #define ADCDAT0 (*(volatile unsigned long *)(ADC_REG_BASE + S3C2410_ADCDAT0)) #define START_ADC(ch,pres) ADCCON = (0x01 | 0x01<<14 | ch<<3 | pres<<6) static ssize_t adc_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) { START_ADC(adc_dev.ch, adc_dev.pres); //启动ADC转换 wait_event_interruptible(adc_dev.wait,adc_dev.flag); //等待ADC转换完成 adc_dev.flag = 0; copy_to_user(buf,(char*)&adc_dev.data,sizeof(adc_dev.data)); return (sizeof(adc_dev.data)); } static int adc_open(struct inode *in, struct file *fp) { adc_dev.ch = 0x02; //输入通道2 adc_dev.pres = 0xff; //prescale : 0 ~255 可选 adc_dev.data = 0; adc_dev.flag = 0; init_waitqueue_head(&(adc_dev.wait)); return 0; } static irqreturn_t adc_done_handler(int irq, void *dev_id) { adc_dev.data = ADCDAT0 & 0x3ff; adc_dev.flag = 1; wake_up_interruptible(&adc_dev.wait); //唤醒等待事件 return IRQ_HANDLED; } static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = adc_open, .read = adc_read, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; static int __init dev_init(void) { int ret; ADC_REG_BASE = ioremap(S3C2410_PA_ADC,0x14); //物理地址映射 adc_clk = clk_get(NULL,"adc"); if (!adc_clk) { return -ENOENT; } clk_enable(adc_clk); //开启adc 时钟,系统开机时默认是关闭的 ret = request_irq(IRQ_ADC,adc_done_handler,IRQF_SHARED,DEVICE_NAME,&adc_dev); //分配中断线 if (ret) { iounmap(ADC_REG_BASE); return ret; } ret = misc_register(&misc); //注册设备 return ret; } static void __exit dev_exit(void) { misc_deregister(&misc); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("itspy"); MODULE_DESCRIPTION("adc driver test");
应用程序adc_test:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <fcntl.h> int main(void) { int fd; char buf[4]; int i,res,adc_data; if ((fd = open("/dev/ADC_DEV",O_RDONLY)) < 0) { printf("open adc_dev failed\n"); return 1; } printf("adc_test result:\n"); for (i= 0; i< 5; i++) { if ((res =read(fd, buf, 4)) != 4) { printf("read adc_dev failed \n"); return 1; } else { adc_data = buf[0] + (int)(buf[1]<<8); printf("adc value = %d (%.2f V) \n",adc_data, adc_data/1024.0*3.3); } } close(fd); return 0; }
测试结果:
[root@WSN /]# ./adc_test
adc_test result:
adc value = 740 (2.38 V)
adc value = 744 (2.40 V)
adc value = 744 (2.40 V)
adc value = 744 (2.40 V)