• 驱动之字符设备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 }







  • 相关阅读:
    Ubuntu配置sublime text 3的c编译环境
    ORA-01078错误举例:SID的大写和小写错误
    linux下多进程的文件拷贝与进程相关的一些基础知识
    ASM(四) 利用Method 组件动态注入方法逻辑
    基于Redis的三种分布式爬虫策略
    Go语言并发编程总结
    POJ2406 Power Strings 【KMP】
    nyoj 会场安排问题
    Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    Java的String、StringBuffer和StringBuilder的区别
  • 原文地址:https://www.cnblogs.com/zhou2011/p/2381282.html
Copyright © 2020-2023  润新知