zoukankan      html  css  js  c++  java
  • 驱动之字符设备ADC

    ADC 驱动:

    10bit或者12bitCMOS模拟数据转为数字(ADC),它是一个带有10通道模拟输入的,回收(recycling)类型设备,它将输入的模拟信号转化为10bit或者12bit2进制数字编码,最大的转化比率1MSPS5MHZAD转换器时钟。

    范围:Anlog  input  range 0--3.3V 模拟输入电压;

    精度:10/12   3.3V平均分为2n次方,在通过计算获取当前的电压值。。。

    最大转换次数:1 MSPS

    通道: 表示ADC能接多少个设备。。。

    电阻屏,无需转换成电压值、、、

    一、

    如果时钟pclk66MHZ,预分频值为65,10或者12bit转换时间为:

    A/D 转换频率 =  66MHZ/(65 +) = 1MHZ;

    转换时间 = 1/(1MHZ / 5cysles)=1/200khz=5us;

    5MHZ时钟,实际转化后为1M

    二、设计说明:

    A/D转换的数据可以通过中断或者轮询的方式使用。。。。

    ②使用中断的方法:全部的转换时间,从转换的开始到读转换的数据,

    三、查看芯片手册,驱动ADC我们需要操作的4个寄存器分别是:

    ①ADCCON 控制寄存器

    ②ADCDAT0 转换数据寄存器

    ③ADCCLRINT  清中断寄存器

    ④ADCMUX   模拟输入通道位

    四、时钟:一个设备的工作都基于时钟来实现,然而linux内核在系统初始化的过程,屏蔽了大部分的时钟,其中包括ADC的时钟。。。因此,在设备初始化的过程中就必须打开时钟;这里linux内核提供了一种较为简单的方式去修改时钟,使用到一个结构struct clk 使能各个时钟;在arch/arm/mach-板子名/clock.c下可以查看到clk  init_clocks 中包括系统启动时默认enable的时钟。其中一个函数 struct  clk  *adcclk = clk_get(NULL,"adc");获取的值传入clk_enable(clk);

    五、信号量和等待队列
     ①信号量是用来保护临界资源区的一种常用的方法,它的使用方法和自旋锁类似,只有得到信号量
     的进程才能执行临界区代码。。。。但是与自旋锁不同的是:当获取不到信号量时,进程不会原
     地自旋,而是进入睡眠!
     
     ②等待队列:使用等待队列来实现阻塞进程的唤醒。以队列为基础数据结构,与进程调度紧密相接合
     能够用于实现内核中的异步事件通知机制。

    六、①adc_init()

     

     1 static int  my_adc_init(void)
    2 {
    3 int result;
    4 dev_t dev = MKDEV(adc_major, adc_minor);
    5
    6 result = register_chrdev_region(dev, 1, "adc");
    7 if (result < 0)
    8 {
    9 return result;
    10 }
    11 adc_setup_cdev(&adc_dev,&adc_fops);
    12
    13 adccon_va = ioremap(ADCCON,4);
    14 adcdat0_va = ioremap(ADCDAT0,4);
    15 adcmux_va = ioremap(ADCMUX,4);
    16 adcclrint_va = ioremap(ADCCLRINT,4);
    17
    18 adc_clk = clk_get(NULL,"adc");
    19 if (adc_clk == NULL)
    20 {
    21 printk(KERN_ERR "clk_get error.....\n");
    22 return -ENOENT;
    23 }
    24 clk_enable(adc_clk);
    25


    29 printk(KERN_INFO"adc device installed, with major %d\n", adc_major);
    30 return 0;
    31 }

    ②adc_cleanup()

     

     1 static void my_adc_cleanup(void)
    2 {
    3 /*取消映射*/
    4 iounmap(adccon_va);
    5 iounmap(adcdat0_va);
    6 iounmap(adcmux_va);
    7 iounmap(adcclrint_va);
    8
    9 clk_disable(adc_clk); //有时钟的enable,就有时钟的disable
    10 cdev_del(&adc_dev);
    11 unregister_chrdev_region(MKDEV(adc_major, 0), 1);
    12 printk(KERN_INFO"adc device uninstalled........\n");
    13 }

    ③ file_operations

    1 static struct file_operations adc_fops = {
    2 .owner = THIS_MODULE,
    3 .open = my_adc_open,
    4 .read = my_adc_read,
    5 .write = my_adc_write,
    6 .release = my_adc_release,
    7 };

    ④adc_setup_cdev

     1 static void adc_setup_cdev(struct cdev *dev,struct file_operations *fops)
    2 {
    3 int error, devno = MKDEV(adc_major, adc_minor);
    4
    5 cdev_init(dev, fops);
    6 dev->owner = THIS_MODULE;
    7 dev->ops = fops;
    8 error = cdev_add (dev, devno, 1);
    9
    10 if (error)
    11 printk (KERN_NOTICE "Error %d adding adc %d", error, adc_minor);
    12 }

    ⑤my_adc_release

     

    1 static int my_adc_release(struct inode *inode, struct file *filp)
    2 {
    3 free_irq(IRQ_ADC,NULL);
    4
    5 printk(KERN_INFO"adc_interrupt release.....\n");
    6 return 0;
    7 }

     1 static ssize_t my_adc_write(struct file *filp, char *buffer, size_t count)
    2 {
    3 int data;
    4 if (count != sizeof(data))
    5 {
    6 printk("the size of input data must be %d\n",sizeof(data));
    7 return 0;
    8 }
    9 copy_from_user(&data,buffer,count);
    10
    11 return count;
    12 }
    13 static ssize_t my_adc_read(struct file *filp, char *buffer, size_t count)
    14 {
    15 int ret = 0;
    16 /*
    17 * down_interruptible()获取信号量时,对返回值一般要进行判断,如果非0,
    18 * 通常立即返回-ERESTARTSYS 。。。。
    19 * 信号量的获取*/
    20 if (down_interruptible(&adcdev.sem))
    21 return -ERESTARTSYS;
    22 //使能 adccon
    23 //writel((readl(adccon_va) & (~0xff))| (0x1<<16) | ~(0x1<<2),adccon_va);
    24 writel(readl(adccon_va)&(~0x1),adccon_va);
    25
    26 writel(0,adcmux_va); //选择通道0
    27
    28 writel((0x1<<16)|(0x1<<14)|(0xff<<6)|(0x1<<0),adccon_va);
    29
    30
    31 /*
    32 * sleep_on()函数的作用是将目前进程的状态设置为TASK_UNINTERRUPTUBLE,并定义一个等待队列
    33 * 之后把它附属到等待队列列头q,知道资源可获得,q引导的等待队列被唤醒。
    34 * */
    35 sleep_on(&adcdev.wait);
    36 ret = readl(adcdat0_va);
    37 ret &= (0xfff);// 只读取低12位。。。。
    38
    39 copy_to_user(buffer,(char *)&ret,sizeof(ret));
    40
    41 up(&adcdev.sem);//释放信号量,唤醒等待者。。。
    42 return sizeof(ret);
    43 }
    44
    45 static int my_adc_open(struct inode *inode, struct file *filp)
    46 {
    47 int ret;
    48 ret = request_irq(IRQ_ADC, adc_handler,IRQF_DISABLED,"adc", NULL);
    49 if (ret)
    50 {
    51 return ret;
    52 }
    53
    54 /*
    55 * init_MUTEX 该宏用于初始化一个用于互斥的信号量,它把信号量sem设置为1
    56 *
    57 * 初始化互斥信号量,并设置为1; 初始化等待队列*/
    58 init_MUTEX(&adcdev.sem);
    59 init_waitqueue_head(&adcdev.wait);
    60 printk(KERN_INFO"adc open success.....\n");
    61 return 0;
    62 }

    ⑦中断处理函数

     

     1 irqreturn_t adc_handler(int irq,void *dev_id)
    2 {
    3 //flag = 1;
    4 //唤醒。。wake_up();
    5 //wake_up();
    6
    7
    9 /**
    10 * wake_up()操作会唤醒所有以&adcdev.wait作为等待队列头的所有等待队列中所有属于该等待队列
    11 * 头的等待队列对应的进程。。。。
    12 * /
    13 清中断*/
    14 wake_up(&adcdev.wait);
    15 // writel(readl(adcclrint_va) | 0x1,adcclrint_va);
    16 writel(0,adcclrint_va);
    17 return IRQ_HANDLED ;
    18 }







  • 相关阅读:
    九度OJ 1333:考研海报 (区间操作)
    九度OJ 1326:Waiting in Line(排队) (模拟)
    九度OJ 1325:Battle Over Cities(城市间的战争) (并查集)
    九度OJ 1324:The Best Rank(最优排名) (排序)
    九度OJ 1323:World Cup Betting(世界杯) (基础题)
    九度OJ 1283:第一个只出现一次的字符 (计数)
    九度OJ 1262:Sequence Construction puzzles(I)_构造全递增序列 (DP)
    九度OJ 1261:寻找峰值点 (基础题)
    SDWebImage
    MBProgressHUDDemo
  • 原文地址:https://www.cnblogs.com/zhou2011/p/2381282.html
Copyright © 2011-2022 走看看