摘要:TinyOS系统以其组件结构模型、事件驱动、并发型等优点成为目前最受关注的无线传感器网络操作系统。但TinyOS不支持STM32和CC2 520芯片。因此在分析TinyOS基本原理、NesC编程语言实现机制及其编译过程的基础上,介绍了基于STM32和CC2520的TinyOS移植方法,完成了STM32的I/O组件、Timer组件、USART组件、SPI组件和CC2520芯片驱动的移植。在实现CC2520的基本通信功能基础上,实现简单MAC协议。最后测试了各组件的移植效果。实验测试结果表明,节点可以稳定可靠地通信。
关键词:无线传感器网络;TinyOS;STM32;CC2520;移植;驱动
引言
无线传感器网络(Wireless Sensor Network,WSN)是一种应用相关的网络。需要对某些操作系统进行移植。本文采用TinyOS作为软件平台,成功移植了Radio、Timer、USART、SPI和General I/O等5个底层模块。测试结果表明,移植的5个底层模块能够正常工作。
1 MCU和无线模块介绍
STM32系列按性能分成两个系列:STM32F103“增强型”系列和STM32F101“基本型”系列,时钟频率达到72 MHz,是同类产品中性能最高的产品。本项目采用芯片STM32F103RBT6。CC2520选用第二代ZigBee/IEEE 802.15.4无线电频率(RF)收发器。
2 TinyOS操作系统和NesC分析
2.1 NesC介绍
NesC是专门为网络嵌入式系统设计的编程语言,是对C的扩展,它基于TinyOS的结构化概念和执行模型而设计。主要编程模式包括:事件驱动、弹性并发型和面向组件程序设计等。NesC编译器进行数据竞争检测(提高可靠性)、积极的函数内联(降低资源消耗)等整体程序分析,简化了应用程序的开发。
2.2 TinyOS分析
TinyOS(Tiny Micro Threading Operating System)是由加州大学伯克利分校开发的开源的传感器网络操作系统,其本身是由NesC语言编写。TinyOS的组件模型体系结构如图1所示。上层组件对下层组件发命令,下层组件向上层组件发信号通知事件,最底层的硬件抽象组件直接和硬件打交道。
TinyOS的硬件抽象层通常是3级抽象结构,称作HAA(Hardware Abstraction Architecture)。整个硬件抽象层分为硬件表示层(HPL)、硬件适配层(HAL)和硬件接口层(HIL)。
硬件表示层(Hardware Presentation Layer,HPL)是对硬件平台的功能性描述,主要通过存储单元和I/O映射端口访问硬件和通过硬件中断来实现以下功能:能量管理、控制硬件、硬件中断开闭、提供硬件中断服务程序等。
在HPL上的是硬件适配层(Hardware Adaptation Layer,HAL),该层是整个硬件抽象层的核心,利用HPL提供的原始接口建立硬件描述资源,并通过状态来反映硬件的使用情况以实现对硬件的仲裁控制,提高系统性能。
硬件抽象层的最高层是硬件接口层(Hardware Interface Layer,HIL),该层把HAL层提供的接口转换成硬件独立的接口,隐藏了平台之间的差异,并向上层提供统一的硬件API接口。
3 基于STM32和CC2520平台的TinyOS实现
由上文所述,将TinyOS移植到STM32核处理器和CC2520上的关键问题是硬件抽象层组件的定制和编译器工具链的配置。基于TinyOS开源代码的约定,主要修改代码放置在/tos/chips/STM32和tos/platforms/STM32p103。
3. 1 修改芯片文件
每个芯片都通过多个接口或组件提供它所实现的功能,这些接口或组件组成芯片的驱动。将芯片的结构抽象文件放于tos/chips。如果芯片有子系统则建立子目录,如:tos/chips/STM32/timer。需要改写的TinyOS和MCU相关模块如下所述。
3.1.1 STM32核处理器
为了同其他外围设备的驱动分开,需要在MCU相关的文件中增加两个定义;原子操作的开始和结束,在头文件hardware.h中定义;低功耗工作模式,由组件MCUSleepC定义。具体可以参考其他MCU文件编写。
TinyOS通过一些接口管理MCU的状态,决定MCU何时进入低功耗状态。MCUSleepC组件向上层提供McuSleep和McuPowerState接口。TinyOS调度器在原子操作中调用McuSleep.sleep(),保证在进入低功耗状态前处于开中断状态。在sleep状态,可以关闭一些高耗能的模块,比如:高频时钟、PLL等。
3. 1.2 修改I/O、LED灯、Timer、USART、SPI组件
(1)通用I/O接口
HIL层组件通过3个接口描述MCU可以控制的通用输入输出引脚。General I/O接口描述输入输出引脚被清零或置位状态、设置为输入或输出模式。GpioInterrupt接口描述单个引脚触发的中断,可以对每一个引脚分别配置为上升沿触发或者下降沿触发中断。GpioCapture接口描述捕获单个引脚发生的事件,可以对每一个引脚分别配置为上升沿触发或者下降沿触发。
(2)LED灯
TinyOS最多可独立支持平台上3个LED灯。TinyOS通过PlatformLedsC组件直接访问3个LED灯,LedC和LedP组件再通过装配PlatformLedsC组件,向上层组件提供Led接口,相当于General I/O接口的使用。在PlatformLedsC组件中,可以对每种开发板指定相应的LED引脚。
(3)Timer
MCU时钟通常有3种常见的功能:控制、时钟/计数器、触发。将与MCU时钟相关的文件放到chips/STM32/timer。时钟的通用功能通过一系列的接口定义实现:
(4)USART
节点可以通过USART与PC通信,将与USART相关文件放于chips/STM32/uart。本文只通过HplSTM32UartNoDmaP组件的STM32Uart1C配件实现了简单的USART功能。串口的初始化包括:波特率、字长、奇偶校验位、流控制、打开串口时钟和配置串口发送接收引脚等。
(5)SPI
由于CC2520通过SPI接口和MCU通信,所以必须实现SPI接口。SPI的初始化包括:时钟信号的相位和极性、NSS模式、数据帧格式、主从模式等。
3.2 CC2520驱动
在tos/chips/cc2520中,定义与CC2520芯片相关的组件,这里面的组件直接对芯片进行操作。在tos/platforms/cc2520文件夹中定义CC2520和MCU交互的接口。CC2520通过SPI接口与STM32完成设置和收发数据两方面的任务。
CC2520工作流程如下:应用程序中使用SplitControl接口,该接口最终连接到CCDriverLayerP上,在SoftwareInit.init中对CC2520使用的和MCU相连的引脚进行初始配置。
首先,复位CC2520,并且关闭电压调整器,根据Datasheet,延时1100 μs,延时通过接口BusyWait<T32khz,uint16_t>。延时完成后,开启电压调整器,并延时200μs后使能CC2520。完成后,申请SPI资源,该资源接口连接在CC2520DriverLayerP上,资源申请允许后,在CC25 20DriverLayerP中捕获到SpiResource.granted事件,然后开始通过SPI接口初始化CC2520,调用startOscillator。之后,调用initRadio对CC2520的寄存器进行相应配置。
而在接收模式时,CC2520收到物理帧的SFD字段后,会在SFD引脚输出高电平,直到接收完该帧。如果启用了地址识别,在地址识别后,SFD引脚立即转为输出低电平。FIFO和FIFOP引脚标识FIFO缓存区的状态。如果接收FIFO缓存区有数据,FIFO引脚输出高电平;如果接收FIFO缓冲区为空,FIFO引脚输出低电平。TinyOS无线模块组织架构图如图2所示。
3.3 修改平台文件
在tos/platforms下放置平台相关文件,将物理驱动相关的组件连接起来组成具体平台。创建一个平台需要完成5个部分:
①“.platform”文件告知编译系统驱动文件的位置,由一系列的包含路径和NesC的参数组成。ncc将“.platform”文件当作perl脚本读出,并将参数传递到NesC预编译器。
②平台导入程序PlatformP/PlatformC,通过调用两个初始化接口platformC.Init和MainC.softwarelnit来激活MCU、传感器和射频等硬件模块及相关软件。platformC.Init通常完成时钟校准和引脚设置,保证硬件在可操作状态。
③与硬件具体功能相关的头文件“hardware.h”和将芯片与平台结合的具体代码。“hardware.h”包含了其他硬件子系统的头文件,并被主文件“MainC.nc”所包含。
④修改链接脚本文件,仿照其他平台文件在tos/platforms文件夹下建立tos.x文件,设置rom和ram的起始地址和大小。设置中断向量表、代码段、数据段、非初始化数据段和其他段的放置位置。
⑤定义中断向量表,在tos/platforms/STM32-p103/vectors文件夹下新建STM32-vector.c文件,用于放置中断向量表,按照STM32提供的中断配置中断向量表,并初始化默认中断处理函数,实际应用中这些中断处理函数可以在其他文件中重定义。比如时钟中断处理函数在McuSleepC.nc中重定义。
3.4 修改编译工具链
3.4.1 配置交叉开发工具链
STM32支持的编译器有很多,比如:ARM—NONE—EABI—GCC、KEIL、IAR等。本文采用ARM—NONE—EABI—GCC编译器。NesC程序的编译主要分两步,首先调用ncc编译器把NesC预编译成C文件(即预编译过程);然后通过一个脚本程序,将经过NesC预编译生成的TinyOS应用程序转换成可以下载到开发板的hex格式文件;最后送入J—Flash ARM下载器下载到硬件。修改后的编译工具链如图3所示。
下面对这个过程进行详细的介绍。
第一步:源组件文件(.nc源文件)的分析和转换,在这个阶段调用TinyOS自带的编译器ncc,对.nc源文件进行文法、语法分析,检测共享数据缓冲区等。再根据各个组件和接口对其使用的函数和变量进行名字扩展,使其具有全局唯一性。最后把所有的函数和变量都整合到一个主函数main()中,并生成相应的app.c文件。此时生成的已经是普通的C语言程序。以上这个替换过程是由ncc的核心程序nescl.exe完成的。
第二步:在生成app.c文件后,ncc调用arm—none—eabi—gcc的交叉编译器工具对该C文件进行编译、链接生成可执行文件main.exe,然后通过arm—none—eabi—objcopy工具转换成main.hex,以便最后下载到STM32平台上运行调试。
3.4.2 定制编译环境
为使编译系统能够寻找到编译平台,在工程主文件夹下增加环境定制文件“setup.sh”,定制工程目录、编译工具、解释工具等路径。在/support/make目录增加“STM32-p103.target”文件。定制STM32的MAKE系统。在make文件中指定用arm—none—eabi—gcc为编译器,arm-none-eabi-objcopy为输出格式转换器。
以一个简单的应用程序radioCountToLeds为例,在STM32平台上编译的结果如图4所示。
可以通过TinyOS自带的工具生成各个组件的连线关系,只需要在make命令后面添加docs选项。
4 测试
TinyOS自带的radioCountToLeds测试程序,需用通用I/O接口、Timer、Leds、SPI和无线模块(CC2520)驱动等组件。为了查看USART模块驱动,也为了便于跟踪程序执行流程,在源程序中加入了串口输出代码,结果如图5所示。
本实验通过一个节点每一秒定时广播数据包,定时器中断触发时发送一个16位的数据,并把此数加1。其他节点接收此数据,并根据接收数据的低3位是否为1来分别控制3个Led灯闪烁。通过观察节点上Led灯的有规律闪烁,能够证明各模块成功移植。
结语
本文简单介绍了NesC语言的特点和TinyOS系统架构,重点介绍了TinyOS的编译机制,并在此基础上详细说明了将TinyOS内核程序移植到以STM32核处理器和CC2520无线模块芯片为核心的开发板上的具体方法。通过测试发现,各个模块能够正常稳定工作。但无线模块协议栈还不够完善,功能还不全面,仍需要进一步的研究。