博客:http://blog.csdn.net/muyang_ren

实现功能:开发板动态载入adc驱动模块并能通过測试程序

系统:Ubuntu 14.04    
驱动交叉编译内核:linux-2.6.32.2     开发板:mini2440

建立交叉编译请点击。烧写linux到开发板请点击。Linux
RootFs 选择rootfs_rtm_2440.img  (光盘文件夹:image/linux/rtm )

开发所需工具:NFS网络文件  minicom 
vim

linux文件文件夹:/opt/FriendlyARM/mini2440/linux-2.6.32.2

自己驱动文件夹 : /home/lianghuiyong/my2440drivers

注意:我的开发板内核被我裁剪了adc驱动部分,包含之后要写的一些驱动,裁剪内核驱动非常easy,make menuconfig菜单后相应的驱动空格为空即可了

ADC原理

ADC模块共同拥有8个模拟信号通道(XP、XM、YP、YM。A[ 3 : 0 ]),我们使用最多的是触摸屏,这时设置XP、XM、YP、YM选择为触摸屏的引脚,XM、YM则分别为X、Y方向接地线,为低电平。XP、YP使能后,笔尖等让触摸屏接触点受压产生电压变化来计算产生的X 、Y轴坐标。并将坐标值分别存储于ADCDAT0。ADCDAT1中。触摸屏就先这样简介下,当触摸屏引脚为禁止时,这四个port(XP、XM、YP、YM)可被用于ADC的模拟输入port(AIN4、AIN5、AIN6和AIN7);



原理:这里模拟信号源选择开发板上的可调电位器。从电位器电路图中可知模拟信号输出端为AIN0。可调电位器阻值的改变产生电压的变化,设置MUX多路模拟信号选择器为AIN0,进行模数转换,并将产生的数据存储于ADCDAT0中。顺便说一下。MUX(8选1)可选择XP、XM、YP、YM作为模拟信号源,但此时不是触摸屏信号。而是AIN4、AIN5、AIN6或者AIN7的模拟信号

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbXV5YW5nX3Jlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

驱动部分

