本文主要是介绍s3c2410多通道adc驱动及测试程序(使用write控制多通道),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
免责声明:
本文所涉及的驱动是在SeonKon Choi于2003年编写的适合2.4版本内核的ADC驱动基础上结合网络能找得到的代码片段经过自己的修改并实践通过的。测试过程中的ADC并不涉及其它驱动(比如与ADC有很密切关系的触摸屏驱动)。本文作者本着GNU共享精神发表此文并附录源代码——其实公布源代码是必须的,因为代码使用GPL(而如何行文则是作者的自由)。至于如何使用文中知识点及代码,无论正确与否,概与文章作者无关,风险自担。不过,趁文章作者还有激情研究驱动之时,欢迎一起讨论,大家共同学习,天天向上。
杂项:
本文上接《s3c2410多通道adc驱动及测试程序》,里面承诺了用write来传递数据到内核中。那篇文章使用ioctl来控制通道,而本文使用write控制通道。而两者的驱动,在本质上是一致的——除了针对内核版本不同而有所修改外。
收获:
在修改人家驱动代码过程中,对于一些不确定的函数(比如位于哪个头文件,每个函数参数的含义),逐一google之,并查看内核源代码。个人认为,学习代码、驱动这种事情,最好在实践中学习,看书与实践结合起来。话虽如此,真正做到的人却不多。如果不去动手写,看再多的LDD3、ELDD、ULK、APUE、CSAPP也收获不丰,印象不深。说来惭愧,正是这个观点,让我一直不去看书,至今连LDD3也没看完,APUE也刚开了个头。
未尽及将来可能会做的事:
1、与TS共享中断,使用信号量进行互斥访问。
2、其它的。
正文:
如想直接抄代码,可忽略行文,直接到文章后面下载附录的工程压缩包或源代码。
1、对于头文件,不必说太多,2.4版本的内核有些头文件已经删除或修改或合并等等,比如像<linux/config.h>已经不在了。
2、使用ioremap,这使得代码整洁、易看。举一例:
网上使用如下代码片段(示意性代码)设置通道,读取ADC数据:
writel((1<<14) | PRSCVL(adcdev.prescale) | ADC_INPUT(adcdev.channel) | 0x01 | 0x01, S3C2410_ADCCON);
ret = readl(S3C2410_ADCDAT0);
使用宏后:
START_ADC_AIN(adcdev.channel, adcdev.prescale);
ret = ADCDAT0;
其中START_ADC_AIN将设置通道、启动ADC等合在一起,ADCDAT0是经过ioremap后加上一偏移得到的“寄存器”。
如此一来,是不是觉得后面的代码很直观很容易看?不过这也有一点不好,封闭得太多了,对于想知道其中原理的人来说可不是件好事。比如声明一个信号量,使用DECLARE_MUTEX(ADC_LOCK)或DEFINE_SEMAPHORE(ADC_LOCK),这很好懂,但是这个宏到底做了些什么呢?这就要深入到这个宏定义所在文件中研究了(在<linux/semaphore.h>文件中)。
对于这两宏,想说几句,因为最近一次移植新版本的内核(2.6.37.3),就是因为DECLARE_MUTEX不再使用而导致我浪费了几天的时间。据本人考查,最早说要将DECLARE_MUTEX改为DEFINE_SEMAPHORE是在08年10月底,参见地址http://lwn.net/Articles/304725/。而在Linux Cross Reference(网站如http://lxr.free-electrons.com/等等)中发现,直到2.6.36才出现DEFINE_SEMAPHORE,而且这个版本中两个宏同时存在。但是到了2.6.37,DECLARE_MUTEX这个宏却不存在了。
不由得想到某个论坛的签名,原文是英文的,原文及出处一时不知道去哪找了。大意是说UNIX最好的地方是什么?答曰:有社区,再问UNIX不好的地方是什么?答曰:有太多的社区。像Linux内核这东西,版本更新太快了,各种结构体名称变化、成员变化及新增函数、删除函数,让人目不暇接。虽说一味追求新版本不好,但是心理作用,还是选择新的版本来移植。新的东西当然有它的好,像买台android的手机,就得不停的刷机,不断折腾,这跟搞Linux在某种程度上是一样的。
3、硬件寄存器
ADC驱动涉及的寄存器不多,都可以在datasheet中找到说明的。如:
#define PRSCVL(x) ((x) << 6)
#define ADC_INPUT(x) ((x) << 3)
#define ADC_START (1 << 0)
第一个是设置AD转换器预定频率(注:该术语可能不标准),左移6位,是因为ADCCON寄存器的6~13位就是这个值。而ADC_INPUT是选择通道的,位于ADCCON寄存器的3~5位。最后一行是使能ADC(启动ADC),位于ADCCON第0位,1表示开始转换(注:根据datasheet英文理解)。
其实这些不难理解的,看多代码了,自然也知道了。自从当年学AVR后,我对这种方式的移位特别有好感,甚至一度上瘾,常常跟别人说,不过没多少人理解,后来就算了,自己独自享受这种便捷了。
4、与原来代码的改动:
1)、去掉MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT,这个据说在2.6中不再使用了。
2)、对于request_irq函数,将IRQ_ADC_DONE改为IRQ_ADC,最后一个参数改为&adcdev。
3)、将adcdone_int_handler函数返回类型由原来的void改为irqreturn_t。
4)、另外添加cdev类型的变量,因为注册字符设备需要使用到。
5)、判断copy_from_user、copy_to_user两个函数的返回值,正常返回0,出错则返回没有复制成功的字节数。出错时返回-EFAULT。如果不判断,会出现一个警告,比如:
warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
内核中很多地方都使用这两个函数,都作了判断。
其它的修改不一一道来了。
测试过程:
测试手段很多,由于本人的网络驱动一直不成功,不能使用方便快捷的NFS方式来测试。将编译好的驱动模块和交叉编译后的测试程序拷贝到U盘,再用U盘复制到开发板上,在这个过程中,文件丢失了可执行性,又得在开发板上用chmod恢复可执行性。很是麻烦。
原想将未修改的代码与修改后的代码作一个patch的,不过有10KB那么大,就不帖出来了(附件里有)。下面附上工程目录及源代码。如果网页显示与工程文件不一致,以工程目录中的文件为准。实际代码中没有中文注释,由此带来的各种质问及不解,本人不作解释。
整个工程下载(压缩文件名称为s3c2410-adc-latelee.org.tar.bz2,md5sum为0aa975c62eaefb8d05430bca8d7df088):
---
应用层的测试代码:
---
本人得到的原始的源代码如下:
---
经过修改后的源代码:
---
原文出处:www.latelee.org/embedded-linux/mutil-channel-adc-of-s3c2410-write.html
这篇关于s3c2410多通道adc驱动及测试程序(使用write控制多通道)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!