TA的每日心情 | 开心 前天 08:38 |
---|
签到天数: 273 天 [LV.8]以坛为家I
金牌会员
- 积分
- 1677
|
本帖最后由 小坤 于 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[UART3.Rx_counter - 2] == Modbus.CRC_H) && (UART3.Rx_buff[UART3.Rx_counter - 1] == Modbus.CRC_L)) //判断CRC校验是否正确
{
Modbus.address = UART3.Rx_buff[0]; //地址
if((Modbus.address == Sys_addr)||(Modbus.address == Broad_addr)) //判断地址
{
Modbus.CMD = UART3.Rx_buff[1]; //功能码
Modbus.Reg_addr = UART3.Rx_buff[2]*256+UART3.Rx_buff[3]; //寄存器地址
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. 读寄存器
void PC_ReadReg(void)
{
Buff_u16_u8 Buff;
uint8_t buff_n[255];
uint16_t i=0;
uint8_t n=0;
Modbus.Reg_Len = UART3.Rx_buff[4]*256+UART3.Rx_buff[5]; //寄存器长度
if((Modbus.Reg_Len<1)||(Modbus.Reg_Len>125)) return; //寄存器个数范围 1<=寄存器数量<=125
if(Modbus.Reg_addr+Modbus.Reg_Len > Reg_len) return; //起始地址+寄存器长度<=总寄存器数量
buff_n[n++] = Sys_addr ; //地址
buff_n[n++] = Modbus.CMD; //功能码
buff_n[n++] = Modbus.Reg_Len*2; //数据长度
for(i=Modbus.Reg_addr; i<(Modbus.Reg_addr+Modbus.Reg_Len); i++ )
{
Buff.Dat = Modbus.Reg;
buff_n[n++] = Buff.Byte[1];
buff_n[n++] = Buff.Byte[0]; //数据
}
Modbus.CRC_data = crc16( buff_n, n);
buff_n[n++] = Modbus.CRC_data>>8;
buff_n[n++] = Modbus.CRC_data&0xFF; //校验
UART3_Send_n(buff_n, n); //中断发送
}
5.写寄存器
void PC_WriteReg()
{
Buff_u16_u8 Buff;
uint8_t buff_n[10];
uint16_t i=0;
uint8_t n=0;
if( Modbus.CMD == 0X06 ) //写单个寄存器
{
if( Modbus.Reg_addr <= Reg_len) //判断地址是否合规
{
Buff.Byte[1] = UART3.Rx_buff[4];
Buff.Byte[0] = UART3.Rx_buff[5]; //获取数据
Modbus.Reg[Modbus.Reg_addr] = Buff.Dat; //寄存器赋值
}
}
if( Modbus.CMD == 0X10 ) //写多个寄存器
{
Modbus.Reg_Len = UART3.Rx_buff[4]*256+UART3.Rx_buff[5];
if((Modbus.Reg_Len<1)||(Modbus.Reg_Len>123)) return; //寄存器个数范围 1<=寄存器数量<=123
if(Modbus.Reg_Len*2!=UART3.Rx_buff[6]) 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[1] = UART3.Rx_buff[n+7];
n++;
Buff.Byte[0] = UART3.Rx_buff[n+7];;
n++;
Modbus.Reg = Buff.Dat; //寄存器赋值
}
}
for(n=0; n<6; n++)
{
buff_n[n] = UART3.Rx_buff[n]; //返回值
}
Modbus.CRC_data = crc16(buff_n, n);
buff_n[n++] = Modbus.CRC_data>>8;
buff_n[n++] = Modbus.CRC_data&0xFF; //校验
UART3_Send_n(buff_n, n); //发送
}
以上就是从机的读写机制;
结构体声明
/**************** Modbus定义 **************************************/
typedef struct
{
volatile uint8_t address; //地址
volatile uint8_t CMD; //功能码
volatile uint16_t Reg_addr; //寄存器地址
volatile uint16_t Reg_Len; //寄存器长度
// volatile uint16_t Read_Reg[Reg_len]; //读寄存器数组
// volatile uint16_t Write_Reg[Reg_len]; //写寄存器数组
volatile uint16_t Reg[Reg_len]; //写寄存器数组
volatile uint16_t CRC_data; //CRC校验
volatile uint8_t CRC_H; //CRC H
volatile uint8_t CRC_L; //CRC L
volatile uint16_t Err_code; //异常码
}Modbus_conf;
数据 共用体
/**************** 共用体 *****************************/
typedef union
{
unsigned int Dat;
unsigned char Byte[2];
}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[RX_Len];//接收缓冲区
uint8_t Rx_wr_index; //接收写指针
uint8_t Rx_rd_index; //接收读指针
uint8_t Rx_counter; //接收计数
//发送
uint8_t Tx_buff[TX_Len]; //发送缓冲区
uint8_t Tx_Length; //需发送的数据长度
uint8_t Tx_counter; //发送计数
}Typedef_COMx;
谢谢,勿喷,挣积分
|
|