adc.c

  1. /*************************************************************************
  2. > File Name: adc.c
  3. > Author: 梁惠涌
  4. > Mail:
  5. > Created Time: 2014年10月08日 星期三 21时01分48秒
  6. ************************************************************************/
  7. //在 linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach 文件夹下
  8. #include<mach/regs-gpio.h> // 和GPIO相关的宏定义
  9. #include<mach/hardware.h> //S3C2410_gpio_cfgpin等gpio函数定义
  10. //在 linux-2.6.32.2/include/linux 文件夹下
  11. #include<linux/miscdevice.h> //注冊 miscdevide 结构体成员变量
  12. #include<linux/delay.h> //这个应该是时延函数了
  13. #include<linux/kernel.h> //内核。不用说
  14. #include<linux/module.h> //驱动载入卸载函数定义
  15. #include<linux/init.h> //初始化头文件
  16. #include<linux/mm.h>
  17. #include<linux/fs.h> //注冊file_operations结构体变量
  18. #include<linux/types.h>
  19. #include<linux/moduleparam.h>
  20. #include<linux/slab.h>
  21. #include<linux/errno.h>
  22. #include<linux/ioctl.h>
  23. #include<linux/cdev.h>
  24. #include<linux/string.h>
  25. #include<linux/list.h>
  26. #include<linux/pci.h>
  27. #include<linux/gpio.h> //gpio口定义文件(虚拟地址)
  28. #include<linux/clk.h> //系统时钟频率初始化文件
  29. #include<linux/sched.h>
  30. #include<linux/interrupt.h> //宏定义 IRQ_HANDLED 等
  31. //在 linux-2.6.32.2/arch/arm/include/asm 文件夹下
  32. #include<asm/io.h>
  33. #include<asm/irq.h>
  34. #include<asm/uaccess.h>
  35. #include<asm/atomic.h>
  36. #include<asm/unistd.h>
  37. //在 linux-2.6.32.2/arch/arm/plat-s3c/include/plat 文件夹下
  38. #include<plat/regs-adc.h> //定义ADCCON ADCDAT0等寄存器
  39.  
  40. #define DEVICE_NAME "adc"
  41.  
  42. static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);
  43. static void __iomem *adc_base;
  44. static struct clk *adc_clk;
  45. static int adc_data;
  46. static volatile int ev_adc =0;
  47. /* (1) 定义等待变量adc_waitq
  48. * (2) 定义虚拟地址指针 adc_base, __iomem是2.6.9之后增加的特性,用来表示
  49. * 指向一个 I/O 的虚拟内存空间,void能够使编译器忽略对变量的检查。
  50.  
  51. * (3) 定义一个clk类型的指针变量adc_clk,用于存储头文件里adc时钟的设置。
  52. * (4) clk结构体是定义在 s3c2410-clock.c文件里。
  53. * (5) 中volatile确保读取ev_adc的值不会被编译器的优化忽略
  54. * */
  55.  
  56. static ssize_t adc_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
  57. {
  58. unsigned int tmp;
  59. tmp = (1<<14)|(255<<6)|(0<<3)|(1<<0);
  60. writel(tmp, adc_base + S3C2410_ADCCON); //AD转换转换開始
  61. wait_event_interruptible(adc_waitq, ev_adc);
  62. ev_adc =0;
  63.  
  64. copy_to_user(buf, (char *)&adc_data, sizeof(adc_data));
  65.  
  66. return sizeof(adc_data);
  67. }
  68. /* (1) adc_read函数中,ssize_t是表示读写数据块的大小,类型是无符号整形
  69. * (2) (1<<14) 预分频使能,(255<<6)预分频值设为255,(0<<3)模拟输入通道AIN0
  70. * (1<<0)使能AD转换,依据开发板启动信息(S3C244X:core 405 MHZ 等)能够知道开
  71. * 发板 PCLK为50.625MHZ,A/D转换频率=50.625MHZ /(255+1)=0.197MHZ,A/D转换时间
  72. * =1/(0.197/5)=26uS。
  73.  
  74. * (3) writel函数是将tmp的值写入adc虚拟控制寄存器
  75. * (4) wait_event_interruptible使等待队列进入睡眠,应该是为了copy_to_user
  76. * 传递的不是一个正在改变的值的值,adc_data值读取在adc_irq函数实现
  77. * (5) copy_to_user。将读取到的值发送到測试程序(用户层)返回adc_data的字节数
  78. * */
  79.  
  80. static irqreturn_t adc_irq(int irq, void *dev_id){
  81. if(!ev_adc){
  82. adc_data=readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;
  83. ev_adc= 1;
  84. wake_up_interruptible( &adc_waitq);
  85. }
  86. return IRQ_HANDLED;
  87. }
  88. /* (1)adc_irq在以下函数被引用的,irqreturn_t在irqreturn.h中定义枚举类型
  89. * (2)adc_data存储ADCDAT0虚拟数据寄存器后十位值
  90. * (3)wake_up_interruptible唤醒adc_waitq队列
  91. * (4)IRQ_HANDLED宏定义为1,表示接收到了准确中断信号,并作了对应正确的处理
  92. * */
  93.  
  94. static int adc_open(struct inode *inode, struct file *fp){
  95. int ret;
  96.  
  97. ret=request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, &adc_data);
  98. if(ret){
  99. printk(KERN_ERR"IRQ %d error %d \n", IRQ_ADC , ret);
  100. }
  101. return 0;
  102. }
  103. /* (1) request_irq中断申请函数
  104. * 參数1:中断源, 參数2:中断处理函数,
  105. * 參数3:中断方式。 參数4:设备名字, 參数5:设备ID
  106. * (2) 当中设备ID不能使用NULL,否则ret会为1值,报错,设备ID一般为驱动结构体
  107. * 地址。但我没有使用结构体,直接使用&adc_data;
  108. * (3) 看过一些文章说写1等。可是会有warning。原因是设备ID參数是一个指针,需
  109. * 要写一个地址才行。
  110.  
  111. * */
  112.  
  113. static struct file_operations dev_fops={
  114. .owner = THIS_MODULE,
  115. .open = adc_open,
  116. .read = adc_read,
  117. };
  118.  
  119. static struct miscdevice misc = {
  120. .minor = MISC_DYNAMIC_MINOR,
  121. .name = DEVICE_NAME,
  122. .fops = &dev_fops,
  123. };
  124.  
  125. static int __init adc_init(void){
  126. int ret;
  127. adc_base = ioremap(S3C2410_PA_ADC, 4);
  128.  
  129. adc_clk=clk_get(NULL, "adc");
  130. if(!adc_clk){
  131. printk(KERN_ERR "failed to find adc clock source! \n");
  132. return -ENOENT;
  133. }
  134.  
  135. printk(KERN_ALERT "Hello! mini2440 adc module is installed\n");
  136. clk_enable(adc_clk);
  137.  
  138. ret = misc_register(&misc);
  139. return ret;
  140. }
  141. /* (1) 函数__init是一个属性标志。当驱动模块被载入时,__init函数就被运行了
  142. * (2) ioremap函数是将物理地址映射成虚拟地址并传递于adc_base
  143. * (3) clk_get是从头文件里获取adc时钟设置,定义在s3c2410-clock.c,因为内核
  144. * 并没有adc驱动。所以adc的时钟并没初始化,clk_enable(adc_clk)初始化 adc
  145. * 时钟misc_register函数是注冊混杂设备驱动
  146. * */
  147.  
  148. static void __exit adc_exit(void){
  149. printk(KERN_ALERT "Good-bye ,mini2440 adc module is removed! \n");
  150. misc_deregister(&misc);
  151. }
  152. /* (1) __exit和__init一样是module属性标志
  153. * (2) misc_deregister函数是注销混杂设备驱动
  154. * */
  155.  
  156. module_init(adc_init);
  157. module_exit(adc_exit);
  158.  
  159. MODULE_LICENSE("GPL");
  160. MODULE_AUTHOR("Lianghuiyong Inc.");

