Modbus RTU 从机处理机制 跟风 赚点积分
本帖最后由 小坤 于 2024-4-15 09:28 编辑1. 首先就是基础的串口配置就不说 。
2. 接收超时机制根据实际的波特兰进行配置,3.5个字节~4个字节都是可以的。串口波特率可以修改的话,接收超时时间可以做成自动计算或者查表类。
3.接收解析:
void Modebus_PC_task(void)
{
if(UART3.Rx_OK) //接收完成
{
//关闭接收
Modbus.CRC_data = crc16( UART3.Rx_buff, (UART3.Rx_counter - 2)); //计算CRC校验值
Modbus.CRC_H = Modbus.CRC_data >> 8;
Modbus.CRC_L = Modbus.CRC_data & 0xFF; //拆分高低位
if((UART3.Rx_buff == Modbus.CRC_H) && (UART3.Rx_buff == Modbus.CRC_L))//判断CRC校验是否正确
{
Modbus.address = UART3.Rx_buff; //地址
if((Modbus.address == Sys_addr)||(Modbus.address == Broad_addr)) //判断地址
{
Modbus.CMD = UART3.Rx_buff; //功能码
Modbus.Reg_addr =UART3.Rx_buff*256+UART3.Rx_buff; //寄存器地址
switch(Modbus.CMD )
{
case 0x03 : PC_ReadReg(); break; //读寄存器
case 0x06:
case 0x10:PC_WriteReg(); break; //写寄存器
//default:Send_Err(Err_CMD); break; //异常功能码
}
}
}
// Delay_Ms(1);
memset(&UART3.Rx_buff, 0, sizeof(UART3.Rx_buff)); //内存清零
UART3.Rx_OK = 0; //状态赋值
UART3.Rx_counter = 0; //清零
// 打开接收
}
}
4. 读寄存器
voidPC_ReadReg(void)
{
Buff_u16_u8 Buff;
uint8_t buff_n;
uint16_t i=0;
uint8_t n=0;
Modbus.Reg_Len = UART3.Rx_buff*256+UART3.Rx_buff; //寄存器长度
if((Modbus.Reg_Len<1)||(Modbus.Reg_Len>125)) return; //寄存器个数范围 1<=寄存器数量<=125
if(Modbus.Reg_addr+Modbus.Reg_Len >Reg_len) return; //起始地址+寄存器长度<=总寄存器数量
buff_n = Sys_addr ; //地址
buff_n = Modbus.CMD; //功能码
buff_n = Modbus.Reg_Len*2; //数据长度
for(i=Modbus.Reg_addr; i<(Modbus.Reg_addr+Modbus.Reg_Len); i++ )
{
Buff.Dat = Modbus.Reg;
buff_n = Buff.Byte;
buff_n = Buff.Byte; //数据
}
Modbus.CRC_data = crc16( buff_n, n);
buff_n = Modbus.CRC_data>>8;
buff_n = Modbus.CRC_data&0xFF; //校验
UART3_Send_n(buff_n, n); //中断发送
}
5.写寄存器
void PC_WriteReg()
{
Buff_u16_u8 Buff;
uint8_t buff_n;
uint16_t i=0;
uint8_t n=0;
if( Modbus.CMD == 0X06 ) //写单个寄存器
{
if( Modbus.Reg_addr <=Reg_len) //判断地址是否合规
{
Buff.Byte = UART3.Rx_buff;
Buff.Byte = UART3.Rx_buff; //获取数据
Modbus.Reg = Buff.Dat; //寄存器赋值
}
}
if( Modbus.CMD == 0X10 ) //写多个寄存器
{
Modbus.Reg_Len = UART3.Rx_buff*256+UART3.Rx_buff;
if((Modbus.Reg_Len<1)||(Modbus.Reg_Len>123)) return; //寄存器个数范围 1<=寄存器数量<=123
if(Modbus.Reg_Len*2!=UART3.Rx_buff) return; //数据字节数据范围Modbus.Reg_Len*2
if(Modbus.Reg_addr+Modbus.Reg_Len >Reg_len) return; //起始地址+寄存器长度<=总寄存器数量
for(i=Modbus.Reg_addr; i<(Modbus.Reg_addr+Modbus.Reg_Len); i++ )
{
Buff.Byte = UART3.Rx_buff;
n++;
Buff.Byte = UART3.Rx_buff;;
n++;
Modbus.Reg = Buff.Dat; //寄存器赋值
}
}
for(n=0; n<6; n++)
{
buff_n = UART3.Rx_buff; //返回值
}
Modbus.CRC_data = crc16(buff_n, n);
buff_n = Modbus.CRC_data>>8;
buff_n = Modbus.CRC_data&0xFF; //校验
UART3_Send_n(buff_n, n); //发送
}
以上就是从机的读写机制;
结构体声明
/**************** Modbus定义 **************************************/
typedef struct
{
volatileuint8_t address; //地址
volatileuint8_t CMD; //功能码
volatileuint16_t Reg_addr; //寄存器地址
volatileuint16_t Reg_Len; //寄存器长度
// volatileuint16_t Read_Reg; //读寄存器数组
// volatileuint16_t Write_Reg; //写寄存器数组
volatileuint16_t Reg; //写寄存器数组
volatileuint16_t CRC_data; //CRC校验
volatileuint8_t CRC_H; //CRC H
volatileuint8_t CRC_L; //CRC L
volatileuint16_t Err_code; //异常码
}Modbus_conf;
数据 共用体
/**************** 共用体 *****************************/
typedef union
{
unsigned int Dat;
unsigned charByte;
}Buff_u16_u8;
寄存器枚举
enum MODBUS_REG
{
Start_Reg = 0,
Sys_addr_Reg, //设备地址
/**************** 1号***************************/
Status_1_Reg, //状态
Power_1_Reg, //功率大小
Mode_1_Reg, //模式
Time_1_Reg, //时间
Temp_1_Reg, //温度
/**************** 2号***************************/
Status_2_Reg, //状态
Power_2_Reg, //功率大小
Mode_2_Reg, //模式
Time_2_Reg, //时间
Temp_2_Reg, //温度
Reg_len //寄存器长度
};
可以参考的串口配置
typedef struct
{
uint8_t state; //状态
uint8_t RXDEnable; //接收使能
uint8_t TXDEnable; //接收使能
uint8_t TX_over_flag; //发送完成标识
uint8_t rx_buffer_overflow; //接收溢出:
char rx_over_time; //接收溢出时间
uint8_t rx_err; //校收出错
uint8_t Rx_OK; //校收完成
//接收
uint8_t Rx_buff;//接收缓冲区
uint8_t Rx_wr_index; //接收写指针
uint8_t Rx_rd_index; //接收读指针
uint8_t Rx_counter; //接收计数
//发送
uint8_t Tx_buff; //发送缓冲区
uint8_t Tx_Length; //需发送的数据长度
uint8_t Tx_counter; //发送计数
}Typedef_COMx;
谢谢,勿喷,挣积分{:smile:}
RTU? _奶咖君_ 发表于 2024-4-15 09:21
RTU?
是的,写错了,修正 ୧(๑•̀◡•́๑)૭ {:4_250:} 支持楼主,学习了!谢谢1 感谢分享
页:
[1]