打印
[STM32F0]

基于STM32和CC2520的TinyOS移植与驱动分析

[复制链接]
1288|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Orchids|  楼主 | 2019-10-5 14:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
基于STM32和CC2520的TinyOS移植与驱动分析



摘 要: TinyOS系统以其组件结构模型、事件驱动、并发型等优点成为目前最受关注的无线传感器网络操作系统。而STM32和CC2520以其高性能、低成本、低功耗被广泛应用于无线传感器网络嵌入式系统。 但TinyOS 不支持STM32和CC2520芯片。因此在分析TinyOS 基本原理、NesC 编程语言实现机制及其编译过程的基础上,介绍了基于STM32和CC2520的T inyOS移植方法,完成了STM32的IO组件、Timer 组件、USART组件、SPI 组件和CC2520芯片的移植。在实现CC2520的基本通信功能基础上,实现简单mac协议。最后测试了各组件的移植效果,实验测试结果表明,节点可以稳定可靠通信。
关键词:无线传感器网络;TinyOS;STM32;CC2520;移植;驱动


引 言
无线传感器网络(Wireless  Sensor  Network,  WSN)是一种应用相关的网络。为满足不同环境和应用需求,需要选择或设计特定的硬件节点和操作系统,而许多操作系统在设计之初支持的硬件节点类型又十分有限,因此需要对某些操作系统进行移植。本文选用的无线传感器节点的核心部件为意法半导体公司STM32系列MCU和TI第二代射频芯片(CC2520),这两款芯片都是高性能、低成本、低功耗的。广泛用于无线传感器网络相关应用。采用TinyOS 作为软件平台,成功移植了Radio、Timer、USART、SPI和GeneralIO等 5个底层模块。测试结果表明,移植的5个底层模块能够正常工作。


1 MCU和无线模块介绍
STM32系列按性能分成两个系列:STM32F103“增强型”系列和STM32F101“基本型”系列,时钟频率达到72MHz,是同类产品中性能最高的产品。时钟频率72MHz时,从闪存执行代码,STM32功耗36mA,是32位市场上功耗最低的产品,相当于0.5mA/MHz。本项目采用STM32F103RBT6。
CC2520是第二代ZigBee/IEEE 802.15.4无线电频率(RF)收发器。该款CC2520产品具有当今业界最佳的选择性/共存性及优异的链路预算功能特点,其目标在于满足各种应用中ZigBee/IEEE 802.15.4同专有无线系统的要求。


2 TinyOS操作系统和NesC分析
2.1 NesC介绍
NesC是专门为网络嵌入式系统设计的编程语言,是对C的扩展,它基于体现TinyOS 的结构化概念和执行模型而设计。主要编程模式包括:事件驱动、弹性并发型和面向组件程序设计等。NesC 编译器进行数据竞争检测 (提高可靠性) [4]、积极的函数内联 (降低资源消耗) 等整体程序分析,简化了应用程序的开发,缩小了代码大小,减少了许多潜在诱发错误的因素。因此,NesC 语言设计的这些特征很好的满足这个领域程序设计的特定要求。
2.2 TinyOS分析
TinyOS(Tiny Micro Threading Operating System)是由加州大学伯克利分校开发的开源的传感器网络操作系统,其本身是由 NesC 语言编写。其设计的主要目标是代码量小、耗能少、并发性高、鲁棒性好,可以适应不同的应用。TinyOS主要采用了基于组件的体系结构,事件驱动机制,轻量级线程技术,基于FIFO(First In First Out)的任务队列调度方式和主动消息通信方式等思路。完整的系统由一个调度器和一些组件组成,应用程序与组件一起编译成系统。TinyOS的组件模型体系结构如图1所示。



