s3c2440 的ADC驱动
来源:互联网 发布:js array push object 编辑:程序博客网 时间:2024/05/16 12:44
驱动写好了很久了,只是由于之前网络坏了,没法通过网络挂载加载程序,后面使用rz下载的
驱动使用混合设备,即misc_devices 来实现自动生成节点,这一点很不错,不用自己整一个class。
还有一点需要说明的是,驱动open时,初始化 dev里面的等待队列头。然后在读的时候,若不可读
使用wait_envent_interruptible(&adc_dev.adc_wq,adc_sta)来阻塞读的进程
当有数据可读的时候,发生中断,唤醒等待队列中的进程,使读继续。
源码如下:
#include <linux/miscdevice.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/irq.h>#include <linux/moduleparam.h>#include <linux/io.h>//#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <asm/irq.h>#include <asm/uaccess.h>//#include <asm/unistd.h>//#include <arch/map.h>//#include <arch/regs-gpio.h>//#include <arch/regs-adc.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/platform_device.h>#include <linux/list.h>#include <linux/err.h>#include <linux/clk.h>#include <linux/interrupt.h>#include <linux/io.h>#include <plat/regs-adc.h>#include <plat/adc.h>static void __iomem *adc_base; //ÓÃÓÚ±£´æÓ³ÉäºóµÄÐéÄâµØÖ·#define ADCCON (adc_base + S3C2410_ADCCON) //ADC control#define ADCTSC (adc_base + S3C2410_ADCTSC) //ADC touch screen control#define ADCDLY (adc_base + S3C2410_ADCDLY) //ADC start or Interval Delay#define ADCDAT0 (adc_base + S3C2410_ADCDAT0) //ADC conversion data 0#define ADCDAT1 (adc_base + S3C2410_ADCDAT1) //ADC conversion data 1#define ADCUPDN (adc_base + 0x14) //Stylus Up/Down interrupt status/* ADCCON¼Ä´æÆ÷ÉèÖà */#define PRESCALE_DIS (0 << 14) //Ô¤·ÖƵ½ûÖ¹#define PRESCALE_EN (1 << 14) //Ô¤·ÖƵʹÄÜ#define PRSCVL(x) ((x) << 6) //Ô¤·ÖƵϵÊý£¬0~255Ö®¼ä#define ADC_INPUT(x) ((x) << 3) //ÉèÖÃÄ£ÄâÐźÅÊäÈëƵµÀ#define ADC_START (1 << 0) //ͨ¹ý¶ÁÈ¡Æô¶¯ADC#define ADC_ENDCVT (1 << 15) //ת»»½áÊø±êÖ¾/* adcʱÖÓ */static struct clk *adc_clk;#define DEVICE_NAME "jakilladc"typedef struct { wait_queue_head_t adc_waitq; //adc¹¤×÷¶ÓÁÐ int channel; int prescale;} ADC_DEV;static ADC_DEV adc_dev;static volatile int ev_adc = 0; //adc״̬±ê¼Ç£¬1±íʾÊý¾Ý¿É¶Ástatic int adc_data;DECLARE_MUTEX(ADC_LOCK);/* ÉèÖÃÊäÈëƵµÀΪch£¬Ô¤·ÖƵϵÊýΪprescale£¬²¢Ê¹ÄÜADCת»» */static void start_adc(int ch, int prescale){ unsigned int tmp; tmp = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT(ch); writel(tmp, ADCCON); tmp = readl(ADCCON); tmp |= ADC_START; writel(tmp, ADCCON);}/* adcÖжϴ¦Àíº¯Êý */static irqreturn_t adc_irq(int irq, void *dev_id){if (!ev_adc) {/* ¶ÁÈ¡adcת»»½á¹û£¬ÊÖ²áÉÏ¿ÉÒԵóöת»»½á¹ûλÓÚ9£º0λ */adc_data = readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;ev_adc = 1; //±ê¼ÇÊý¾Ý¿É¶Áwake_up_interruptible(&adc_dev.adc_waitq);}return IRQ_HANDLED;}static int adc_open(struct inode *inode, struct file *file){ int ret;/* ³õʼ»¯adcµÈ´ý¶ÓÁÐ */init_waitqueue_head(&(adc_dev.adc_waitq));ret = request_irq(IRQ_ADC,adc_irq, IRQF_SHARED, DEVICE_NAME, &adc_dev);if (ret) {printk(KERN_ERR "IRQ%d error %dn", IRQ_ADC, ret);return -EINVAL;}return 0;}static ssize_t adc_read(struct file *filp, char *buffer, size_t count,loff_t * ppos){ /* »ñÈ¡ÐźÅÁ¿ */if (down_trylock(&ADC_LOCK)){return -EBUSY;}if (!ev_adc){/* µ±Êý¾Ý²»¿É¶Áʱ£¬·Ç×èÈû·½Ê½¶ÁÈ¡Ö±½Ó·µ»Ø */if (filp->f_flags & O_NONBLOCK){return -EAGAIN;} else {/* Æô¶¯adc */start_adc(adc_dev.channel, adc_dev.prescale);/* ×èÈû½ø³Ì£¬µÈ´ýadcת»»Íê³É */wait_event_interruptible(adc_dev.adc_waitq, ev_adc); }}/* ½ø³Ì»½ÐѺ󣬱ê¼Çadc²»¿É¶Á */ev_adc = 0;copy_to_user(buffer, (char *) &adc_data, sizeof(adc_data));/* ÊÍ·ÅÐźÅÁ¿ */up(&ADC_LOCK);return sizeof(adc_data);}static int adc_release(struct inode *inode, struct file *filp){ free_irq(IRQ_ADC,&adc_dev);return 0;}static struct file_operations adc_fops = {.owner = THIS_MODULE,.open = adc_open,.read = adc_read,.release = adc_release,};static struct miscdevice adc_miscdev = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &adc_fops,};static int __init adc_init(void){ int ret;/* ÉèÖÃadcƵµÀºÍÔ¤·ÖƵϵÊý */adc_dev.channel = 0;adc_dev.prescale = 0xff;/* »ñÈ¡ADCʱÖÓ£¬¹ØÓÚʱÖÓ¿ò¼ÜÎÒÄ¿Ç°»¹²»ÉõÁ˽⣬* ¹ý¼¸Ì쿪ʼ·ÖÎö */adc_clk = clk_get(NULL, "adc");if (!adc_clk) {printk(KERN_ERR "failed to find adc clock sourcen");return -ENOENT;}/* ʹÄÜʱÖÓ */clk_enable(adc_clk);/* Ó³ÉäIOÄÚ´æ */adc_base = ioremap(S3C2410_PA_ADC, 0x14);//Ò»¹²20¸ö¶Ë¿Ú£¬¹ÊΪ0x14if (adc_base == NULL) {printk(KERN_ERR"Failed to remap register blockn");ret = -EINVAL;goto err_noclk;}/* ×¢²áÉ豸 */ret = misc_register(&adc_miscdev);if (ret){printk(KERN_ERR"cannot register miscdev on minor=%d (%d)n",MISC_DYNAMIC_MINOR, ret);goto err_nomap;}printk(DEVICE_NAME " initialized!\n");return 0;err_noclk:clk_disable(adc_clk);clk_put(adc_clk);err_nomap:iounmap(adc_base);return ret;}static void __exit adc_exit(void){ free_irq(IRQ_ADC,&adc_dev);iounmap(adc_base);if (adc_clk){clk_disable(adc_clk);clk_put(adc_clk);adc_clk = NULL;}misc_deregister(&adc_miscdev);}module_init(adc_init);module_exit(adc_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("jakill ADC Driver"); #include <linux/miscdevice.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/irq.h>#include <linux/moduleparam.h>#include <linux/io.h>//#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <asm/irq.h>#include <asm/uaccess.h>//#include <asm/unistd.h>//#include <arch/map.h>//#include <arch/regs-gpio.h>//#include <arch/regs-adc.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/platform_device.h>#include <linux/list.h>#include <linux/err.h>#include <linux/clk.h>#include <linux/interrupt.h>#include <linux/io.h>#include <plat/regs-adc.h>#include <plat/adc.h>static void __iomem *adc_base; //ÓÃÓÚ±£´æÓ³ÉäºóµÄÐéÄâµØÖ·#define ADCCON (adc_base + S3C2410_ADCCON) //ADC control#define ADCTSC (adc_base + S3C2410_ADCTSC) //ADC touch screen control#define ADCDLY (adc_base + S3C2410_ADCDLY) //ADC start or Interval Delay#define ADCDAT0 (adc_base + S3C2410_ADCDAT0) //ADC conversion data 0#define ADCDAT1 (adc_base + S3C2410_ADCDAT1) //ADC conversion data 1#define ADCUPDN (adc_base + 0x14) //Stylus Up/Down interrupt status/* ADCCON¼Ä´æÆ÷ÉèÖà */#define PRESCALE_DIS (0 << 14) //Ô¤·ÖƵ½ûÖ¹#define PRESCALE_EN (1 << 14) //Ô¤·ÖƵʹÄÜ#define PRSCVL(x) ((x) << 6) //Ô¤·ÖƵϵÊý£¬0~255Ö®¼ä#define ADC_INPUT(x) ((x) << 3) //ÉèÖÃÄ£ÄâÐźÅÊäÈëƵµÀ#define ADC_START (1 << 0) //ͨ¹ý¶ÁÈ¡Æô¶¯ADC#define ADC_ENDCVT (1 << 15) //ת»»½áÊø±êÖ¾/* adcʱÖÓ */static struct clk *adc_clk;#define DEVICE_NAME "jakilladc"typedef struct { wait_queue_head_t adc_waitq; //adc¹¤×÷¶ÓÁÐ int channel; int prescale;} ADC_DEV;static ADC_DEV adc_dev;static volatile int ev_adc = 0; //adc״̬±ê¼Ç£¬1±íʾÊý¾Ý¿É¶Ástatic int adc_data;DECLARE_MUTEX(ADC_LOCK);/* ÉèÖÃÊäÈëƵµÀΪch£¬Ô¤·ÖƵϵÊýΪprescale£¬²¢Ê¹ÄÜADCת»» */static void start_adc(int ch, int prescale){ unsigned int tmp; tmp = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT(ch); writel(tmp, ADCCON); tmp = readl(ADCCON); tmp |= ADC_START; writel(tmp, ADCCON);}/* adcÖжϴ¦Àíº¯Êý */static irqreturn_t adc_irq(int irq, void *dev_id){if (!ev_adc) {/* ¶ÁÈ¡adcת»»½á¹û£¬ÊÖ²áÉÏ¿ÉÒԵóöת»»½á¹ûλÓÚ9£º0λ */adc_data = readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;ev_adc = 1; //±ê¼ÇÊý¾Ý¿É¶Áwake_up_interruptible(&adc_dev.adc_waitq);}return IRQ_HANDLED;}static int adc_open(struct inode *inode, struct file *file){ int ret;/* ³õʼ»¯adcµÈ´ý¶ÓÁÐ */init_waitqueue_head(&(adc_dev.adc_waitq));ret = request_irq(IRQ_ADC,adc_irq, IRQF_SHARED, DEVICE_NAME, &adc_dev);if (ret) {printk(KERN_ERR "IRQ%d error %dn", IRQ_ADC, ret);return -EINVAL;}return 0;}static ssize_t adc_read(struct file *filp, char *buffer, size_t count,loff_t * ppos){ /* »ñÈ¡ÐźÅÁ¿ */if (down_trylock(&ADC_LOCK)){return -EBUSY;}if (!ev_adc){/* µ±Êý¾Ý²»¿É¶Áʱ£¬·Ç×èÈû·½Ê½¶ÁÈ¡Ö±½Ó·µ»Ø */if (filp->f_flags & O_NONBLOCK){return -EAGAIN;} else {/* Æô¶¯adc */start_adc(adc_dev.channel, adc_dev.prescale);/* ×èÈû½ø³Ì£¬µÈ´ýadcת»»Íê³É */wait_event_interruptible(adc_dev.adc_waitq, ev_adc); }}/* ½ø³Ì»½ÐѺ󣬱ê¼Çadc²»¿É¶Á */ev_adc = 0;copy_to_user(buffer, (char *) &adc_data, sizeof(adc_data));/* ÊÍ·ÅÐźÅÁ¿ */up(&ADC_LOCK);return sizeof(adc_data);}static int adc_release(struct inode *inode, struct file *filp){ free_irq(IRQ_ADC,&adc_dev);return 0;}static struct file_operations adc_fops = {.owner = THIS_MODULE,.open = adc_open,.read = adc_read,.release = adc_release,};static struct miscdevice adc_miscdev = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &adc_fops,};static int __init adc_init(void){ int ret;/* ÉèÖÃadcƵµÀºÍÔ¤·ÖƵϵÊý */adc_dev.channel = 0;adc_dev.prescale = 0xff;/* »ñÈ¡ADCʱÖÓ£¬¹ØÓÚʱÖÓ¿ò¼ÜÎÒÄ¿Ç°»¹²»ÉõÁ˽⣬* ¹ý¼¸Ì쿪ʼ·ÖÎö */adc_clk = clk_get(NULL, "adc");if (!adc_clk) {printk(KERN_ERR "failed to find adc clock sourcen");return -ENOENT;}/* ʹÄÜʱÖÓ */clk_enable(adc_clk);/* Ó³ÉäIOÄÚ´æ */adc_base = ioremap(S3C2410_PA_ADC, 0x14);//Ò»¹²20¸ö¶Ë¿Ú£¬¹ÊΪ0x14if (adc_base == NULL) {printk(KERN_ERR"Failed to remap register blockn");ret = -EINVAL;goto err_noclk;}/* ×¢²áÉ豸 */ret = misc_register(&adc_miscdev);if (ret){printk(KERN_ERR"cannot register miscdev on minor=%d (%d)n",MISC_DYNAMIC_MINOR, ret);goto err_nomap;}printk(DEVICE_NAME " initialized!\n");return 0;err_noclk:clk_disable(adc_clk);clk_put(adc_clk);err_nomap:iounmap(adc_base);return ret;}static void __exit adc_exit(void){ free_irq(IRQ_ADC,&adc_dev);iounmap(adc_base);if (adc_clk){clk_disable(adc_clk);clk_put(adc_clk);adc_clk = NULL;}misc_deregister(&adc_miscdev);}module_init(adc_init);module_exit(adc_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("jakill ADC Driver");测试程序:
jakilladc_main.c
#include <sys/types.h>#include <fcntl.h>#include <stdio.h>#include <signal.h>#include <unistd.h>#include <sys/stat.h>#include <errno.h>#include <stdlib.h> int main(int argc, char **argv) { int fd; fd = open("/dev/jakilladc", 0); if(fd < 0) { printf("Open ADC Device Faild!n"); exit(1); } while(1) { int ret; int data; ret = read(fd, &data, sizeof(data)); if(ret != sizeof(data)) { if(errno!=EAGAIN)printf("read adc_device erro\n"); continue; } else { printf("Read ADC value is: %d \n", data); sleep(1); } } close(fd); return 0; }
open("/dev/jakilladc,0)表示以只读方式打开
编译生成 jakilladc.ko
再insmod jakilladc.ko ls -l /dev/jakill*可看到jakilladc设备
在arm-linux-gcc -static -o jakilltest jakilladc_main.c 生成测试程序
chmod +x jakilltest
使用./jakilltest可以看到串口终端里面产生的数据
点击屏幕 可以看到数据变化的不同,这就是adc转换。
- S3C2440的ADC驱动
- s3c2440 的ADC驱动
- s3c2440 ADC驱动
- s3c2440 ADC驱动
- s3c2440 ADC驱动研究
- 基于linux2.6.30.4的s3c2440的ADC驱动
- s3c2440的ADC触摸屏驱动——学习笔记
- S3C2440的ADC接口
- S3C2440驱动篇—ADC驱动分析
- AMR-linux S3c2440之ADC驱动实现
- S3C2440上ADC驱动实例开发讲解
- S3C2440上ADC驱动实例开发讲解
- ARM Linux S3C2440之ADC驱动实现
- ARM Linux S3C2440之ADC驱动实现
- s3c2440 触摸屏和ADC驱动移植
- S3C2440上ADC驱动实例开发讲解
- S3C2440的ADC实例讲解
- s3c2440 LCD驱动,USB驱动,触摸屏以及ADC驱动移植
- 设置Mysql的主从复制的主要用途
- 用XPath精确定位节点元素
- iOS面试题汇总(一)
- javascript中的或 ||
- 今日作业
- s3c2440 的ADC驱动
- ExportListToExcel
- iOS面试题汇总(二)
- java高级编程面试简答题
- “机遇”到底是什么?
- [10月9日的脚本] 在Microsoft Lync Server2010中得到用户策略分配
- 今年两站结束~两铜惨淡收场
- 16位下的寄存器一览
- boost noncopyable