mini2440驱动分析之adc


1 . ADC_DEV 结构
  1. typedef struct {  
  2.     wait_queue_head_t wait;  
  3.     int channel;  
  4.     int prescale;  
  5. }ADC_DEV;  
 wait      等待队列,进程读取设备,如果没有转换数据,就会睡眠在此队列上
channel   转换通道,s3c2440有八通道的ad,但是只有四个通道AIN[3:0]可以使用,其他四个用于触摸屏
prescale   计算转换速率的时候使用
2 . 变量
    OwnADC   表示是否拥有ADC信号量
    ev_adc   使能ADC标志,用于等待队列的等待条件
    adc_data 转换读出的数据
3 模块初始化,dev_init() 
  1. static int __init dev_init(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     base_addr=ioremap(S3C2410_PA_ADC,0x20);  
  6.         //映射虚拟地址,其中S3C2410_PA_ADC为物理地址  
  7.     if (base_addr == NULL) {  
  8.         printk(KERN_ERR "Failed to remap register block\n");  
  9.         return -ENOMEM;  
  10.     }  
  11.   
  12.     adc_clock = clk_get(NULL, "adc");  
  13.     if (!adc_clock) {  
  14.         printk(KERN_ERR "failed to get adc clock source\n");  
  15.         return -ENOENT;  
  16.     }  
  17.     clk_enable(adc_clock);  
  18.     //设置时钟,adc_clk为clk结构体,定义在arm/mach-mmp/clock.h中,代表一种时钟,系统默认时钟是关闭的,所以这里要使能  
  19.     /* normal ADC */  
  20.     ADCTSC = 0;  
  21.   
  22.     ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);  
  23.         //装载中断处理例程   
  24.     if (ret) {  
  25.         iounmap(base_addr);  
  26.         return ret;  
  27.     }  
  28.   
  29.     ret = misc_register(&misc);  
  30.         //注册杂项设备  
  31.     printk (DEVICE_NAME"\tinitialized\n");  
  32.     return ret;  
  33. }  
      ADC为字符设备,所有注册成杂项设备,接口简单
4 . 设备打开
  1. static int s3c2410_adc_open(struct inode *inode, struct file *filp)  
  2. {  
  3.     init_waitqueue_head(&(adcdev.wait));   //初始化等待队列  
  4.   
  5.     adcdev.channel=0;   //设置转换通道为0,从电路图中可以看到,mini2440的AIN[0]接了一个可调电阻。  
  6.     adcdev.prescale=0xff; //这个是设置转换速率用  
  7.     DPRINTK( "adc opened\n"); //这个宏是模块自己定义的,用于打印调试信息  
  8.     return 0;  
  9. }  
5 . 读操作
  1. static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)  
  2. {  
  3.     char str[20];  
  4.     int value;  
  5.     size_t len;  
  6.     if (down_trylock(&ADC_LOCK) == 0) {  
  7.         //down_trylock不会睡眠,如果获得不了信号量就会返回  
  8.         //每个设备都因该有一个信号量或者自旋锁来保护,以防止并发引起的竞态,这种错误很难调试(ldd3)  
  9.         OwnADC = 1;  
  10.         //代表拥有设备  
  11.         START_ADC_AIN(adcdev.channel, adcdev.prescale);  
  12.     // 这是一个宏定义,就是设置ADCCON寄存器,通道,装换速率,并开始转换,把设置寄存器定义成宏简单明了  
  13.         wait_event_interruptible(adcdev.wait, ev_adc);  
  14.         // 已经开始转换了,等待转换完成中断  
  15.         ev_adc = 0;  
  16.         //装换完成,清完成条件为下次转换做准备  
  17.         DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);  
  18.   
  19.         value = adc_data;  
  20.   
  21.         OwnADC = 0;  
  22.         up(&ADC_LOCK);  
  23.     } else {  
  24.         value = -1;  
  25.     }  
  26.         // 释放信号量,因为不操作设备了,这里用信号量而不是自旋锁,因为拥有自旋锁的代码不允许睡眠  
  27.         // 接下来将转换读出的数据,复制到用户空间  
  28.     len = sprintf(str, "%d\n", value);  
  29.     if (count >= len) {  
  30.         int r = copy_to_user(buffer, str, len);  
  31.         return r ? r : len;  
  32.     } else {  
  33.         return -EINVAL;  
  34.     }  
  35. }  
START_ADC_AIN 定义如下
  1. #define START_ADC_AIN(ch, prescale) \  
  2.     do{ \  
  3.         ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \  
  4.         ADCCON |= ADC_START; \  
  5.     }while(0)  
这个宏,设置装换速率,设置转换通道,并开始ad转换
6 . 中断处理例程
  1. static irqreturn_t adcdone_int_handler(int irq, void *dev_id)  
  2. {  
  3.     if (OwnADC) {  
  4.         adc_data = ADCDAT0 & 0x3ff;  
  5.         //如果现在拥有设备,读取转换结果  
  6.         ev_adc = 1;  
  7.         //设置等待条件为真  
  8.         wake_up_interruptible(&adcdev.wait);  
  9.         //唤醒等待的进程  
  10.     }  
  11.   
  12.     return IRQ_HANDLED;  
  13. }  
7. 其他
       release 函数什么也没有做,只是通知设备关闭了
       模块卸载函数,释放掉中断线,注销设备

相关内容