MODBUS RTU数据解析,我只实现了功能码3,其他功能码请根据协议自行进行补充:
// 保持寄存器
#define REG_HOLD_SIZE 10UL
uint16_t REG_HOLD[REG_HOLD_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// 互换两个变量的值
void swap(unsigned char* byte1, unsigned char* byte2)
{
*byte1 ^= *byte2;
*byte2 ^= *byte1;
*byte1 ^= *byte2;
}
inline void modbus_slave_write(const void* buf, uint32_t len)
{
HAL_UART_Transmit(&huart3, (uint8_t *)buf, len, 1000);
}
/**
* 串口三数据接收处理
*/
void MODBUS_RecvHandler(void)
{
// 无数据收到
if ((MODBUS_RX_STA & 0x8000) == 0)
return;
// 收到的数据有误
if (MODBUS_RX_BUF[0] != SLAVE_ADDRESS)
goto __exit;
// 选择相应功能码
switch (MODBUS_RX_BUF[1])
{
case CMD3:
{
int i = 0;
int nread = 0;
int offset = 0;
int num = 0;
// CRC校验
if (usMBCRC16(MODBUS_RX_BUF, 6) != *((uint16_t *)(MODBUS_RX_BUF + 6)))
goto __exit;
// 大端转小端
swap(MODBUS_RX_BUF + 2, MODBUS_RX_BUF + 3);
swap(MODBUS_RX_BUF + 4, MODBUS_RX_BUF + 5);
// 得到保持寄存器偏移地址
offset = *((uint16_t *)(MODBUS_RX_BUF + 2));
// 偏移地址错误
if (offset >= REG_HOLD_SIZE) goto __exit;
// 得到要读取的保持寄存器个数
num = *((uint16_t *)(MODBUS_RX_BUF + 4));
// 计算实际可读的保持寄存器个数
nread = REG_HOLD_SIZE - offset >= num ? num : REG_HOLD_SIZE - offset;
// 实际能够被读取的保持寄存器数量错误
if (nread <= 0) goto __exit;
// 填充响应数据的字节长度
MODBUS_RX_BUF[2] = nread * sizeof(uint16_t);
// 填充保持寄存器的值
for (i = 0; i != nread; i++)
{
*((uint16_t *)(MODBUS_RX_BUF + 3 + i * 2)) = swap_uint16(REG_HOLD[offset + i]);
}
// 计算CRC
*((uint16_t *)(MODBUS_RX_BUF + 3 + nread * 2)) = usMBCRC16(MODBUS_RX_BUF, 3 + nread * 2);
// 发送给主机
modbus_slave_write(MODBUS_RX_BUF, 3 + nread * 2 + 2);
// 等待3.5个字符的时间
HAL_Delay(5);
// 改变保持寄存器的值
for (i = 0; i != REG_HOLD_SIZE; i++)
{
REG_HOLD[i]++;
}
// 退出
goto __exit;
}
default:
goto __exit;
}
__exit:
MODBUS_RX_STA = 0; // 清空接收
return;
}
|