快速发布采购 管理采购信息

嵌入式数据采集系统中的ADS8364驱动程序设计

时间:2012-3-24, 来源:互联网, 文章类别:嵌入式系统

数据采集系统的工作原理就是将被测对象(外部世界或现场)的各种参量(可以是物理量,也可以是化学量,生物量)通过各种传感元件做适当转换后,再经信号调理、采样、量化、编码、传输等步骤,最后送到服务器进行数据处理和存储记录的过程。用于数据采集的成套设备称为数据采集系统(Data Acquisition System,DAS)。数据采集系统一般包括传感器、放大器、滤波器、A/D采样转换电路、微处理器等元件。数据采集系统基本组成示意图如图1所示。A/D转换电路在数据采集系统中起着至关重要的作用,它的转换精度和工作可靠度直接关系着整个采集系统的工作性能。由于篇幅有限,本文仅就该数据采集系统中的A/D转换芯片ADS8364的驱动开发过程加以概述。

2 ADS8364的功能简介 ADS8364是美国TI公司生产的一种高速、低能耗、6通道同步采样转换、单+5V供电、16位高速并行接口的高性能模数转换芯片。并带有6个80dB共模抑制的全差分输入通道以及6个4μs连续近似的模数转换器、6个差分采样/保持放大器[1]。 ADS8364的6个模拟输入分为3组(A,B和C),每个输入端都有一个ADCs保持信号以用来保证几个输入通道能同时进行采样和转换,因此不同模拟输入通道信号之间的相位差异得以保存。当3个保持信号同时被选通时,6个ADCs同步转换,其转换结果将保存在6个寄存器中。采样频率最高为 250 KHz,抗噪性能好。3 设备驱动程序开发的基本原理 设备驱动程序是操作系统内核和机器硬件之间的接口,它为应用程序屏蔽了硬件的细节,将应用程序对设备的操作转化为对相应的设备文件的操作,使应用程序可以象操作普通文件一样,用处理普通文件的标准系统调用来打开、关闭和读写设备。设备驱动程序作为内核的一部分,主要完成如下功能:对设备进行初始化和释放;完成硬件与内核的数据交互;完成内核与应用层的数据交互;对可能出现的错误进行检测和处理。 在Linux系统中,打开的设备在内核内部由设备文件结构标识,内核使用file_operations(文件操作)结构访问驱动程序的函数。每个文件都与自己的函数集相关联(通过包含在设备中指向file_operations结构的指针实现),这些操作主要负责系统调用的实现。用户进程利用系统调用对设备文件进行操作时,系统调用通过设备的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这就是 Linux的设备驱动程序工作的基本原理。由此可见,编写设备驱动程序的主要工作就是编写file_operations中的子函数,完成对设备的操作。 为了实现设备的独立性,Linux把设备分成三种类型:字符设备(character device),块设备(block device)和网络设备(network device)[2]。每类设备都有独特的管理控制方式和不同的驱动程序,这样就可以把控制不同设备的驱动程序与操作系统的其它部分分离开来,不但便于对不同设备的管理,而且为系统中增加和撤销某种设备提供了很多方便。在此,我们是把ADS8364看作字符设备来驱动的,其驱动程序就是字符设备驱动程序。 4 ADS8364的驱动程序设计概述4.1 驱动程序的LKM实现机制 Linux内核提供了两种机制来开发设备驱动程序,一种是直接把驱动程序编译进内核,成为Linux内核的一部分。另一种是通过LKM(Loadable Kernel Module,即可加载模块化机制)来开发可动态加载和卸载的驱动模块[3]。驱动程序如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以本系统把ADS8364的驱动程序编写成了 LKM型驱动程序(就是把ADS8364的驱动程序作为一个独立的单元模块,在使用时可以使用insmod命令加载到核心中,用完后使用rmmod命令卸载的那种)。4.2 驱动程序的注册与注销 向系统增加一个驱动程序则意味着要赋予它一个主设备号,这一赋值过程是在驱动程序模块的初始化中完成的。ADS8364的初始化入口函数定义如下:int _init ads8364_init_module(void)全功能版J-LINK ARM仿真器V6.0 { …..ret = register_chrdev(11, ADS8364, &ad_fops); ……} 在用insmod命令将编译好的模块调入内存时,ads8364_init_module( )函数被调用。在这里,ads8364_init_module( )只做了一件事,它调用函数register_chrdev( )向内核注册该字符设备。函数register_chrdev()定义在 中。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将自动选择一个没有被占用的设备号作为该字符设备的主设备号并返回。参数二是设备文件名,这个名字必须插入到/dev目录中,并与驱动程序的主设备号和次设备号相连。参数三用来登记驱动程序实际执行操作的函数的指针。如果登记成功,返回设备的主设备号,不成功,返回一个负值。在此,我们选用当前不用的设备号11作为ADS8364的主设备号,设备名为 ADS8364。在关闭字符设备或块设备时,还需要通过unregister_chrdev( )从内核中注销设备,并释放主设备号。在用rmmod卸载该驱动模块时,cleanup_module函数被调用,它释放字符设备ADS8364在系统字符设备表中占有的表项。void ads8364_cleanup_module(void){ unregister_chrdev(11, ADS8364); info("ADS8364 cleanup module ok!"); }4.3 file_operations结构体的设计在Linux中,字符设备向内核提供的接口函数集就是文件操作集file_operations结构体。在Linux系统中,打开的设备在内核内部由设备文件file结构标识,内核使用file_operations(文件操作)结构访问驱动程序的函数。每个文件都与自己的函数集相关联(通过包含指向file_operations结构的f_ops指针段实现),这些操作主要负责系统调用的实现[4]。为了使驱动程序在结构的定义发生变化时更具可移植性,并且使得代码更加紧凑且易读,我们首先采用标记化格式声明ADS8364的file_operations结构:static struct file_operations ad_fops = {全功能版J-LINK ARM仿真器V6.0 owner: THIS_MODULE,/* ad_fops所属的设备模块 */read:ads8364_read,/*从设备中读数据*/poll:ads8364_poll, /*查询设备状态*/ioctl:ads8364_ioctl,/*设备I/O控制*/全功能版J-LINK ARM仿真器V6.0 open:ads 8364_open,/*打开设备操作*/release:ads 8364_release,/*释放操作*/};file_operations结构体把系统调用和驱动程序关联起来。这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read、write、open等操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这就是ADS8364驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充 file_operations的各个域。下面我们就把file_operations的各个域的功能和主要调用函数加以介绍。1)设备I/O控制操作函数与普通文件相比,设备文件的操作要复杂得多,不可能简单地通过 read、write 和llseek 等来实现。所有其它类型的操作都可以通过VFS的ioctl调用来执行,函数定义如下:static int ads8364_ioctl(struct inode * inode, struct file *filp, unsigned int cmd, unsigned long arg) ads8364_ioctl是ADS8364驱动程序中对设备的I/O通道进行管理的函数。其中参数inode就是用户程序打开ADS8364时使用open函数返回的文件标示符,cmd就是用户程序对ADS8364的控制命令,它是唯一联系用户程序命令和驱动程序支持的途径,该函数通过cmd区分操作,通过arg传递参数和结果。在驱动程序中实现的ads8364_ioctl函数体内,有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。 ads8364_ioctl的设计的关键就是,如何将cmd命令码在用户程序里生成以及在驱动程序里的解析,所以switch{case}结构在 ads8364_ioctl中至关重要,因为对设备的I/O控制都是通过这一部分的代码实现的。cmd参数的内容主要有以下:通过调用copy_from_user( )内核函数将应用程序中由参数arg传来的数据结构传给内核的同类型结构;采样开始控制命令,通过调用采样开始函数,使能AD开始控制管脚来开始采样,该函数通过调用copy_from_user( )内核函数将应用程序中由参数arg传来的数据结构传给内核的同类型结构;获得采样频率,控制ADS8364按照用户要求的采样频率进行采样。2)查询设备状态操作static unsigned int ads8364_poll(struct file * filp, poll_table * wait)该函数用于查询ADS8364的状态,以便用户程序获知是否能对设备进行非阻塞的访问。它的设计比较简单,它通过调用API函数poll_wait()把当前阻塞的进程添加到wait参数指定的等待列表(poll_table)中。3)从设备中读数据操作static ssize_t ads8364_read(struct file * filp, char * buf, size_t count, loff_t * l)全功能版J-LINK ARM仿真器V6.0 该函数由用户程序调用,它主要通过调用内核函数copy_to_user()把内核空间已经完成转换的数据读入到用户空间。这是内核与用户交换数据的接口函数。4)打开设备操作static int ads8364_open(struct inode * inode, struct file * filp)ads8364_open ()函数必须对将要进行的I/O操作做好必要的准备工作,如调用内核函数memset()清除缓冲区,使能中断等。如果设备是独占的,即同一时刻只能有一个程序访问此设备,则open()子程序必须设置一些标志以表示设备处于“忙”状态。当其为NULL指针时,设备的打开操作永远成功,但系统不会通知你的驱动程序。5)释放设备操作static int ads8364_release(struct inode * inode, struct file * filp)全功能版J-LINK ARM仿真器V6.0 ads8364_release是ads8364_open的逆操作,主要用来完成释放驱动程序占用的空间,禁止中断等。4.4 中断处理程序响应中断并提供相应服务的程序称为中断处理程序。在ADS8364的驱动程序中,中断处理程序的主要功能是,控制ADS8364进行采样,并将采样后的数据放入缓冲区,等待应用程序调用read函数读取。我们把中断处理程序注册到系统中,操作系统在硬件中断发生后,调用驱动程序的中断处理处理程序。函数定义如下:static void ads8364_interrupt(int irq, void *dev_id, struct pt_regs *regs){…..stat = AT91_SYS->PIOB_PDSR;//读出中断电平,如果为低电平, 控制ADS8364开始采样。/*单芯片采集6路*/全功能版J-LINK ARM仿真器V6.0 for(j=0;j<6;j++) { tmpdata=*(volatile u16 *)(ADOFFSET[i][j]+RtPara.remapptr); *(ad_priv->head_ptr++)= tmpdata;}…..}5 将驱动程序加载进Linux内核经过上面各部分的编程设计,我们的ADS8364驱动程序已经设计完毕,把上面的各部分程序组成一个源程序文件,并将其命名ADS8364drv.c,然后经过以下步骤将其加载进内核,那么我们的ADS8364芯片就可以在驱动程序的控制下进行工作了。具体加载操作如下:全功能版J-LINK ARM仿真器V6.0 1)将驱动程序源码ADS8364drv.c拷贝到arm/linux /drives/char/目录下。2)修改位于arm/linux/drives/char/目录下的Makefile文件,在该文件中添加下面语句。obj-$(ADS8364_drv)+= ADS8364drv.o。3)修改位于arm/linux/drives/char/目录下的config.in文件,增加如下语句。bool ‘ADS8364_drive’ ADS8364_drv。6 结束语本文就嵌入式数据采集系统中的AD驱动程序的设计进行了叙述,阐述了ADS8364字符设备驱动程序开发的方法和过程。本文作者创新点为:本嵌入式数据采集系统以嵌入式ARM芯片AT91RM9200为处理器,以高精度ADS8364为AD转换芯片,实现了多通道、高精度的数据采集和AD转换,以 LKM机制设计成的ADS8364驱动程序可以减小内核空间,对其它字符设备的学习与开发设计具有一定的指导意义。

技术文章分类
相关技术文章