Makefile

  1. ifeq ($(KERNELRELEASE),)
  2. KDIR ?= /opt/FriendlyARM/mini2440/linux-2.6.32.2/
  3. PWD := $(shell pwd)
  4. modules:
  5. $(MAKE) -C $(KDIR) M=$(PWD) modules
  6. modules_install:
  7. $(MAKE) -C $(KDIR) M=$(PWD) modules_install
  8. clean:
  9. rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
  10. else
  11. obj-m:= adc.o
  12. endif
  13.  
  14. /* 第1行:推断 KERNELRELEASE 变量是否为空,仅仅有make命令该变量才不为空。
  15.  
  16. * 第2、3行:KDIR是内核路径,PWD是当前模块路径。
  17.  
  18. * 第4行:是makefile的功能选项。冒号结尾。
  19. * 第5行:是运行模块的编译,语法是“Make -C 内核路径 M=模块路径 modules”。
  20. * 第6、7行和第4、5行一样。
  21. * 第8行:删除多余文件功能标识。
  22. * 第9行:是删除编译过程的中间文件的命令。
  23. * 第11行:假设make命令没有带功能标识(modules、modules_install、clean)使用,
  24. * 就将adc.o编译成adc.ko模块
  25. * */

測试模块

adcceshi.c

  1. /*************************************************************************
  2. > File Name: adcceshi.c
  3. > Author: 梁惠涌
  4. > Mail:
  5. > Created Time: 2014年10月10日 星期五 23时41分26秒
  6. ************************************************************************/
  7.  
  8. #include<stdio.h>
  9. #include<stdlib.h>
  10. #include<errno.h>
  11.  
  12. void delay(int j){
  13. int i;
  14. for(;j>0;j--)
  15. for(i=10000;i>0;i--);
  16. }
  17.  
  18. int main (int argc, char **argv){
  19. int fd;
  20. fd = open("/dev/adc",0);
  21.  
  22. if( fd<0 ){
  23. printf("open adc device faild! \n");
  24. exit(1);
  25. }
  26.  
  27. while(1){
  28. int ret;
  29. int data;
  30.  
  31. ret =read(fd, &data, sizeof(data));
  32. if(ret !=sizeof(data)){
  33. if(errno!=EAGAIN){
  34. printf(" Read ADC Device Faild! \n");
  35. }
  36. continue;
  37. }
  38.  
  39. else {
  40. printf(" Read ADC : %d \n",data);
  41. }
  42. delay(5000);
  43. }
  44. close(fd);
  45. return 0;
  46. }
  47. /* (1) read(fd,&data,sizeof(data)) 函数将跳转到驱动file_openration
  48. * 中的.read =adc_read。进入adc_read函数,通过copy_to_user将驱动程
  49. * 序中adc_data 传递给用户层測试程序data。return返回data字节数
  50. * (2) 当中驱动中 adc_data的值是在驱动程序 adc_irq函数中通过readl从
  51. * ADCDAT0虚拟地址获取.
  52. * (3) delay函数通过多次空循环实现时间的延迟
  53. * */

測试程序使用命令:arm-linux-gcc -g adcceshi.c -o adcceshi  来生成測试程序adcceshi

开发板执行截图

当中rmmod: module 'adc' not found提示是由于是没把驱动程序放在/lib/modules/2.6.32.2-FriendlyARM下,我直接在挂载的目录下使用,

这个提示能够忽略

错误笔记

1 、error :‘TASK_INTERRUPTIBLE' undeclared (first use in this function)

包括头文件: #include<linux/sched.h>

