跳转至

通信协议

更新日期:2026/2/1

参与者:KkarinL15

在项目文件夹中创建Modules,在User文件夹中创建Utils,如图放置通信协议所需文件

数据发送

调用message_register_send_uart注册串口消息通讯句柄;

调用message_send_data函数发送数据(检查数据是否为空、消息ID是否正确、消息队列是否为空 -> 将消息队列中ID对应的数据赋给中间变量msg进行下一步拆分解析数据 -> 检查发送串口和该串口的发送缓存区是否为空 -> 对比缓存区与发送数据的长度大小,对缓存区的大小进行扩缩到合适大小 -> 拆分数据存入缓存区 -> 检查发送串口的TxDMA句柄是否为空并发送数据)。

扩缩缓存区

  • 不够(缓存区长度 <= 数据长度+5):将缓存区的长度扩为当前数据的两倍。
  • 有余((数据长度+3)*3 <= 缓存区长度):将缓存区的长度缩到原来的1/2。

数据拆分

缓存区数组(uint8_t类型)第一字节高四位标记数据ID,低四位标记数据类型;第二字节标记数据长度;第三字节开始将数据复制到字节流;最后一个字节用帧结束标志MSG_EOF结尾。

发送数据

  • 不为空:用DMA搬运并转发缓存区数据。
  • 为空:直接调用HAL_UART_Transmit函数发送缓存区数据。

数据接收

调用message_register_polling_uart函数注册数据接收串口,在函数里对消息队列初始化;

调用message_register_recv_callback函数注册接收回调函数指针,绑定消息ID与其对应回调函数。

调用message_polling_data函数轮询消息队列读取数据(依次读取消息队列中的数据,检查数据、接收串口、接收缓冲区是否为空 -> 消息数据出队 -> 读取串口接收缓冲区接收到的数据长度并检查是否为空 -> 消息数据入队)。

队列

环形缓冲区,注册数据接口时传入的队列大小参数必须为2的幂次方。代码里用位掩码替代取模来做索引环绕:msg_fifo_initmask = fifo_size - 1,然后在读写时用 index & mask 访问缓冲区(例如 fifo->buf[fifo->tail & fifo->mask])。只有当 fifo_size 是 2 的幂时,fifo_size-1 的二进制是低位全 1,index & mask 等价于 index % fifo_size,才能正确、快速地做环绕索引。若不是 2 的幂,& mask 不再等同于取模,会导致索引错误和数据混乱。

回调函数

回调函数参数形式必须是void func(uint32_t, uint8_t, uint8_t*)第一个参数是消息长度, 第二个参数是消息标识 (高四位是 ID, 低四位是数据类型),第三个参数是数据区内容, 无返回值。

消息数据入队

把从串口读到的字节写入环形 FIFO,遇到 MSG_EOF 完成一帧并在帧前预留的位置写入该帧在 FIFO 中的总长度。

消息数据出队

FIFO按帧读取数据,校验帧长度(和 CRC8 可选),把完整帧拷贝到 recv_buf(处理跨界情形),并在校验通过时调用注册的回调 msg->recv_callback对消息ID调用相应回调函数。

各项辅助功能

  • 启用线程安全处理

    启用后会使用互斥信号量来保护发送缓冲区(仅支持FreeRTOS)。

  • 启用转义标识

    为了做到透传(即原样、完整地将数据从发送端转发至接收端),消息会对内容转义。定义MSG_ESC可以选择转义字符(建议选择出现频次低的字节)。

  • 启用始能统计

    可以启用MSG_ENABLE_STATISTICS宏定义来统计每种消息的接收情况(接收成功、错误计数,内存分配失败计数,队列长度与最大深度等)。

  • 启用CRC校验

    传入数据和数据长度。(需接收方与发送方同时开启,否则数据解析时数据会被误判为脏数据)

    CRC8CRC16的区别在于校验长度有区别和返回的校验值位数不同;CRC8的校验码为1byteCRC16的校验码为2byte