短文标题:3.5T静默分帧,CRC16校验:Modbus-RTU帧结构解析
你有没有想过一个问题:Modbus-RTU数据帧没有固定的帧头,接收方怎么知道一帧从哪里开始、到哪里结束?靠“沉默”来分帧。协议规定:两个字节之间的间隔超过3.5个字符时间,就认为上一帧结束,下一帧开始。
3.5T(3.5字符时间)怎么算?以9600bps、8N1(10位/字符,1起始+8数据+1停止)为例:
- 1位时间 = 1/9600 ≈ 104µs
- 1字符时间 = 10 × 104µs ≈ 1.04ms
- 3.5T = 3.5 × 1.04ms ≈ 3.64ms
IDLE中断检测帧结束,USART的IDLE标志在总线空闲超过1字符时间时置1,可配合软件定时器测量更精确的3.5T,或直接用IDLE中断触发帧结束标志(工程实践中通常足够)。
void USART2_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
// 帧接收完成
frame_received = 1;
}
}
Modbus-RTU帧结构
- 地址:从机地址(1~247),0为广播地址
- 功能码:03读保持寄存器、06写单个寄存器、05写线圈
- 数据:寄存器地址、数量、写入值
- CRC16:从地址到数据区末尾,低字节在前
常用功能码
DMA+IDLE接收变长帧
// 配置DMA接收
HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE);
// IDLE中断中处理
void USART2_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
uint16_t len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart2.hdmarx);
// len就是这一帧的长度,处理数据
HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE);
}
}
这个故事的启示,Modbus-RTU没有帧头,用“静默”分帧——3.5T就是它的“句号”。CRC16保数据完整,一个bit错都不同。学会解析帧结构,Modbus通信不再神秘。写在最后,Modbus-RTU帧结构就四部分:地址区分设备,功能码定操作,数据传内容,CRC保安全。
(本文灵感源于于振南《新概念ARM32单片机》教程第4.16节、第4.17节。)
觉得有用?点赞、转发,让更多人看懂Modbus-RTU的帧结构。