串口AD嵌入式Linux驱动实现
2010-07-14杨小容陈建政
杨小容,陈建政
(西南交通大学牵引动力国家重点实验室,四川 成都 610031)
1 引 言
Linux操作系统的迅猛发展,与其具有的良好特性是分不开的。Linux是一种性能优良、源码公开、多用户、多任务操作系统,目前主要运用在大型服务器领域、网络处理应用和嵌入式系统。随着现代智能设备的不断升级换代,基于ARM等嵌入式系统等的32位机智能系统在现代生活中的地位也越来越重要。为了加强在嵌入式系统领域的优势,Linux2.6已经在内核中加入了提高中断性能和调度响应时间的改进,包括采用可抢占内核、效率更高的调度算法和同步特性。另外,Linux2.6内核加入了包括S3C2440X在内的多种微控制器的支持,并开始支持多种流行的无MMU单元的微控制器,如Dragonball、ColdFire、Hitachi H8/300。掌握嵌入式Linux驱动开发及AD数据采集系统的开发,实现高速、可靠的数据采集理念,并可将其用于高速列车的分布式数据采集系统中。
2 硬件说明
2.1 ARM微处理器S3C2440
S3C2440是三星公司推出的采用RISC结构的16/32位微处理器。它基于ARM920T内核,采用五级流水线和哈佛结构,开发板布局简洁明了,外设齐全,接口标准,扩展方便。最高频率可达400MHz,低功耗,高性能,适合于PDA、便携媒体播放器、卫星导航仪等多媒体终端。文件系统采用YAFFS文件系统,YAFFS类似于JFFS/JFFS2,是专门为NAND闪存设计的嵌入式文件系统,适用于大容量的存储设备[1-2]。
S3C2440有2个SPI口,可实现串行数据传输,每个SPI接口各有2个移位寄存器分别负责接收和发送数据。发送数据和接收数据是同步进行的,传送的频率可由相应的控制寄存器设定。数据传输时,一个SPI系统作为“主机”控制数据流,其他SPI作为“从机”,主机控制数据的输入和输出。S3C2440的SPI口包括2条数据线及2条控制线:
主机输出从机输入(SPIMOSI):数据线将主机输出数据作为从机输入。
主机输入从机输出(SPIMISO):数据线将从机输出作为主机输入。传输时,只有单从机传输数据。
串行时钟(SPICLK):控制线由主机驱动,用来调节数据流。主机传输数据波特率可变,传输一位,产生一个SPICLK周期。
从机选择(NSS):控制线允许硬件开关从机。可与译码器结合控制多个外设。
该文采用 MISO,MOSI,SPICLK0 作为 SPI通信信号,NSS0作为TLC2543的片选信号。如图1所示为S3C2440与TLC2543的接口电路图。
图1 S3C2440与TLC2543的连接图
2.2 TLC2543引脚及原理
TLC2543是TI公司生产的一种12位开关电容逐次逼近A/D转换器[2-5]。芯片共有11个模拟输入通道(AIN0~AIN10)。芯片的三个控制端串行三态输出数据端(DATA OUTPUT)、输入数据端(DATA INPUT)、输入/出时钟(I/O CLOCK)能形成与微处理器之间数据传输较快和较为有效的串行外设接口(SPI)。片内具有一个14通道多路选择器用于在11个模拟输入通道和3个内部自测试(SELl-TEST)电压中任选一个。可通过对其8位内部控制寄存器进行编程完成通道的选择,并可对输出结果的位数、MSB/LSB导前和极性进行选择。如图2是TLC2543时序图(MSB FIRST),TLC2543在程序中定义16位输出,但实际转换结果仍为12位,只不过在数据传送过程中4个LSB位被置为0。
TLC2543的片选信号CS变低时开始转换和传送过程,I/O CLOCK的前8个上升沿将8个输入数据位键入输入数据寄存器,同时它将前一次转换的数据的其余11位移出DATA OUT端。TLC2543收到4个时钟信号后,开始对选定的通道的模拟量进行采样。并保持到第16个时钟的下降沿,之后开始对采样的模拟量进行转换,大约需10 μs,转换的数据保存在输出数据寄存器中待下一个工作周期输出。
3 设备驱动程序
设备驱动程序是操作系统内核和机器硬件之间的接口,它为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件。应用程序可以和操作普通文件一样,只需要进行文件的打开、读写和关闭等操作,就能控制底层的具体硬件设备[6-10]。在Linux操作系统下有三类主要的设备文件类型,即字符设备、块设备和网络设备。模数转换芯片TLC2543属于字符设备类(char device),这也是使用最多的设备类别。
3.1 A/D模块的初始化函数
使用一个设备之前要首先对其进行初始化,A/D设备驱动的初始化函数AD_init_module(void)主要完成两个部分的操作,第一是对A/D的PIO口进行初始化设置,并对SPI的工作方式、各控制寄存器和模式寄存器等配置参数;第二是完成A/D设备驱动程序向Linux内核注册,为其动态的申请一个设备号。
图2 TLC2543时序图(MSB FIRST)
初始化工作首先要对S3C2440的SPI控制器编程,设置SPI工作方式,完成对控制寄存器SPCON设置、波特率SPPRE设置和GPIO口的设置等。由于在Linux下是不允许对设备的物理地址进行直接访问的,必须转换为虚拟地址后才能访问,定义AD_devices=kmalloc(sizeof(structAD_dev),GFP_KERNEL),并动态分配内存 memset(AD_devices,0,sizeof(struct AD_dev))。
设置完各参数后还需要将设备驱动程序向Linux内核注册,利用函数int register_chrdev(unsigned int major,constchar*name,struct file_operations*fops)其中:major是感兴趣的主编号,name是驱动的名字(出现在/proc/devices),fops是缺省的 file_operations结构。这样基本完成了设备驱动的初始化函数。
3.2 设备操作的具体实现函数
该驱动的工作流程如图3所示。Linux内核是通过一个file_perations的结构体来组织对设备操作的具体实现函数,结构中的成分几乎全是函数指针。包含了驱动程序提供给应用程序访问硬件设备的各种方法。每个成员都对应一个系统调用,当用户对设备文件进行操作时,系统调用通过设备文件的主设备号找到相应设备的驱动程序,然后找到file_operations中相应的函数指针,并由该函数进行以下的控制[2-5,11-12]。需要对SPI端口进行打开、关闭、接收、发送、传输数据操作,因此定义file_operations结构体如下:
int AD_open(struct inode*inode,struct file*filp);
int AD_release(struct inode*inode,struct file*filp);
static ssize_t AD_write (struct file*file,const char*buf,size_t count,loft_t*offset);
其中读设备方法主要实现A/D转换后的结果输出到用户空间中,写设备方法则相反,它从用户空间读取数据,对A/D设备写命令字进行控制。用户利用SPI接口发送和接收数据时,系统就会分别调用AD_write和 AD_read函数及 AD_convert和 spi_tx_data函数。AD_read函数中需要注意参数buf和count。buf是用户空间缓冲区,read调用的返回数据保存在这里。count是指buf空间的大小,用字节数表示。当S3C2440的状态寄存器(在此使用的是GPH的数据寄存器GPHDAT)设置为TXD时进行A/D转换,可以通过 copy_to_usr(buf,from,count)函数将接收的数据传送给用户程序。AD_write函数中的buf和count参数同AD_read函数中的说明,可通过copy_from_usr(to,buf,count)函数接收用户需要发送的数据。
文件的打开(AD_open)和关闭(AD_release)函数只是将模块使用计数加一、减一。open调用时如果返回-1则表示打开设备文件不成功,close调用时只需关闭open函数返回的文件描述字所连的设备即可。
对于A/D转换及其数据传输,定义:
void AD_convert(void);
void spi_tx_data(unsigned char data);
A/D转换函数AD_convert中首先设置寄存器为发送状态,调用spi_tx_data通过中断方式完成数据发送。S3C2440的SPI接口设置为中断传送模式进行串行数据传输:int request_irq(unsigned int irq,irqreturn_t(*handler)(int,void*,structpt_regs*),unsignedlongflags,constchar*dev_name,void*dev_id)。
3.3 设备注销函数
与设备初始化函数相对应,设备注销函数void AD_cleanup_module(void)主要完成释放内存空间、端口等操作,其核心部分是由字符设备注销函数unregister_chrdev (unsigned intmajor,constchar*name)组成的,该函数完成驱动程序向Linux内核的注销,当设备驱动程序注销以后,内核收回所分配的设备号。
4 驱动程序的编译与加载
完成了驱动程序的编写,接下来就要对生成的.c文件进行编译。采用Linux中的交叉编译工具arm-linux-gcc编译驱动程序为目标文件[1,7-10]。驱动代码完成后,需要编写一个makefile:
执行make命令后,生成ad.ko。启动开发板后,在minicom终端下进入开发板/tmp目录,执行rz命令,将ad.ko发送到目标板上。随后执行动态加载命令:
#insmod ad.ko
这时可以用命令lsmod查看动态加载模块:
#lsmod
为了正确使用设备驱动程序,必须先创建设备文件,可以用mknod命令来把设备映射为一个设备文件,用户的应用程序就可以像操作标准文件那样来访问硬件设备了:
#mkmod/dev/ad c major minor
c是指字符设备,major是主设备号,minor为次设备号。
至此,完成了SPI设备TLC2543的驱动程序的开发。配合Linux环境下的数据采集程序将其用于高速动车组,这是传统的单片机数据采集系统无法比拟的。
5 结束语
应用带SPI接口的串行A/D转换器占用较少的微处理器I/O资源,硬件连接简单,软件易于实现,程序运行效率高。带有SPI接口的串行A/D转换器和ARM微处理器的结合可广泛应用于实现数据采集功能的掌上设备及其他嵌入式的系统,如医疗仪器、通信设备、抄表设备等领域。
[1]刘 淼.嵌入式系统接口设计与Linux驱动程序开发[M].北京航空航天大学出版社,2006.
[2]沃尔瓦诺.嵌入式微计算机系统:实时接口技术[M].李 曦,等译.北京:机械工业出版社,2003.
[3]魏永明.Linux设备驱动程序[M].2版.北京:中国电力出版社,2002.
[4]孙天泽,袁文菊,张海峰.嵌入式设计及Linux驱动开发指南[M].北京:电子工业出版社,2005.
[5]潘志东,刘增华.串行A/D转换器TLC2543原理及应用[J].电测与仪表,2001,38(3):40-43.
[6]倪继利.Linux内核分析及编程[M].北京:电子工业出版社,2006.
[7]冯国进.嵌入式Linux驱动程序设计从入门到精通[M].北京:清华大学出版社,2008.
[8]孙 琼.嵌入式Linux应用程序开发详解[M].北京:人民邮电出版社,2007.
[9]罗苑堂,杨宗德.嵌入式Linux应用系统开发实例精讲[M].北京:电子工业出版社,2007.
[10]Robbins A.实战Linux编程精髓[M].杨明军,译.北京:中国电力出版社,2005.
[11]宋宝华.Linux设备驱动开发详解[M].北京:人民邮电出版社,2005.
[12]李胜朝,黄先祥,谢 建.嵌人式Linux系统中字符设备驱动程序的开发[J].计算机工程,2007,33(4):5-8.