狐狸梦见乌鸦

当坚持成为一种习惯,目标将不在遥远```
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

驱动之字符设备---ADC

Posted on 2012-03-06 08:52  灬啊U  阅读(479)  评论(0编辑  收藏  举报

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 }