本帖最后由 穿西装的强子 于 2025-6-20 23:11 编辑
根据上一个帖子已经搭建好了modbus传输的协议,只需添加0x06和0x10寄存器功能即可
以下是0x06寄存器,根据协议,如果协议正常,则返回值与发送值一样,因此使用memcpy将接收的usart数据复制到tx数据内
再将接收数据的寄存器值存在Modbus_data这个变量里做存储使用
void ModBus_Register_SingleWrite(USART_RxTx_TypeDef *usart,USART_RxTx_TypeDef *tx)
{
uint16_t register_addr = (usart->Buffer[2]<<8)|(usart->Buffer[3]);// 寄存器地址
if( register_addr >= 128)
{
} else {
tx->Length = usart->Length;
memcpy(&tx->Buffer[0],&usart->Buffer[0],usart->Length);
modbus_data[register_addr] = (usart->Buffer[4]<<8)|(usart->Buffer[5]);
tx->CompleteFlag = 1;
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
}
根据0x10寄存器,是写多个数据,按照协议,是有数据长度和寄存器个数的参数,理论上是按着寄存器数据连续的写入,而不是跳着写入这些数据,因此这些数据可以根据地址内容进行填充,正常接收的话,返回寄存器地址和寄存器个数即可。
根据以上协议,数据总长度= 寄存器个数*2+固定字节9,因此小于该长度的数据都属于异常数据。
由于数据类型是16位的,因此将接收的8位数据转换成16位数据进行存储,存储的数据可以根据自己的使用方法进行调用即可。
void ModBus_Register_MultiWrite(USART_RxTx_TypeDef *usart,USART_RxTx_TypeDef *tx)
{
uint16_t register_addr = (usart->Buffer[2]<<8)|(usart->Buffer[3]);// 寄存器地址
uint16_t register_len = (usart->Buffer[4]<<8)|(usart->Buffer[5]);// 寄存器数据长度
if( usart->Length < (9+register_len*2)){
} else {
tx->Length = 0;
tx->Buffer[tx->Length++] = DEVICES_ID;
tx->Buffer[tx->Length++] = 16;
tx->Buffer[tx->Length++] = usart->Buffer[2];
tx->Buffer[tx->Length++] = usart->Buffer[3];
tx->Buffer[tx->Length++] = usart->Buffer[4];
tx->Buffer[tx->Length++] = usart->Buffer[5];
uint16_t crc = calculate_crc_direct(tx->Buffer,tx->Length);
tx->Buffer[tx->Length++] = crc;
tx->Buffer[tx->Length++] = crc>>8;
for( uint16_t i = 0; i < register_len;i++)
{
modbus_data[register_addr+i] = (usart->Buffer[(i)*2+7]<<8)|(usart->Buffer[(i)*2+1+7]);
}
tx->CompleteFlag = 1;
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
}
最后,视频演示,该视频是在使用KEIL5进行debug阶段验证,可以看到左侧工程文件是MM32F0120的芯片
右侧是modbus寄存器数据,可以看到视频在使用modbus poll软件进行通信时,选择了16(0x10)寄存器,进行多数据写入功能,每次修改寄存器值时,keil的右侧debug界面modbus数据都会变化。
录屏没录好,只录了modbuspoll的,没把keil录进去
|