STM32 在串口通信时简单模仿运用MODBUS协议

最近一个项目用到了MODBUS协议 , 就学习了一下 , 这里做一下记录以免后续忘记 。
要用到MODBUS肯定要先知道是MOBUS协议 , 这里呢我们就又要先理解协议的含义了 。
所谓的协议是什么?就是互相之间的约定嘛,如果不让别人知道那就是暗号 。 现在就来定义一个新的最简单协议 。
例如:协议:“A”--“LED灭”“B”--“报警”“C”--“LED亮”。
单片机接收到“A”控制一个LED灭 , 单片机接收到“B”控制报警 , 单片机接收到“A”控制一个LED亮 。 那么当收到对应的信息就执行相应的动作 , 这就是协议 。
MODBUS 一包数据主要组成有 :设备地址 功能码数据长度数据CRC
先来简单分析一条MODBUS-RTU报文 , 例如:
STM32 在串口通信时简单模仿运用MODBUS协议文章插图
这个包数据的意思是往设备地址为 0x01 的设备中 , 执行 0x1f 代号的功能, 这里假设 0x1f 代号功能码代表的是保存数据再Flash , 那这包数据的意思就是 , 控制 0x01 设备 , 执行 0x1f 代号功能保存后面4位数据到 MCU 的 Flash 中 , 其中数据长度是指后面数据字节数的大小 , 最后两个 CRC 校验是除了最后两位 CRC 前面所有数据经过CRC运算出来的校验码 , 用来保证数据的准确性 。
【STM32 在串口通信时简单模仿运用MODBUS协议】协议大概就是这么些内容 , 因为只是简单的应用 , 就没有深入去研究了 , 知道这些运用到STM32上已经够了 , 下面的是CRC校验的代码(重点):
/*** 函数名:uint16_t Crc(uint8_t Rxbuff[],uint8_t Rx_len) 说明:Modbus协议CRC校验 传入值:Rxbuff[] 串口接收的数据 , len串口接收的数据长度 传出值:返回两个字节的CRC校验码 , 高位在前 , 低位在后**/uint16_t Crc(uint8_t Rxbuff[],uint8_t Rx_len){uint8_t len = Rx_len - 2;uint16_t crc_result = 0xffff;int crc_num =0;int xor_flag=0;for(int i=0;i>= 1;if(xor_flag)crc_result ^= 0xa001;crc_num = (crc_result}}return crc_result;}下面的是MODBUS运用的举例代码 , 这里我对MODBUS协议做了些修改 , 在 “功能码 ” 跟 “数据长度 ” 间多加了个 “读写标志位” , 实际项目时可以根据自己项目需求做一下修改也无可厚非:
/*** 函数名:void modbus(uint8_t Rxbuff[],uint8_t len) 说明:Modbus协议处理 传入值:Rxbuff[] 串口接收的数据 , len串口接收的数据长度 传出值:无**/void modbus(uint8_t Rxbuff[],uint8_t len){uint8_t Read_Robot [1]={0};uint8_t Robot_add=0x01;uint8_t Robot = Get_Robot_Num();//获取预先保存好的设备地址uint16_t crc=Crc(Rxbuff,len);uint16_t Rx_crc = Rxbuff[len-2]<<8 | Rxbuff[len-1];if((Robot==Rxbuff[0] ||Rxbuff[0] ==0xff)else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,ID_Flash_Add);break;case 0x02: //操作名称if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Name_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Name_Flash_Add);break;case 0x03: //操作环节if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Link_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Link_Flash_Add);break;case 0x04: //操作型号if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Model_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Model_Flash_Add);break;case 0x05: //操作品牌if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Brand_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Brand_Flash_Add);break;case 0x06: //操作行数if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Linage_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Linage_Flash_Add);break;case 0x07: //操作幅宽if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Wite_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Wite_Flash_Add);break;case 0x08: //操作高度if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Hight_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Hight_Flash_Add);break;case 0x09: //操作马力if(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Soup_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Soup_Flash_Add);break;case 0x0a: //操作合作社IDif(Rxbuff[2]==0x00) //读Read(MCU_Rx_buff,Artel_Flash_Add);else if(Rxbuff[2]==0x01) //写Write(Rxbuff,len,Artel_Flash_Add);break;case 0x21: //修改波特率if(Rxbuff[2]==0x00) //读{Get_Boud_num();}else if(Rxbuff[2]==0x01) //写{Boud_Set(Rxbuff);HAL_UART_Transmit(}break;case 0x22: //操作设备地址if(Rxbuff[2]==0x00) //读{Read_Robot[0] = Get_Robot_Num();HAL_UART_Transmit(}else if(Rxbuff[2]==0x01) //写{Robot_add=Rxbuff[5];Robot_Num_Init(Robot_add);HAL_UART_Transmit(}break;}}}


推荐阅读