396 lines
16 KiB
C
396 lines
16 KiB
C
#include "modbus_rtu_master.h"
|
||
|
||
MODBUS_MASTER mod_master;
|
||
|
||
void modbus_rtu_master_init(void)
|
||
{
|
||
mod_master.target_id = 0x00;
|
||
mod_master.command_code = 0x00;
|
||
mod_master.start_address = 0x0000;
|
||
mod_master.register_num = 0x0000;
|
||
mod_master.crc16 = 0x0000;
|
||
mod_master.byte_num = 0;
|
||
memset(mod_master.data_tx_value, 0, TX_DATA_MAX);
|
||
memset(mod_master.data_tx, 0, TX_BUFF_MAX);
|
||
memset(mod_master.data_rx, 0, RX_BUFF_MAX);
|
||
mod_master.data_tx_len = 0;
|
||
mod_master.data_rx_len = 0;
|
||
mod_master.rx_error_message = RX_ERROR_NONE;
|
||
mod_master.tx_error_message = TX_ERROR_NONE;
|
||
mod_master.timeout = 1000;
|
||
mod_master.rx_flag = RX_NONE;
|
||
mod_master.tx_flag = TX_NONE;
|
||
memset(mod_master.result_display, 0, sizeof(mod_master.result_display));
|
||
}
|
||
|
||
void modbus_rtu_master_load(void)
|
||
{
|
||
mod_master.data_tx[0] = mod_master.target_id;
|
||
mod_master.data_tx[1] = mod_master.command_code;
|
||
memset(mod_master.data_rx, 0, sizeof(mod_master.data_rx));
|
||
mod_master.tx_error_message = TX_ERROR_NONE;
|
||
|
||
switch (mod_master.data_tx[1])
|
||
{
|
||
//读四种寄存器的格式一致
|
||
case ReadCoilState:
|
||
{
|
||
//注释break,直接向下穿透
|
||
}
|
||
//break;
|
||
|
||
case ReadDisInputState:
|
||
{
|
||
//注释break,直接向下穿透
|
||
}
|
||
//break;
|
||
|
||
case ReadHoldReg:
|
||
{
|
||
//注释break,直接向下穿透
|
||
}
|
||
//break;
|
||
|
||
//【从站地址】【功能码】【起始地址高】【起始地址低】【数量高】【数量低】【CRC16校验低】【CRC16校验高】
|
||
case ReadInputReg:
|
||
{
|
||
mod_master.data_tx[2] = (uint8_t)(mod_master.start_address >> 8);
|
||
mod_master.data_tx[3] = (uint8_t)(mod_master.start_address & 0x00FF);
|
||
mod_master.data_tx[4] = (uint8_t)(mod_master.register_num >> 8);
|
||
mod_master.data_tx[5] = (uint8_t)(mod_master.register_num & 0x00FF);
|
||
mod_master.crc16 = ModbusCRC16(mod_master.data_tx, 6);
|
||
mod_master.data_tx[6] = (uint8_t)(mod_master.crc16 & 0x00FF);
|
||
mod_master.data_tx[7] = (uint8_t)(mod_master.crc16 >> 8);
|
||
|
||
mod_master.data_tx_len = 8;
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【地址高】【地址低】【标志位高】【标志位低】【CRC16校验低】【CRC16校验高】
|
||
//标志位0xFF00表示ON,0x0000表示OFF,其他所有值均视作无效
|
||
case WriteSingleCoil:
|
||
{
|
||
mod_master.data_tx[2] = (uint8_t)(mod_master.start_address >> 8);
|
||
mod_master.data_tx[3] = (uint8_t)(mod_master.start_address & 0x00FF);
|
||
mod_master.data_tx[4] = (mod_master.data_tx_value[0] == 0xFF)?(0xFF):(0x00);
|
||
mod_master.data_tx[5] = 0x00;
|
||
mod_master.crc16 = ModbusCRC16(mod_master.data_tx, 6);
|
||
mod_master.data_tx[6] = (uint8_t)(mod_master.crc16 & 0x00FF);
|
||
mod_master.data_tx[7] = (uint8_t)(mod_master.crc16 >> 8);
|
||
|
||
mod_master.data_tx_len = 8;
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【地址高】【地址低】【数据高】【数据低】【CRC16校验低】【CRC16校验高】
|
||
case WriteSingleReg:
|
||
{
|
||
mod_master.data_tx[2] = (uint8_t)(mod_master.start_address >> 8);
|
||
mod_master.data_tx[3] = (uint8_t)(mod_master.start_address & 0x00FF);
|
||
mod_master.data_tx[4] = mod_master.data_tx_value[0];
|
||
mod_master.data_tx[5] = mod_master.data_tx_value[1];
|
||
mod_master.crc16 = ModbusCRC16(mod_master.data_tx, 6);
|
||
mod_master.data_tx[6] = (uint8_t)(mod_master.crc16 & 0x00FF);
|
||
mod_master.data_tx[7] = (uint8_t)(mod_master.crc16 >> 8);
|
||
|
||
mod_master.data_tx_len = 8;
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【起始地址高】【起始地址低】【数量高】【数量低】【写入字节数n】【写入值1】...【写入值n】【CRC16校验低】【CRC16校验高】
|
||
case WriteMultiCoil:
|
||
{
|
||
if( mod_master.register_num % 8 )
|
||
{
|
||
//线圈数量不为8的倍数时,字节数 == (线圈数量/8 + 1)
|
||
if( (mod_master.register_num/8 + 1) != mod_master.byte_num )
|
||
{
|
||
mod_master.tx_error_message = TX_ERROR_COIL_NUM;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//线圈数量为8的倍数时,字节数 == 线圈数量/8
|
||
if( (mod_master.register_num/8) != mod_master.byte_num )
|
||
{
|
||
mod_master.tx_error_message = TX_ERROR_COIL_NUM;
|
||
return;
|
||
}
|
||
}
|
||
|
||
mod_master.data_tx[2] = (uint8_t)(mod_master.start_address >> 8);
|
||
mod_master.data_tx[3] = (uint8_t)(mod_master.start_address & 0x00FF);
|
||
mod_master.data_tx[4] = (uint8_t)(mod_master.register_num >> 8);
|
||
mod_master.data_tx[5] = (uint8_t)(mod_master.register_num & 0x00FF);
|
||
mod_master.data_tx[6] = mod_master.byte_num;
|
||
for(uint8_t i = 1; i <= mod_master.byte_num; i++ )
|
||
{
|
||
mod_master.data_tx[6 + i] = mod_master.data_tx_value[i - 1];
|
||
}
|
||
mod_master.crc16 = ModbusCRC16(mod_master.data_tx, 7 + mod_master.byte_num);
|
||
mod_master.data_tx[7 + mod_master.byte_num] = (uint8_t)(mod_master.crc16 & 0x00FF);
|
||
mod_master.data_tx[8 + mod_master.byte_num] = (uint8_t)(mod_master.crc16 >> 8);
|
||
|
||
mod_master.data_tx_len = 8 + mod_master.byte_num + 1;
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【起始地址高】【起始地址低】【数量高】【数量低】【写入字节数】【写入值1高】【写入值1低】...【写入值n高】【写入值n低】【CRC16校验低】【CRC16校验高】
|
||
case WriteMultiReg:
|
||
{
|
||
if( (mod_master.register_num * 2) != mod_master.byte_num )
|
||
{
|
||
//字节数 == (寄存器数量*2)
|
||
mod_master.tx_error_message = TX_ERROR_HOLDREG_NUM;
|
||
return;
|
||
}
|
||
|
||
mod_master.data_tx[2] = (uint8_t)(mod_master.start_address >> 8);
|
||
mod_master.data_tx[3] = (uint8_t)(mod_master.start_address & 0x00FF);
|
||
mod_master.data_tx[4] = (uint8_t)(mod_master.register_num >> 8);
|
||
mod_master.data_tx[5] = (uint8_t)(mod_master.register_num & 0x00FF);
|
||
mod_master.data_tx[6] = mod_master.byte_num;
|
||
for(uint8_t i = 1; i <= mod_master.byte_num; i++ )
|
||
{
|
||
mod_master.data_tx[6 + i] = mod_master.data_tx_value[i - 1];
|
||
}
|
||
mod_master.crc16 = ModbusCRC16(mod_master.data_tx, 7 + mod_master.byte_num);
|
||
mod_master.data_tx[7 + mod_master.byte_num] = (uint8_t)(mod_master.crc16 & 0x00FF);
|
||
mod_master.data_tx[8 + mod_master.byte_num] = (uint8_t)(mod_master.crc16 >> 8);
|
||
|
||
mod_master.data_tx_len = 8 + mod_master.byte_num + 1;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
{
|
||
mod_master.tx_error_message = TX_ERROR_WRONG_CMD;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
void modbus_rtu_master_send(void)
|
||
{
|
||
if(mod_master.tx_error_message == TX_ERROR_NONE)
|
||
{
|
||
mod_master.rx_flag = TX_WAITING;
|
||
|
||
//将准备好的数据发送至从设备
|
||
memcpy(scom2_rs485.tx_buff, mod_master.data_tx, mod_master.data_tx_len);
|
||
scom2_rs485.tx_len = mod_master.data_tx_len;
|
||
scom2_rs485.tx_flag = TRUE;
|
||
|
||
if(scom2_rs485.tx_flag == TRUE)
|
||
{
|
||
scom2_rs485.tx_flag = FALSE;
|
||
|
||
//将数据发送至上位机
|
||
HAL_UART_Transmit_DMA(&huart2, scom2_rs485.tx_buff, scom2_rs485.tx_len);
|
||
}
|
||
|
||
//在发送回调中判断发送是否完成
|
||
sig_trans = TRANS_MODBUS_SIG_TO_SLAVE;
|
||
}
|
||
}
|
||
|
||
void modbus_rtu_master_analysis(void)
|
||
{
|
||
//收到的数据是否溢出
|
||
if(scom2_rs485.rx_len > RX_BUFF_MAX)
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_OVERFLOW;
|
||
strcpy(mod_master.result_display, "ERROR: RX OVERFLOW");
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
mod_master.data_rx_len = scom2_rs485.rx_len;
|
||
memcpy(mod_master.data_rx, scom2_rs485.rx_buff, mod_master.data_rx_len);
|
||
}
|
||
|
||
//返回的从站地址与目标设备是否一致
|
||
if(mod_master.data_rx[0] != mod_master.target_id)
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_ID;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG ID");
|
||
return;
|
||
}
|
||
|
||
//收到的是异常响应
|
||
if(mod_master.data_rx[1] > 0x80)
|
||
{
|
||
ex_message = (eMBException)mod_master.data_rx[2];
|
||
sprintf(mod_master.result_display, "Exceptional Response: %02x", mod_master.data_rx[2]);
|
||
return;
|
||
}
|
||
|
||
//处理接收到的数据
|
||
modbus_rtu_master_data_process();
|
||
|
||
mod_master.rx_flag = RX_OK;
|
||
}
|
||
|
||
void modbus_rtu_master_data_process(void)
|
||
{
|
||
//获取功能码
|
||
uint8_t fun_cmd = mod_master.data_rx[1];
|
||
|
||
switch (fun_cmd)
|
||
{
|
||
//【从站地址】【功能码】【返回字节数n】【字节1~n】【CRC16校验低】【CRC16校验高】
|
||
case ReadCoilState:
|
||
{
|
||
if( mod_master.data_rx_len != (mod_master.data_rx[2] + 5) )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x Bytes:%02x Data: ", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2]);
|
||
char num2str[2];
|
||
for(uint8_t i = 0; i < mod_master.data_rx[2]; i++)
|
||
{
|
||
sprintf(num2str, "%02x ", mod_master.data_rx[3 + i]);
|
||
strcat(mod_master.result_display, num2str);
|
||
}
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【返回字节数n】【字节1~n】【CRC16校验低】【CRC16校验高】
|
||
case ReadDisInputState:
|
||
{
|
||
if( mod_master.data_rx_len != (mod_master.data_rx[2] + 5) )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x Bytes:%02x Data: ", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2]);
|
||
char num2str[2];
|
||
for(uint8_t i = 0; i < mod_master.data_rx[2]; i++)
|
||
{
|
||
sprintf(num2str, "%02x ", mod_master.data_rx[3 + i]);
|
||
strcat(mod_master.result_display, num2str);
|
||
}
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【返回字节数】【字1~n高】【字1~n低】【CRC16校验低】【CRC16校验高】
|
||
case ReadHoldReg:
|
||
{
|
||
if( mod_master.data_rx_len != (mod_master.data_rx[2] + 5) )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x Bytes:%02x Data: ", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2]);
|
||
char num2str[2];
|
||
for(uint8_t i = 0; i < mod_master.data_rx[2]; i++)
|
||
{
|
||
sprintf(num2str, "%02x ", mod_master.data_rx[3 + i]);
|
||
strcat(mod_master.result_display, num2str);
|
||
}
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【返回字节数】【字1~n高】【字1~n低】【CRC16校验低】【CRC16校验高】
|
||
case ReadInputReg:
|
||
{
|
||
if( mod_master.data_rx_len != (mod_master.data_rx[2] + 5) )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x Bytes:%02x Data: ", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2]);
|
||
char num2str[2];
|
||
for(uint8_t i = 0; i < mod_master.data_rx[2]; i++)
|
||
{
|
||
sprintf(num2str, "%02x ", mod_master.data_rx[3 + i]);
|
||
strcat(mod_master.result_display, num2str);
|
||
}
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【地址高】【地址低】【标志位高】【标志位低】【CRC16校验低】【CRC16校验高】
|
||
case WriteSingleCoil:
|
||
{
|
||
if( mod_master.data_rx_len != 8 )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x CoilAddress:0x%02x%02x Bits:0x%02x%02x", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2], mod_master.data_rx[3], mod_master.data_rx[4], mod_master.data_rx[5]);
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【地址高】【地址低】【数据高】【数据低】【CRC16校验低】【CRC16校验高】
|
||
case WriteSingleReg:
|
||
{
|
||
if( mod_master.data_rx_len != 8 )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x RegAddress:0x%02x%02x Data:0x%02x%02x", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2], mod_master.data_rx[3], mod_master.data_rx[4], mod_master.data_rx[5]);
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【地址高】【地址低】【数量高】【数量低】【CRC16校验低】【CRC16校验高】
|
||
case WriteMultiCoil:
|
||
{
|
||
if( mod_master.data_rx_len != 8 )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x CoilAddress:0x%02x%02x Num:0x%02x%02x", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2], mod_master.data_rx[3], mod_master.data_rx[4], mod_master.data_rx[5]);
|
||
}
|
||
break;
|
||
|
||
//【从站地址】【功能码】【地址高】【地址低】【数量高】【数量低】【CRC16校验低】【CRC16校验高】
|
||
case WriteMultiReg:
|
||
{
|
||
if( mod_master.data_rx_len != 8 )
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_LENGTH;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG LENGTH");
|
||
return;
|
||
}
|
||
|
||
//数据解析
|
||
sprintf(mod_master.result_display, "Address:0x%02x Command:0x%02x RegAddress:0x%02x%02x Num:0x%02x%02x", mod_master.data_rx[0], mod_master.data_rx[1], mod_master.data_rx[2], mod_master.data_rx[3], mod_master.data_rx[4], mod_master.data_rx[5]);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
{
|
||
mod_master.rx_error_message = RX_ERROR_WRONG_CMD;
|
||
strcpy(mod_master.result_display, "ERROR: WRONG COMMAND");
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
|