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转换。

原创粉丝点击