图1  TinyOS组件模型体系结构
上层组件对下层组件发命令,下层组件向上层组件发信号通知事件,最底层的硬件抽象组件直接和硬件打交道。
TinyOS 的硬件抽象层通常是3级抽象结构,称作HAA(Hardware  Abstraction Architecture) [6][7]。整个硬件抽象层分为硬件表示层 (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/STM32-p103。
3.1 修改芯片文件
每个芯片都通过多个接口或组件提供它所实现的功能,这些接口或组件组成芯片的驱动。将芯片的结构抽象文件放于tos/chips。如果芯片有子系统则建立子目录,如:tos/chips/STM32/timer。需要改写的TinyOS和MCU相关模块如下所述。
3.1.1 STM32核处理器
为了同其他外围设备的驱动分开,需要在MCU 相关的文件中增加两个定义:(1)原子操作的开始和结束,在头文件 hardware.h 中定义;(2)低功耗工作模式,由组件 MCUSleepC 定义。具体可以参考其他MCU文件编写。
TinyOS 通过一些接口管理 MCU的状态,决定MCU 何时进入低功耗状态。MCUSleepC组件向上层提供McuSleep和McuPowerState接口。TinyOS调度器在原子操作中调用McuSleep.sleep(),保证在进入低功耗状态前处于开中断状态。在sleep状态,可以关闭一些高耗能的模块,比如:高频时钟、PLL等。
3.1.2 修改IO,LEDs,Timers,USART,SPI组件
(1) 通用IO
HIL层组件通过三个接口描述 MCU可以控制的通用输入输出引脚。GeneralIO接口描述输入输出引脚被清零或置位、设置为输入或输出。GpioInterrupt 接口描述单个引脚触发的中断,可以对每一个引脚分别配置为上升沿触发或者下降沿触发中断。GpioCapture接口描述捕获单个引脚发生的事件,可以对每一个引脚分别配置为上升沿触发或者下降沿触发。GpioInterrupt接口和GpioCapture接口比较相似,主要区别是对GpioInterrupt接口的调用不累积,只有最后一次连续触发的事件会被处理。而GpioCapture接口只要事件触发就会处理。具体例子:参考cc2520驱动文件CC2520DriverLayerP.nc中对SFD信号的处理。
虽然这三个接口被定义为 HIL接口,但也可使用这三个接口来代表 HPL,CC2520 采用这种表示。
(2) LED 灯
TinyOS 最多可独立支持平台上三个LED灯。TinyOS通过PlatformLedsC组件直接访问三个LED灯,LedC和LedP组件再通过装配PlatformLedsC组件向上层组件提供Led接口,相当于GeneralIO接口的使用。在 PlatformLedsC组件中可以对每种开发板指定相应的Led引脚。
PlatformLedsC 组件使用 Init 接口连接到PlatformP组件的Init接口,保证包含 LedC 组件的应用程序的初始化。PlatformP 组件的Init接口是开发板重启后Tinyos操作系统首先开始初始化的部分,这里可以进行一些系统启动必要模块的初始化,比如:时钟、中断、低功耗组件等。
(3) Timers
MCU时钟通常有三种常见的功能:控制,时钟/计数器,触发。将与MCU时钟相关的文 件放chips/STM32/timer。时钟的通用功能通过一系列的接口定义实现。如下所示:
interface Counter<precision_tag, size_type>//提供当前时钟和处理当前时刻的溢出事件
interface Alarm< precision_tag, size_type >  //给定时间内的扩展计数
interface BusyWait< precision_tag, size_type >//短时同步延时
interface LocalTime< precision_tag >//提供32位的计数器,没有溢出处理
interface Timer< precision_tag >//提高指定精度的定时器,有只触发一次和触发多次之分
Timer组件的三个最主要属性是时钟精度、时钟宽度和精确度。TinyOS提供的时钟精度有:TMilli(1024嘀嗒每秒)、T32khz(32768嘀嗒每秒)和TMicro(1048576嘀嗒每秒)。时钟宽度有:8位、16位、32位和64位。推荐是32位,本项目也使用32位。精确度主要是相应Timer组件实际提供的精度和声称的精度的误差,影响精确度的因数主要有:时钟漂移和硬件限制等。STM32的mcu能提供最高25MHz的时钟,足以满足TinyOS内部需求。
不同的平台可以针对不同需求在Timers组件中选择实现上面5种接口。可以提供不同精度、不同宽度的组件。通常一个计时单元由一个计数器和一些比较寄存器组成,比较寄存器可以触发中断。HPL组件将MCU时钟的这些硬件特性抽象划,通过Counter接口和Alarm接口提供给高层组件。HAL组件具体指定了时钟频率和位数。每个平台的 HIL组件都应包括:HilTimerMilliC 和 BusyWaitMicroC,以保证适应各种平台。
(4) USART
节点可以通过USART与PC通信,将与USART相关文件放于chips/STM32/uart。本文只通过HplST-
M32UartNoDmaP组件STM32Uart1C配件实现了简单的USART功能。串口的初始化包括:波特率、字长、奇偶校验位、流控制、打开串口时钟和配置串口发送接收引脚等。
    有了串口的输入输出功能后,为了方便调试,可以自己重写printf函数,实现终端输出重定向到PC,通过串口工具来分析终端运行情况。具体设计是:如果输出字符串,则直接调用UartStream.send命令;如果是输出数字,则可以先重写itoa函数,把数字按指定进制转换成字符串,然后按照输出字符串方式处理。
(5)SPI
由于CC2520通过SPI接口和MCU通信,所以必须实现SPI接口。SPI接口的处理方式和USART接口的处理方式很相像,将与SPI相关的文件放于chips/STM32/spi。本文只通过 HplSTM32SpiP组件和STM32Spi1C配件实现了简单的SPI功能。SPI的初始化包括:时钟信号的相位和极性、NSS模式、数据帧格式、主从模式等。
3.2 CC2520驱动
在tos/chips/cc2520中定义与CC2520芯片相关的组件,这里面的组件直接对芯片进行操作。比如:一些直接操作CC2520的函数接口和兼容TinyOS上层接口提供必要的封装等。在tos/platforms/cc2520文件夹中定义CC2520和MCU交互的接口,比如:CC2520和MCU的引脚连接配置、使用的时钟等。
CC2520通过SPI接口与STM32完成设置和收发数据两方面的任务。SPI接口由CSn、SI、SO和SCLK 四个引脚构成。STM32为接口主设备,访问CC2520内部寄存器和存储区;CC2520为SPI接口从设备,接收时钟信号和片选信号,并在处理器的控制下执行输入/输出操作。CC2520通过SFD、FIFO、FIFOP和CCA四个引脚与STM32表示收发数据状态。
CC2520大概工作流程:应用程序中使用SplitControl接口,该接口最终连接到CCDriverLayerP上,在SoftwareInit.init中对CC2520使用的和MCU相连的引脚进行初始配置,比如:把CSN、VREN和RSTN配置为输出模式,把CCA、SFD、FIFO和FIFOP配置为输入模式。
首先,复位CC2520,并且关闭电压调整器,根据Datasheet,延时1100us,延时通过接口BusyWait<T32khz,uint16_t>。延时完成后,开启电压调整器,并延时200us后使能CC2520,完成后,申请SPI资源,该资源Resource接口连接在CC2520DriverLayerP上,资源申请允许后,在CC2520DriverLayerP中捕获到SpiResource.granted事件,然后开始通过SPI接口初始化CC2520,调用startOscillator。之后调用initRadio对CC2520的寄存器进行相应配置。至此完成对CC2520启动配置。应用程序执行发送命令时最终连接到CC2520DriverLayerP的RadioSend.send命令,在这里可以对发送进行完全的控制,比如在发送过程中可以通过捕获SFD上升沿信号来对发送准确计时。CCA引脚有效表示信道空闲评估有效,通常为CSMA-CA算法的实现提供依据。
而在接收模式时,CC2520收到物理帧的SFD字段后,会在SFD引脚输出高电平,直到接收完该帧。如果启用了地址识别,在地址识**,SFD引脚立即转为输出低电平。FIFO和FIFOP引脚标识FIFO缓存区的状态。如果接收FIFO缓存区有数据,FIFO引脚输出高电平;如果接收FIFO缓冲区为空,FIFO引脚输出低电平。FIFOP引脚在接收FIFO缓存区的数据超过某个临界值时或者在CC2520接收到一个完整的帧以后输出高电平,触发STM32的中断。在CC2520DriverLayerP中对中断进行处理,调用downloadMessage把接受到的数据从rxfifo中读取出来,然后向上层提交。下面简单介绍CC2520驱动文件和上层文件的交互,具体请看图2:




图2 TinyOS无线模块组织架构图
最上面是应用层组件,下面一层是Active Message Layer(主动消息层),由于会出现多个服务利用同一个radio通信的情况,TinyOS提供了AM层来多元访问radio。AM层主要通过ActiveMessageC组件实现,一个平台必须提供ActiveMessageC作为包级别通信的基本HIL。ActiveMessageC提供尽最大可能交付的,单跳的通信抽象。每个active message有一个16位的目的地址和一个8位的类型字段。
Unique Layer(唯一收发层):为报头生成一个唯一的数据序列号(DSN,字节单位)。每向外分发一个包,该字节就加1。该字节由一个随机数字初始化。接收方通过检查源地址和DSN字节来识别该包是否重复。同时在接收情况下,维护最近一段时间接受到的包的源地址和DSN字节,识别重复接收到的包。
Packet Link Layer(数据包链路层):本层提供超时自动重传功能。当没有监听到接收方发来ACK时,重传包。PacketLink是基于每个包来激活的,除非包在发送前配置好重传功能,否则Packet Link不会为该包启动自动重传功能。相对于硬件自动应答,当软件应答机制启用时,Packet Link是最可靠的。
Low Power Listening Layer(低功耗监听层):该层负责打开和关闭无线发射器,检查接收情况。低功耗监听传输是基于每包的。
Tinyos Network Layer(Tinyos网络层):本层允许TinyOS 2.x无线协议栈与非TinyOS网络交互。6LowPAN草案在标准的802.15.4头部后有一个网络标识字节[8]。如果使用交互帧(interoperability frames),那么这个分派层就要提供设置发送包该标识字节和过滤接收到的非TinyOS包的功能,
Carrier Sense Multiple Access (CSMA)载波监听多路访问层:本层负责为发送包定义802.15.4 FCF字节,当检测到信道已经在使用时,提供默认的退避时间,定义无线的power-up/power-down过程。
CC2520 Driver Layer(CC2520驱动层):本层实际操作硬件,CC2520DriverLayerP作为HPL层核心文件,主要功能有:芯片初始化、打开关闭、数据发送和接收、对时序的控制、中断处理等。
3.3 修改平台文件
在 tos/platforms 下放置平台相关文件, 将物理驱动相关的组件连接起来组成具体平台。创建一个平台需要完成五个部分:(1)“.platform”文件告知编译系统驱动文件的位置,由一系列的包含路径和NesC 的参数组成。ncc 将“.platform”文件当作 perl脚本读出,并将参数传递到 NesC 预编译器。(2)平台导入程序 PlatformP/PlatformC, 通过调用两个初始化接口 platformC.Init 和 MainC.softwareInit来激活MCU、传感器和射频等硬件模块及相关软件。platformC.Init通常完成时钟校准和引脚设置,保证硬件在可操作状态。(3)与硬件具体功能相关的头文件“hardware.h”和将芯片与平台结合的具体代码。“hardware.h”  包含了其他硬件子系统的头文件,并被主文件“MainC.nc”所包含。(4)修改链接脚本文件,仿照其他平台文件在tos/platforms文件夹下建立tos.x文件,设置rom和ram的起始地址和大小。设置中断向量表、代码段、数据段、非初始化数据段和其他段的放置位置。(5)定义中断向量表,在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 程序的编译主要分两步[10], 首先调用ncc编译器把NesC预编译成C 文件(即预编译过程); 然后通过一个脚本程序,将经过NesC预编译生成的TinyOS应用程序转换成可以下载到开发板的hex格式[9]文件,最后送入 J-Flash ARM下载器下载到硬件。修改后的编译工具链如图3所示。
图3 TinyOS基于STM32的交*开发工具链
下面对这个过程进行详细的介绍。
第一步:源组件文件(.nc文件)的分析和转换,在这个阶段调用TinyOS自带的编译器 ncc, 对.nc源文件进行文法、语法分析, 检测共享数据缓冲区等。再根据各个组件和接口对其使用的函数和变量进行名字扩展, 使其具有全局唯一性。最后把所有的函数和变量都整合到一个主函数main( )中, 并生成相应的app.c文件。此时生成的已经是普通的C语言程序。以上这个替换过程是由 ncc 的核心程序 nesc1.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为输出格式转换器。
4 测试
TinyOS自带的radioCountToLeds测试程序,需用通用IO、Timer、Leds、SPI和无线模块(CC2520)驱动等组件。为了查看USART模块驱动也为了便于跟踪程序执行流程,在源程序中加入了串口输出代码,如图4所示,从图中可以看出TinyOS发送过程为:从应用层开始,逐层往下层封装数据,主要过程是在每一层添加相应层的包头,最后通过CC2520DriverLayerP组件中的send接口把数据通过CC2520模块发送出去,接收过程正好相反,在CC2520DriverLayerP组件中接收到数据后,逐层往上层传输数据,每一层除去相应的包头,再添加其他和协议相关匹配工作,比如:发送确认帧、把接收包源地址添加到本节点缓存等。最后把数据传到应用组件,注意:每一层向上层传输的是指向数据的指针,而不是数据本身。


图4 串口工具现实radioCountToLeds程序的执行过程
本实验通过一个节点每一秒定时广播数据包,定时器中断触发时发送一个16位的数据,并把此数加一。其他节点接收此数据,并根据接收数据的低三位是否为一来分别控制三个Led灯闪烁。通过观察节点上Led灯的有规律闪烁能够证明各模块成功移植。

5 结论
本文简单介绍了NesC语言的特点和TinyOS系统架构,重点介绍了TinyOS 的编译机制,并在此基础上详细说明了将 TinyOS内核程序移植到以STM32核处理器和CC2520无线模块芯片为核心的开发板上的具体方法。通过测试发现各个模块能够正常稳定工作。但无线模块协议栈还不够完善,功能还不全面,仍需要进一步的研究。


使用特权

评论回复
沙发
Orchids|  楼主 | 2019-10-5 14:38 | 只看该作者
基于STM32和CC2520的TinyOS移植与驱动分析

基于STM32和CC2520的TinyOS移植与驱动分析.pdf

520.17 KB

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

697

主题

993

帖子

4

粉丝