终端驱动设计
从表面看,终端驱动就是MCU通过读写SX1278的寄存器,实现射频收发功能。然而,一个优秀的驱动设计,至少满足以下设计目标;最具挑战的是,有些目标是相互抵触的。
提供机制:区分策略和机制,驱动仅提供机制,由用户进程实现策略;
接口简单:接口越简单,驱动越好使用,另外,更好实现“高内聚、低耦合”;
提高效率:最大化硬件设备性能,是驱动的重要使命;
节能内存:内存复用和指针传递等方法可以节省MCU宝贵的内存;
易于移植:能在不同的MCU之间低成本移植,该驱动就越优异;
稳定可靠:驱动是硬件和系统软件的黏合层,不能有任何差错。
如图2-1所示,除contiki系统外,应用层也需要调用sx1278 driver,一个典型实例见图2,contiki协议栈依赖radio实现对应功能,应用层也需要设置radio参数和获得RSSI与SNR值。
图2-2 分层系统调用实例
MCU与LoRa硬件接口如图3-1所示:MCU通过SPI总线与LoRa进行通信,包括设置参数和读写FIFO;当LoRa有异步事件发生时,它通过6根连接线DIO0~DIO5中断MCU;MCU为判断接收和发送数据包是否超时需要设置TIMER,该资源LoRa不需要,仅被MCU所用。
图3-1 MCU与LoRa硬件接口
细化图3-1的设计可以得到图3-2所示驱动:DIO除初始化函数化,还需要DIO0~DIO5对应中断服务函数;SPI需要初始化和输入输出函数;TIMER需要初始化、启动和停止函数。
为方便移植LoRa到不同的MCU,特地将驱动设计成2部分:sx127x_src.c和sx127x_ports.c,前者为LoRa操作函数,后者提供图3-2所示接口。
图3-2 MCU需要提供的硬件驱动
Radio driver为尽可能好地实现功能,它提供如图3-3所示的回调函数接口,当事件(如接收数据包)发生时立即调用对应的方法。
图3-3 radio回调函数
radio一般都有自己的参数,LoRa也不例外,因此提供了图3-4所示的设参接口。
图3-4 LoRa参数设置
如图4-1所示,发送数据包不用另外开辟内存,直接从tx_process拷贝数据到LoRa FIFO之中。即使本次发送失败(如返回发送超时),也可以实现重传(FIFO和寄存器值仍得以保留)。
图4-1 发送数据包
为节省宝贵的内存,驱动程序通过图4-2的回调函数GetBufPtr()从用户进程获取缓存区指针,然后将接收数据包复制到该缓存;再执行回调函数RxDone(),让用户进程执行所需的逻辑(如:通知其他进程解析该数据包)。
图4-2 接收数据包
为了高效实现radiodriver,引入了中断和定时器机制来实现发送和接收数据包。因为LoRa是半双工通信,只需要使用1个定时器即可用于检测发送和接收是否超时;另外,根据LoRa芯片特性,当事件发生时会产生引脚的电平翻转信号,MCU需要捕捉该中断。
图5-1~图5-7显示了发送、接收和CAD时7种时序。
#1:停止定时器;
#2:使SX1278进行休眠状态,节省电能;
#3:执行回调函数TxDone(),让用户进程执行所需逻辑;
图5-1 发送成功
#1:使SX1278进行休眠状态,节省电能;
#2:执行回调函数TxTimeout (),让用户进程执行所需逻辑(定时器已经关闭);
图5-2 发送超时
#1:清除SX1278的中断标志位CRC_ERROR;
#2:关闭定时器;
#3:执行回调函数RxError(),让用户进程执行所需逻辑;
图5-3 接收错误数据包
#1:计算SNR(信噪比)和RSSI(接收信号场强值);
#2:从SX1278的硬件FIFO复制数据包到用户进程的RAM缓冲区;
#3:关闭定时器;
#4:执行回调函数RxDone(),让用户进程执行所需逻辑;
图5-4 接收正确数据包
#1:执行回调函数RxTimeout (),让用户进程执行所需逻辑(定时器已经关闭);
图5-5 接收超时
#1:关闭定时器;
#2:获取CAD侦听结果(信道干净与否);
#3:使SX1278进行休眠状态,节省电能;
#4:执行回调函数CADDone(),让用户进程执行所需逻辑;
图5-6 CAD侦听结束
#1:使SX1278进行休眠状态,节省电能;
#2:执行回调函数CADTimeout (),让用户进程执行所需逻辑(定时器已经关闭);
图5-7 CAD超时