mini2440驱动奇谭——ADC驱动与測试(动态挂载驱动)

简介:

博客: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的模拟信号


驱动部分

adc.c

/*************************************************************************
	> File Name: adc.c
	> Author: 梁惠涌
	> Mail: 
	> Created Time: 2014年10月08日 星期三 21时01分48秒
 ************************************************************************/
//在 linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach 文件夹下
#include<mach/regs-gpio.h>         // 和GPIO相关的宏定义 
#include<mach/hardware.h>          //S3C2410_gpio_cfgpin等gpio函数定义
//在 linux-2.6.32.2/include/linux 文件夹下
#include<linux/miscdevice.h>        //注冊 miscdevide 结构体成员变量
#include<linux/delay.h>             //这个应该是时延函数了
#include<linux/kernel.h>            //内核。不用说
#include<linux/module.h>            //驱动载入卸载函数定义
#include<linux/init.h>              //初始化头文件
#include<linux/mm.h>
#include<linux/fs.h>                //注冊file_operations结构体变量
#include<linux/types.h>
#include<linux/moduleparam.h>
#include<linux/slab.h>
#include<linux/errno.h>              
#include<linux/ioctl.h>
#include<linux/cdev.h>
#include<linux/string.h>
#include<linux/list.h>
#include<linux/pci.h>
#include<linux/gpio.h>              //gpio口定义文件(虚拟地址)
#include<linux/clk.h>               //系统时钟频率初始化文件
#include<linux/sched.h>
#include<linux/interrupt.h>         //宏定义 IRQ_HANDLED 等
//在 linux-2.6.32.2/arch/arm/include/asm 文件夹下
#include<asm/io.h>           
#include<asm/irq.h>
#include<asm/uaccess.h>
#include<asm/atomic.h>
#include<asm/unistd.h>
//在 linux-2.6.32.2/arch/arm/plat-s3c/include/plat 文件夹下
#include<plat/regs-adc.h>            //定义ADCCON ADCDAT0等寄存器

#define DEVICE_NAME "adc"

