严林祥,张红雨
(电子科技大学 电子工程学院,四川 成都 611731)
2.45 G无线通信的工作频段处于2.405~2.485 GHz之间,这个频段是国际规定的免费频段。这就为2.45G无线技术的发展性提供了必要的有利条件。目前工业中可以采用的成熟且有统一的协议标准的应用领域有:微波炉、无绳电话、ZigBee、WI-FI、蓝牙等。但也有采用封闭协议通信的2.45G无线通信技术,如:无线语音导游机,无线鼠标,2.45G有源RFID读写器等。它们在硬件上多使用 Nordic Semiconductor公司的nRF24L01系类芯片,各个厂家可以根据自己的需求制定自己的通信协议[1-2]。虽然蓝牙、ZigBee都是标准协议,但是协议复杂、开发难度大、周期长,而非标准无线射频协议具有低功耗、低成本、易开发等优点。
由于Linux操作系统具有开源、授权免费等优势,因此基于Linux操作系统的嵌入式平台在工业控制、远程通信等领域有着广泛的应用前景。当nRF24L01射频模块作为嵌入式Linux平台下的2.45G无线通信单元时,必须开发相应的驱动程序。因为在Linux系统中,所有的外部设备都被看作是目录/dev下的一个文件,并为用户的访问提供了一种标准接口[3]。因此当我们设计好nRF24L01射频模块的驱动程序,我们就可以很方便用于项目的开发,而不必知道它的具体存在形式。
nRF24L01是一款工作在 2.4~2.5 GHz世界通用 ISM频段的超低功耗单片无线收发器芯片。芯片内置频率发生器、功率放大器、晶体振荡器、调制器和解调器等功能模块,外围扩展少量的器件就可以利用全双工的SPI串行接口与MCU实现通信。芯片有125个频点,能够实现点对点、点对多点的无线通信[1-2]。文中基于S3C2440-Linux的嵌入式平台对nRF24L01射频模块进行驱动程序的设计。S3C2440可以使用GPIO模拟SPI接口的工作时序或者使用SPI控制器的方式对射频模块进行操作。使用GPIO模拟SPI接口的工作时序比较容易实现,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低[4]。因此本文研究S3C2440 SPI控制器的情况。S3C2440与nRF24L01射频模块通信主要由6根信号线组成,它们分别为:主机出从机进数据线(MOSI)、主机进从机出数据线(MISO)、时钟线(SCK)、设备选择线(CS)、中断标志线(IRQ)和接收发送模式选择线(CE)[4-5]。nRF24L01射频模块原理图及其与S3C2440的接口连接图,如图1所示。
nRF24L01射频模块主要由nRF24L01芯片、天线和晶体振荡电路组成。S3C2440通过SPI接口对nRF24L01的相关寄存器进行操作,以实现对射频模块的初始化和相关信息处理。如当射频模块发送信息时,S3C2440通过GPG14选择nRF24L01芯片,利用SPI接口控制nRF24L01芯片工作在发送模式,并通过天线将数据发送出去。发送完成后,则使芯片进入低功耗模式(掉电模式、待机模式Ⅰ和待机模式Ⅱ)。在S3C2440对nRF24L01芯片进行寄存器设置阶段则一般使其进入待机模式Ⅰ。此时部分芯片内部振荡器停振,射频收发单元停止工作。待机模式Ⅱ在待机模式Ⅰ的基础上激活了部分必须的时钟缓存器[1-2]。
图1 nRF24L01射频模块与S3C2440连接图Fig.1 Connect diagram of nRF24L01 RF module and S3C2440
文中根据Linux字符设备开发的方法来设计nRF24L01射频模块驱动程序。应用程序通过系统调用对射频模块的设备文件进行操作,而系统调用则通过设备文件的主设备号找到相应的设备驱动程序,然后读取数据结构file_operations中相应的函数指针,最后把控制权就交给该指针所指向的函数[3]。因此nRF24L01射频模块的驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。主要包括open()、read()、ioctl()、pol()l、write()等函数。 调用 ioctl()函数可为用户程序提供各种硬件控制的操作,从而满足模块在不同情况下的应用。
应用程序在打开设备文件时需要调用open()函数。文中open()的主要功能:首先,通过函数 ioremap()完成 SPI物理地址到虚拟地址的重映射操作并使能SPI时钟;然后,对S3C2440的SPI寄存器和S3C2440与nRF24L01的接口进行设置。打开设备的时候先使片选信号线失效,当具体对从设备操作时再使之有效。最后,初始化信号量和等待队列,并且调用函数request_irq()来注册中断处理例程。打开设备的具体流程如图2所示。
nRF24L01的 IRQ引脚根据nRF24L01控制寄存器的不同配置可以代表不同突发情况的中断事件:nRF24L01在发送模式下成功发送数据中断;nRF24L01在接收模式下正确接收数据中断;nRF24L01在发送模式下,达到最大重传次数中断[2]。中断处理函数主要完成区分中断类型,清除中断标志,设置全局变量的功能。
图2 open函数的流程图Fig.2 Flow chart of the function open
应用程序对nRF24L01射频模块这个设备进行操作时,使用select()或poll()系统调用查询是否可对其进行访问可以提高内核运行效率[3]。这个系统调用进而执行内核中射频模块驱动程序中的poll()函数。Linux内核中poll()函数用来监测文件的状态,在文件的状态未发生变化且未超时的情况下它的用户态select()函数将一直阻塞当前进程的运行。若射频模块作为2.45G无线通信的接收单元时,该函数用于和中断函数配合,使得应用系统只有在接收到发射单元发送的数据时才会向下执行,其他时候则处于阻塞状态。poll()函数的部分代码如下:
当用户空间调用poll函数返回文件为可读或可写状态时,则调用ioctl()函数中定义的相关命令对射频模块接收或发送的数据进行处理。
ioctl()函数主要由一些switch分支选择语句构成,用于配置设备的相关参数。对于nRF24L01射频模块来说由于它可以工作于“ShockBurst?”和增强型“ShockBurst?”这两种方式,通过自定义的SET_NRFMOD命令来进行设置,可以通过设置nRF24L01的寄存器EN_AA,和自动重传寄存器来进行选择[2]。
在控制函数中也可以实现对射频模块设备文件进行读写操作的命令。读写操作的命令与file_operations结构中read()和write()函数的原理相同,都是将应用程序要传送数据通过函数 copy_from_user()或 get_user()传到内核空间。 然后把数据调用送到设备或芯片。读操作的功能与写相反,它读取设备的相关信息,调用 copy_to_user()或 put_user()把数据传到用户空间。
为了创建唯一的控制命令号以避免与内核中已有的命令相冲突,可以把每个命令分成:幻数、序数、传输方向和参数大小这四个位段。这些命令的构造放在nrf24l01.h头文件中:
其中nrf24l01_config()函数的主要功能是根据用户空间传递到内核的参数对nRF24L01射频模块的寄存器进行赋值。而send_id()函数则是将要发送的数据写入到nRF24L01的发送缓冲区,然后设置发送模式信号线。send_id()函数如下:
测试时我们将nRF24L01设置为工作在“ShockBurst?”方式,在此工作方式下的数据包格式由前导码、地址地、数据域和CRC校验这4部分组成。其中前导码由硬件自动进行处理,当nRF24L01在发送模式下自动加入前导码,在接收模式下自动去除前导码。它的作用是给芯片稳定接收或发送预留一定的时间。地址长度为3~5字节,它由寄存器SETUP_AW进行设定。数据域为发送包的有效载荷,长度可以为1~32字节。CRC校验是可以选择的,它由控制寄存器中的EN_CRC位来决定[2]。
文中采用如图3的系统结构对上面设计的驱动程序进行简单测试。我们采用单发单收的方式进行测试。发射单元中,应用程序调用ioctl()函数对内核中与nRF24L01寄存器相关的参数进行设置。然后调用SENDID命令将要发送的数据发送出去。
图3 测试系统结构图Fig.3 Schematic diagram of the test system
发送单元部分关键代码如下:
接收单元程序流程图如图4所示,当nRF24L01设置为接收模式时,芯片内部的基带协议引擎会不停地搜索合法的数据包。若数据包的地址和较验位匹配则将数据包的数据域放入接收缓冲区,将置位nRF24L01的状态寄存器的成功接收数据位(RX_DR),同时IRQ中断信号线输出为低电平。当接收到数据后会触发中断处理函数,并使select()系统调用返回设备文件为可读状态。则从接收缓冲区中提取数据,并将数据包中的数据通过串口发送到PC机进行显示。测试系统的实验截图如图5所示。
图4 接收单元流程图Fig.4 Flow chart of the receiving unit
图5(a)是发射单元将发送数据加载到内核并运行用户空间进程的实验结果截图;图5(b)是接收单元将接收到的数据通过串口传输到PC机进行显示的实验结果截图。值得注意的是接收单元和发送单元的数据域长度寄存器的值要设置成一样。若射频模块之间要实现一对多或多对多的方式进行通信,由于nRF24L01在接收模式下有6个数据通道可供选择,因此可以将发射模块发送数据包的地址设置为接收单元6个数据通道中某个未被利用的通道地址。从而实现一个nRF24L01可以接收6个发射单元的数据。若要实现接收单元可以对大于6个发射单元的数据进行接收处理则要采用一些防碰撞算法,如ALOHA算法和二进制搜索算法[6]。
图5 测试系统的实验截图Fig.5 Experimental screenshot of the test system
文中介绍了nRF24L01[7]射频模块电路与驱动程序的设计,该射频模块体积小、功耗低,能够广泛地应用于2.45G无线通信领域中。在嵌入式Linux平台下,该射频模块以字符设备的文件形式提供给用户空间,并给应用程序提供了统一操作接口,从而可以加快具体项目的开发。经测试若本射频模块采用PCB天线,空中传输速率为1 Mbps,输出功率为0 dBm,接收与发送单元可以在7 m范围内实现通信。若引入防碰撞算法,则本设计可以用于RFID读卡器等具体项目中。
[1]黄智伟.单片无线发射与接收电路设计[M].西安:西安电子科技大学出版社,2009.
[2]Semiconductor N.nRF24L01 Single Chip 2.4GHz Transceiver Product Specification[EB/OL].[2007].http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01.
[3]郑强.Linux驱动开发入门与实战[M].北京:清华大学出版社,2011.
[4]怯肇乾,陈永超.ARM-Linux下SPI设备的添加与驱动实现[J].单片机与嵌入式系统应用,2012,12(4):80-81.
KAI Zhao-qian,CHEN Yong-chao.SPI device increment and driver design in ARM-Linux[J].Microcontrollers&Embedded Systems,2012,12(4):80-81.
[5]马瑾,裴东兴,张少杰.基于nRF24L01的无线温湿度测试系统[J].电子设计工程,2012,20(2):64-66.
MA Jin,PEI Dong-xing,ZHANG Shao-jie.Wireless temperature and humidity test system based on nRF24L01[J].Electronic Design Engineering,2012(2):64-66.
[6]邓一文,张红雨,张鹏程,等.RFID高频读写器防碰撞算法研究[J].电子设计工程,2011(19):31-34.
DENG Yi-wen,ZHANG Hong-yu,ZHANG Peng-cheng,et al.Study on the anti-collision algorithm of the RFID read/write device[J].Electronic Design Engineering,2011(19):31-34.
[7]陈晖,张军国,李默涵,等.基于SIC89C52和nRF24L01的智能小车设计[J].现代电子技术,2012(17):12-15.
CHEN Hui,ZHANG Jun-guo,LI Mo-han,et al.Design of smart car based on STC89C52 and nRF24L01[J].Modern Electronics Technique,2012(17):12-15.