MODBUS通讯一般来说是基于RS485电平的通讯,RS485是半双工,很多单片机工程师做的通讯不是稳定,主要原因是流程没有掌控好。
我以前也犯过和他们一样错误,觉得很容易。在QQ群里,有位老工程师和大家一样犯了同样的毛病。曾经去一家公司面试,他们也遇到过类似的问题。他们咨询我用是什么485芯片,想知道一些信息。他们以为是自己的芯片问题,其实就是程序流程不正确。当然,这次面试肯定是泡汤,他们的动机不是招聘,而是想找答案。我告诉了芯片的型号,但具体流程设计,是口述的。面试时,当有人问解决方案时,就要注意留个心眼。
看似简单,那是因为你没有用到项目上去,只有做过的人才知道其中的奥妙。一次通讯正确,那不叫稳定,要能工作一年不出问题,才能说自己做好了。
1、变量定义如下:
#define RS485_TX_Overtime_RefValue 40 //发送完成最大等待时间为80ms,流程图直接用40
#define RS485_TX_Buffer_Size 17
u8 TX[RS485_TX_Buffer_Size];
u8 TXindex;//TX[]的发送下标值;
u8 Tmax;//TX[]的装载数据完成后的最大下标值
u8 TXstep;
u8 TXcnt;//RS485发送无从机超时计数器
#define Rmax 125
u8 RX[Rmax]; //RS485接收缓冲区数组
u8 RXindex; //RX[]的装载索引值
u8 RXcnt; //RS485接收时间计数器
u8 RXstep;
union RS485_CRC16_DATA_TYPE
{ uint8_t b[2];
uint16_t d; //b[1]和d的高8位值相等;b[0]和d的低8位值相等;
};
union RS485_CRC16_DATA_TYPE RS485_serial_crc;
uint8_t High8Bit,Low8Bit;//用来保存计算到的CRC
2、流程图如下:
照着上面的流程图设计,应该问题不大。
3、CRC循环冗长检测
//函数功能:CRC循环冗长检测
u16 FrequencyConverter_Crc16(u8 *data,u8 len)
{
u16 ccitt16 = 0xA001; //多项式0xA001
u16 crc = 0xFFFF;
int i;
for(;len>0;len--)
{
crc^=*data; //异或
for(i=0;i<8;i++)
{
if(crc & 0x0001) //最低位为1,减去除数
{
crc>>=1; //将最低位的1移出,剩下的数与0xA001异或
crc^=ccitt16;
}
else //最高位为0,不需要减去除数
{
crc>>=1; //直接移位
}
}
data++;
}
return crc;
}
4、装载举例
TX[0] = 0x01; //装载变频器设备地址
TX[1] = 0x03; //装载MODBUS读命令
TX[2] = RegAdress>>8; //装载寄存器地址的高8位值
TX[3] = RegAdress&0xFF; //装载寄存器地址的低8位值
TX[4] = DataOrNumber1>>8; //装载读取数据长度的高8位值
TX[5] = DataOrNumber1&0xFF; //装载读取数据长度的低8位值
crc16 = FrequencyConverter_Crc16(&TX[0],6); //计算CRC16的值
TX[6] = (u8)(crc16&0x00FF); //装载CRC16的低8位值
TX[7] = (u8)(crc16>>8); //装载CRC16的高8位值
Tmax = 7;//设置TX[]中的最大装载索引值
5、结论
本流程图,在实际案例中使用过,很不错。
欢迎交流,互相进步。