1.中断接收轮询函数
1.1.数据类型
uint8_t UART2_RxBuffer[20];//串口接收缓冲变量
uint8_t AddrFlag;//地址标志
uint8_t CodeFlag;//操作码标志
uint8_t CrcFlag;//校验标志
uint8_t SendFlag;//返回帧发送标志
uint8_t uart2_rx_flag = 0;//串口完全接收标志
下侧函数不公开仅提供思路
ModbusAddrJudge:地址判断
ModbusOptCodeJudge:操作码判断
ModbusRecvCrcJudge:CRC16校验判断
ModbusReturnBuilder:返回帧生成
1.2.详细函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart2){
uart2_rx_flag = 1;
}
}
void Uart2_RecvPoll(void)
{
static uint32_t tick=0;
uint32_t t=HAL_GetTick();
if(t - tick >= 10)
{
tick = t;
if(uart2_rx_flag)
{
uart2_rx_flag = 0;
HAL_UART_Receive_IT(&huart2, UART2_RxBuffer, 8);
AddrFlag = ModbusAddrJudge(UART2_RxBuffer);
CodeFlag = ModbusOptCodeJudge(UART2_RxBuffer);
CrcFlag = ModbusRecvCrcJudge(UART2_RxBuffer);
if(AddrFlag){
if(CodeFlag)
{
if(CrcFlag)
{
SendFlag = ModbusReturnBuilder(UART2_RxBuffer);
//可添加自定义函数
}
}
}
memset(ReturnOrder, 0, UART_RXBUFFER_SIZE);
memset(UART2_RxBuffer, 0, UART_RXBUFFER_SIZE);
AddrFlag = 0;
CodeFlag = 0;
CrcFlag = 0;
SendFlag = 0;
}
}
}
2.基于RS485的Modbus协议
RS485虽然历史有点久远,但是稳定性和便携性是No.1。
2.1.数据帧
操作码 | 功能描述 |
---|---|
0x03 | 读取数据 |
0x06 | 写入数据 |
- 读取数据帧(下面是读取四个继电器的状态):
地址码 | 操作码 | 起始高 | 起始低 | 数量高 | 数量低 | 校验高 | 校验低 |
---|---|---|---|---|---|---|---|
03 | 03 | 01 | 00 | 00 | 04 | 44 | 17 |
- 应答帧:
地址码 | 操作码 | 数据数 | 数据高 | 数据低 | 数据高 | 数据低 | 校验高 | 校验低 |
---|---|---|---|---|---|---|---|---|
03 | 03 | 04 | 00 | 00 | 01 | 00 | D8 | 63 |
3.写入数据帧(下面是写入从机地址):
地址码 | 操作码 | 地址高 | 地址低 | 数值高 | 数值低 | 校验高 | 校验低 |
---|---|---|---|---|---|---|---|
03 | 06 | 00 | 31 | 00 | 01 | 18 | 27 |
4.应答帧:
地址码 | 操作码 | 地址高 | 地址低 | 数值返回 | 校验高 | 校验低 |
---|---|---|---|---|---|---|
03 | 03 | 00 | 31 | 01 | B4 | 14 |
2.2.Modbus 收发逻辑
不同从机设备通过分配十六进制的地址码来区分设备
3.总结
说着简单,实际做要细心把逻辑干到位,继续加油。