从2021-2-9开始到2021-2-25该项目基本完成,写一篇笔记算是记录也是纪念把。
第二句话,要感谢一下@iroek,他DE了程序里的所有BUG。如果说为什么要第二句话,因为如果可以的话他喜欢低调。
先来一张全家福
虽然小型AUV的控制算法是由运算能力较强的RK3399芯片完成的,但是在收集传感器数据、编写传感器驱动方面,在运行Linux内核的芯片上写起来不是很方便,于是便有了标题中的基于STM32的消息中继器。
消息中继器主要完成的任务就像他的名字一样,完成消息中继。收集各个外设(传感器)的数据,然后以一定的封包格式通过一种特定的通信方式发送给RK3399。
目前该程序已经基本完成,后续添加设备只需要在现有程序框架下添加相应的代码。
硬件
- STM32F103C8T6核心板
- P30高度计 官方网站
- B30深度计 官方网站
软件
- STM32CubeIDE
- 串口调试助手
软件基本架构
软件采用HAL库为基础,通过Freertos进行任务调度,实现多个任务的调度。
目前主要有三个任务
- rosserial时间同步任务
- B30深度计任务
- P30高度计任务
下面主要根据上述任务对整个中继器的程序进行介绍
rosserial时间同步任务
该任务是最重要的一个任务,用来与RK3399上的rosserial节点进行时间同步,其依赖于rosserial库,进而其他任务才能将数据发送到rosmaster。在RK3399的ROS环境下可以通过较为简单的方式获取STM32上传感器所publish的数据。
下面主要说明一下该任务在源码中路径以及如何移植。
除了上述三个文件还有main.c里面涉及任务创建的内容以及ros_lib文件夹中的消息库。
可以在安装有ROS环境的机器(STM32未来要连接的机器)上运行下述命令来生成需要安装的库
rosrun rosserial_arduino make_libraries.py /home
cd home
目录下的ros_lib文件夹就为上图中的ros_lib文件夹。
如果要移植rosserial,说到底需要需要实现如下4个函数
int init()
- init函数主要用来执行硬件的初始化操作
uint32_t time()
- 获取时间戳单位为ms
int read()
- 读取一个字节
int write(uint8_t* data, uint16_t length)
- 写入一定长度的数据
由于使用HAL库,time直接可以由HAL_GetTick()
函数获取,read和write和使用的通信方式有关,在使用串口的情况下可以调用串口发送和接收相关的函数。
消息中继器采用的是,开启串口的接受DMA中断,在中断内将每一个字节复制进入环形缓冲区。read函数直接从环形缓冲区内读数据。具体代码可以在STM32ROSSerialDevice.h内找到。
深度计任务
深度计任务主要是通过硬件IIC读取深度计的深度数据,通过rosserial 将其数据发布到相应话题下。
深度计提供了一个官方例程是使用软件IIC的。深度计官方例程
除了图片上标注的三个文件还有main.c中和其相关的任务代码。
移植深度计库主要是使用HAL库函数
//阻塞
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
//非阻塞
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
在使用DMA中断接收数据要慎用HAL_I2C_Master_Receive
,该函数内部会关闭所有中断,会导致DMA丢失数据。
PS1:在使用HAL_I2C_Master_Transmit_IT
如果手动模拟错误拉低SDA线,会导致单片机宕机。
PS2:高度计库目前没有做硬件平台无关的处理,目前硬件和库还处于耦合状态,未来时间充足,会优化代码。
高度计
高度计任务通过配置P30设备到自动工作状态下,解析数据,将距离和置信度发布到相应话题下。
官方提供一个开源的测试程序ping-viewer 项目地址
消息中继器使用和其相同的数据解析库 ping-cpp 项目地址
移植P30需要实现
uint8_t init()
init函数主要用来执行硬件的初始化操作uint16_t readByte()
读取一个字节返回size_t write(uint8_t* data, uint16_t length)
写入一定数量的数据uint32_t get_tick()
获取时间戳,单位为ms
与移植rosserial相似,同样采用环形缓冲区储存数据,利用串口DMA中断接收数据。
PS:目前已知ping-parser存在可能数组越界BUG,该BUG会导致单片机宕机。
在数据部分丢失情况下,恰好包头完整,但后续PAYLOAD_LENGTH丢失,后续数据补位导致,PAYLOAD_LENGTH超过缓冲区大小,在接收数据时并没有做溢出检查,会导致数组越界。
项目Github地址
对于整个AUV控制系统相关的程序,都会在 https://github.com/UWVG开源。
该项目地址在 https://github.com/UWVG/AUV-Message-Relayer
项目里的commit都是原汁原味的,并没有最后统一改成一个,因为那些也是调试BUG的美好记忆,你说对吧。