2、 error: 'IRQF_SHARED' undeclared

包括头文件:#include<linux/interrupt.h>

3、挂载驱动后执行測试程序出现:

  1. Unable to handle kernel NULL pointer dereference at virtual address 00000000
  2. pgd = c3934000
  3. [00000000] *pgd=33969031, *pte=00000000, *ppte=00000000
  4. Internal error: Oops: 817 [#2]
  5. last sysfs file: /sys/devices/virtual/misc/adc/dev
  6. Modules linked in: adc [last unloaded: adc]
  7. CPU: 0 Tainted: G D (2.6.32.2-FriendlyARM #2)
  8. PC is at adc_read+0x20/0x10c [adc]
  9. LR is at vfs_read+0xac/0xe0
  10. pc : [<bf00c0e0>] lr : [<c008ec10>] psr: a0000013
  11. sp : c393bf20 ip : c393bf58 fp : c393bf54
  12. r10: 00000000 r9 : c393a000 r8 : c0023088
  13. r7 : bec9fd34 r6 : c393bf78 r5 : bec9fd34 r4 : bf00c470
  14. r3 : 00000000 r2 : 00007fc1 r1 : bec9fd34 r0 : c390e380
  15. Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
  16. Control: c000717f Table: 33934000 DAC: 00000015
  17. Process adcceshi (pid: 664, stack limit = 0xc393a270)
  18. Stack: (0xc393bf20 to 0xc393c000)
  19. bf20: 00000000 c390e380 c399b904 00000000 c393bf64 c393bf40 c390e380 bec9fd34
  20. bf40: c393bf78 00000003 c393bf74 c393bf58 c008ec10 bf00c0d0 c390e380 00000000
  21. bf60: 00000000 00000003 c393bfa4 c393bf78 c008ed1c c008eb74 00000000 00000000
  22. bf80: 00000005 00000000 c393bfa4 00000000 00000000 00000000 00000000 c393bfa8
  23. bfa0: c0022ee0 c008ece0 00000000 00000000 00000003 bec9fd34 00000004 bec9fd34
  24. bfc0: 00000000 00000000 00000000 00000003 00000000 00000000 40024000 bec9fd44
  25. bfe0: 00000000 bec9fd28 00008550 400daebc 60000010 00000003 00000000 00000000
  26. Backtrace:
  27. [<bf00c0c0>] (adc_read+0x0/0x10c [adc]) from [<c008ec10>] (vfs_read+0xac/0xe0)
  28. r7:00000003 r6:c393bf78 r5:bec9fd34 r4:c390e380
  29. [<c008eb64>] (vfs_read+0x0/0xe0) from [<c008ed1c>] (sys_read+0x4c/0x84)
  30. r7:00000003 r6:00000000 r5:00000000 r4:c390e380
  31. [<c008ecd0>] (sys_read+0x0/0x84) from [<c0022ee0>] (ret_fast_syscall+0x0/0x28)
  32. r6:00000000 r5:00000000 r4:00000000
  33. Code: e59f40e0 e59f20e0 e5943004 e1a07001 (e5832000)
  34. ---[ end trace 929b7bc9d9ae4dda ]---
  35. Segmentation fault

仅仅需注意上面中:PC is at adc_read+0x20/0x10c [adc]   出错的是 adc_read函数。从 Unable to handle kernel NULL pointer dereference at virtual address 00000000

可看出是指针的问题。看整个驱动发现是由于adc_base没有赋予虚拟地址值,在驱动载入函数中添加:adc_base= ioremap(S3C2410_PA_ADC, 0x20);adc物理地址转换成虚拟地址并存储于adc_base

4、IRQ 80 error -22  错误

当中irq中断函数 ret
= request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME,NULL);
最后一个參数不能为NULL,我改动后为 &adc_data

mini2440驱动奇谭——ADC驱动与測试(动态挂载驱动)的更多相关文章

  1. Selenium2 Python 自己主动化測试实战学习笔记(五)

    7.1 自己主动化測试用例 无论是功能測试.性能測试和自己主动化測试时都须要编写測试用例,測试用例的好坏能准确的体现了測试人员的经验.能力以及对项目的深度理解. 7.1.1 手工測试用例与自己主动化測 ...

  2. Robot Framework自己主动化測试框架之我见

    一些自己主动化測试现状: 盲目的去做自己主动化,终于以失败告终. 觉得是能提高效率的事情.却推广不下去: 事实上上述问题产生的原因是: 自己主动化測试案例稳定性不高,可维护性比較差: 自己主动化測试工 ...

  3. DM8168 PWM驱动与測试程序

    昨天把DM8168的Timer设置给摸了一遍,为写PWM的底层驱动做好了准备,如今就要进入主题了. dm8168_pwm.c: #include <linux/module.h> #inc ...

  4. S3C6410+FPGA+2*RTL8211 驱动 iperf測试

    驱动也写的差点儿相同了,所以有必要測试下性能怎样?本次採用了iperf进行測试.而且对照了下s3c6410+ks8851的測试结果 1.iperf怎样交叉编译? https://iperf.fr/ 官 ...

  5. Linux声卡驱动移植和測试

    一.分析驱动程序,依据开发板改动代码 代码太长,就不贴了,几个注意点: 1. 查看开发板原理图和S3C2410的datasheet,UDA1341的L3MODE.L3DATA.L3CLOCK分别与S3 ...

  6. Android JNI用于驱动測试

    硬件平台:S3C6410 操作系统:Ubuntu.windows 板子系统:Android 开发工具:jdk.ndk,eclipse 本次測试从linux内核模块编译開始.以S3C6410的pwm驱动 ...

  7. DM8168 GPIO驱动与測试程序

    本次測试针对GPIO1进行,挑选了GP1[31],引脚的复用默认的就是GPIO 还是老规矩,贴上driver.c,Makefile,test.c: dm8168_gpio.c: #include &l ...

  8. Testng 的数据源 驱动測试 代码与配置

    JUnit中有讲述使用注解的方式进行数据源读取进行自己主动循环測试的方法,在TestNG中也提供了对应的方法 public class TestngDataProvider { /** * 数组内的每 ...

  9. linux驱动开发重点关注内容--摘自《嵌入式Linux驱动模板精讲与项目实践》

    本文摘自本人拙著 <嵌入式Linux驱动模板精讲与项目实践> 初步看起来Linux设备驱动开发涉及内容非常多,而须要实现驱动的设备千差万别.事实上做一段时间驱动之后回首看来主要就是下面几点 ...

随机推荐

  1. Spring Tool Suite(简称STS)针对SimpleDateFormat.pase函数的实参值不做检验,异常直接默认值之

    Spring Tool Suite(简称STS)是 Spring 团队开发的一款基于Eclipse的IDE,旨在简化开发Spring MVC 应用的流程.可以自动生成spring相关的配置文件.比如a ...

  2. oracle 分区表exchange原理

    oracle分区的exchange操作非常快,那原理是什么呢?下面我们来做个实验: SQL> create table test (id number(3)); 表已创建. SQL> in ...

  3. 设计模式模式适配器(Adapter)摘录

    23种子GOF设计模式一般分为三类:创建模式.结构模型.行为模式. 创建模式抽象的实例,他们帮助建立一个系统,是独立于如何.这是一个这些对象和陈述的组合.创建使用继承一个类架构更改实例,一个对象类型模 ...

  4. 硬盘被误格式化或Ghost还原后的数据恢复

    硬盘格式化(Ghost还原)后的数据恢复 ---diskgenius使用之数据恢复 问题引出:计算机中病毒后用Ghost版本的winxp安装,由于安装途中选择了把映像安装到硬盘而不是分区,安装好后只剩 ...

  5. perl uri_escape(urlencode ) 转换

    [root@wx03 lib]# cat a1.pl #引入模块 use URI::Escape; #urlencode $encoded = uri_escape("[中均]") ...

  6. 基于TCP/IP协议的C++网络编程(API函数版)

    源代码:http://download.csdn.net/detail/nuptboyzhb/4169959 基于TCP/IP协议的网络编程 定义变量——获得WINSOCK版本——加载WINSOCK库 ...

  7. hdu 2871 Memory Control(伸展树splay tree)

    hdu 2871 Memory Control 题意:就是对一个区间的四种操作,NEW x,占据最左边的连续的x个单元,Free x 把x单元所占的连续区间清空 , Get x 把第x次占据的区间输出 ...

  8. Android手机怎样录制屏幕及转GIF

    有时候我们须要录制Android 手机的屏幕,比方写了一个Demo应用,须要公布到博客和微博上. 例如以下是我录制转GIF的效果图 对于Android4.4的上的手机,系统自带了一个命令screenr ...

  9. eclipse之The currrently displayed page contains invalid values错误

    现象: eclipse的preferences里面须要保存密码,保存报错Could Not Accept ChangesThe currrently displayed page contains i ...

  10. MD5加密以及验证加密-加盐

    加密与解密算法: /// <summary> /// 签名字符串 32位 /// </summary> /// <param name="input" ...