static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);
static void __iomem *adc_base;
static struct clk *adc_clk;
static int adc_data;                 
static volatile int ev_adc =0;  
/* (1) 定义等待变量adc_waitq
 * (2) 定义虚拟地址指针 adc_base, __iomem是2.6.9之后增加的特性,用来表示
 * 指向一个 I/O 的虚拟内存空间,void能够使编译器忽略对变量的检查。

* (3) 定义一个clk类型的指针变量adc_clk,用于存储头文件里adc时钟的设置。 * (4) clk结构体是定义在 s3c2410-clock.c文件里。 * (5) 中volatile确保读取ev_adc的值不会被编译器的优化忽略 * */ static ssize_t adc_read(struct file *fp, char *buf, size_t count, loff_t *ppos) { unsigned int tmp; tmp = (1<<14)|(255<<6)|(0<<3)|(1<<0); writel(tmp, adc_base + S3C2410_ADCCON); //AD转换转换開始 wait_event_interruptible(adc_waitq, ev_adc); ev_adc =0; copy_to_user(buf, (char *)&adc_data, sizeof(adc_data)); return sizeof(adc_data); } /* (1) adc_read函数中,ssize_t是表示读写数据块的大小,类型是无符号整形 * (2) (1<<14) 预分频使能,(255<<6)预分频值设为255,(0<<3)模拟输入通道AIN0 * (1<<0)使能AD转换,依据开发板启动信息(S3C244X:core 405 MHZ 等)能够知道开 * 发板 PCLK为50.625MHZ,A/D转换频率=50.625MHZ /(255+1)=0.197MHZ,A/D转换时间 * =1/(0.197/5)=26uS。

* (3) writel函数是将tmp的值写入adc虚拟控制寄存器 * (4) wait_event_interruptible使等待队列进入睡眠,应该是为了copy_to_user * 传递的不是一个正在改变的值的值,adc_data值读取在adc_irq函数实现 * (5) copy_to_user。将读取到的值发送到測试程序(用户层)返回adc_data的字节数 * */ static irqreturn_t adc_irq(int irq, void *dev_id){ if(!ev_adc){ adc_data=readl(adc_base + S3C2410_ADCDAT0) & 0x3ff; ev_adc= 1; wake_up_interruptible( &adc_waitq); } return IRQ_HANDLED; } /* (1)adc_irq在以下函数被引用的,irqreturn_t在irqreturn.h中定义枚举类型 * (2)adc_data存储ADCDAT0虚拟数据寄存器后十位值 * (3)wake_up_interruptible唤醒adc_waitq队列 * (4)IRQ_HANDLED宏定义为1,表示接收到了准确中断信号,并作了对应正确的处理 * */ static int adc_open(struct inode *inode, struct file *fp){ int ret; ret=request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, &adc_data); if(ret){ printk(KERN_ERR"IRQ %d error %d \n", IRQ_ADC , ret); } return 0; } /* (1) request_irq中断申请函数 * 參数1:中断源, 參数2:中断处理函数, * 參数3:中断方式。 參数4:设备名字, 參数5:设备ID * (2) 当中设备ID不能使用NULL,否则ret会为1值,报错,设备ID一般为驱动结构体 * 地址。但我没有使用结构体,直接使用&adc_data; * (3) 看过一些文章说写1等。可是会有warning。原因是设备ID參数是一个指针,需 * 要写一个地址才行。

* */ static struct file_operations dev_fops={ .owner = THIS_MODULE, .open = adc_open, .read = adc_read, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; static int __init adc_init(void){ int ret; adc_base = ioremap(S3C2410_PA_ADC, 4); adc_clk=clk_get(NULL, "adc"); if(!adc_clk){ printk(KERN_ERR "failed to find adc clock source! \n"); return -ENOENT; } printk(KERN_ALERT "Hello! mini2440 adc module is installed\n"); clk_enable(adc_clk); ret = misc_register(&misc); return ret; } /* (1) 函数__init是一个属性标志。当驱动模块被载入时,__init函数就被运行了 * (2) ioremap函数是将物理地址映射成虚拟地址并传递于adc_base * (3) clk_get是从头文件里获取adc时钟设置,定义在s3c2410-clock.c,因为内核 * 并没有adc驱动。所以adc的时钟并没初始化,clk_enable(adc_clk)初始化 adc * 时钟misc_register函数是注冊混杂设备驱动 * */ static void __exit adc_exit(void){ printk(KERN_ALERT "Good-bye ,mini2440 adc module is removed! \n"); misc_deregister(&misc); } /* (1) __exit和__init一样是module属性标志 * (2) misc_deregister函数是注销混杂设备驱动 * */ module_init(adc_init); module_exit(adc_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lianghuiyong Inc.");



Makefile

ifeq ($(KERNELRELEASE),)
	KDIR ?= /opt/FriendlyARM/mini2440/linux-2.6.32.2/
	PWD  := $(shell pwd)
modules:
	$(MAKE) -C $(KDIR) M=$(PWD) modules
modules_install:
	$(MAKE) -C $(KDIR) M=$(PWD) modules_install
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
else
	obj-m:= adc.o
endif

/* 第1行:推断 KERNELRELEASE 变量是否为空,仅仅有make命令该变量才不为空。

* 第2、3行:KDIR是内核路径,PWD是当前模块路径。

* 第4行:是makefile的功能选项。冒号结尾。 * 第5行:是运行模块的编译,语法是“Make -C 内核路径 M=模块路径 modules”。 * 第6、7行和第4、5行一样。 * 第8行:删除多余文件功能标识。 * 第9行:是删除编译过程的中间文件的命令。 * 第11行:假设make命令没有带功能标识(modules、modules_install、clean)使用, * 就将adc.o编译成adc.ko模块 * */



測试模块

adcceshi.c

/*************************************************************************
	> File Name: adcceshi.c
	> Author: 梁惠涌
	> Mail: 
	> Created Time: 2014年10月10日 星期五 23时41分26秒
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

void delay(int j){
    int i;
    for(;j>0;j--)
    for(i=10000;i>0;i--);
}

int main (int argc, char **argv){
    int fd;
    fd = open("/dev/adc",0);
    
    if( fd<0 ){
        printf("open adc device faild! \n");
        exit(1);
    }

    while(1){
        int ret;
        int data;

        ret =read(fd, &data, sizeof(data));
        if(ret !=sizeof(data)){
            if(errno!=EAGAIN){
                printf(" Read ADC Device Faild! \n");
            } 
            continue;
        }

        else {
            printf("    Read ADC : %d \n",data);
        }
        delay(5000);
    }
    close(fd);
    return 0;
}
/* (1) read(fd,&data,sizeof(data)) 函数将跳转到驱动file_openration
 * 中的.read =adc_read。进入adc_read函数,通过copy_to_user将驱动程
 * 序中adc_data 传递给用户层測试程序data。return返回data字节数
 * (2) 当中驱动中 adc_data的值是在驱动程序 adc_irq函数中通过readl从
 * ADCDAT0虚拟地址获取.
 * (3) delay函数通过多次空循环实现时间的延迟
 * */

測试程序使用命令: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、挂载驱动后执行測试程序出现:

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









本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5094561.html,如需转载请自行联系原作者

相关文章
|
22天前
|
机器学习/深度学习 数据采集 人工智能
探索AI驱动的自动化测试新纪元###
本文旨在探讨人工智能如何革新软件测试领域,通过AI技术提升测试效率、精准度和覆盖范围。在智能算法的支持下,自动化测试不再局限于简单的脚本回放,而是能够模拟复杂场景、预测潜在缺陷,并实现自我学习与优化。我们正步入一个测试更加主动、灵活且高效的新时代,本文将深入剖析这一变革的核心驱动力及其对未来软件开发的影响。 ###
|
1月前
|
存储 测试技术 数据库
数据驱动测试和关键词驱动测试的区别
数据驱动测试 数据驱动测试或 DDT 也被称为参数化测试。
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件测试的未来:AI 驱动的自动化测试方法
【5月更文挑战第29天】随着人工智能(AI)技术的不断发展和成熟,其在软件测试领域的应用也日益广泛。本文旨在探讨 AI 如何改变软件测试的面貌,特别是自动化测试方法。我们将分析当前自动化测试的挑战,并介绍 AI 如何提供解决方案,包括智能化测试用例生成、测试执行优化、以及结果分析等。通过实际案例研究,我们还将讨论 AI 在提高测试效率、减少错误和提升软件质量保障中的作用。最后,文章将预测 AI 在自动化测试领域的未来趋势,并提出对测试工程师的建议。
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
AI驱动的自动化测试:提升软件质量的未来之路
【9月更文挑战第3天】AI驱动的自动化测试是提升软件质量的未来之路。它借助AI技术的力量,实现了测试用例的智能生成、测试策略的优化、故障预测与定位等功能的自动化和智能化。随着技术的不断进步和应用场景的不断拓展,AI驱动的自动化测试将在未来发挥更加重要的作用,为软件开发和运维提供更加高效、准确和可靠的解决方案。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件自动化测试的未来:AI驱动的测试策略
【7月更文挑战第47天】 随着人工智能(AI)技术不断进步,其在软件测试领域的应用也日益广泛。本文将探讨如何整合AI技术与现有的自动化测试流程,提出一个面向未来的测试策略。文章重点分析了AI在测试用例生成、执行、结果分析和持续集成中的作用,同时预测了这种技术融合对测试工程师角色的影响,以及它如何提高软件测试的效率和准确性。
|
3月前
|
Java 测试技术
单元测试问题之想通过单元测试来驱动代码的设计与重构,如何实现
单元测试问题之想通过单元测试来驱动代码的设计与重构,如何实现
|
5月前
|
Web App开发 IDE Java
自动化测试谷歌浏览器和其驱动版本差不多却还是报错The chromedriver version (121.0.6167.184) detected in PATH at DPythonchromed
自动化测试谷歌浏览器和其驱动版本差不多却还是报错The chromedriver version (121.0.6167.184) detected in PATH at DPythonchromed
134 2
|
4月前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件测试的新篇章:AI驱动的测试自动化
【7月更文挑战第10天】随着人工智能技术的蓬勃发展,软件测试领域迎来了革命性的变革。本文将深入探讨AI技术如何重塑软件测试流程,提升测试效率和准确性。从AI在测试用例生成、缺陷预测、测试执行以及结果分析的应用出发,我们将揭示AI技术如何使测试更加智能化、精准化,并展望AI在软件测试领域的未来发展趋势。
50 0
|
5月前
|
机器学习/深度学习 人工智能 算法
探索软件测试的新时代:AI驱动的自动化
【6月更文挑战第4天】随着人工智能技术的不断进步,软件测试领域正经历着一场革命。本文将探讨AI如何改变传统的软件测试方法,提高测试效率和准确性,以及这一趋势对测试工程师未来技能要求的影响。
53 6
|
5月前
|
机器学习/深度学习 人工智能 安全
探索软件测试的新时代:AI驱动的测试自动化
本文深入探讨了人工智能(AI)如何革新软件测试领域,特别是测试自动化。随着AI技术的不断进步,它为测试自动化带来了前所未有的效率和准确性,从而极大地提高了软件开发的速度和质量。本文将详细介绍AI在软件测试中的应用,以及它如何帮助测试人员克服传统测试方法的局限性。