|
发表于 2014-5-22 17:11:55
|
显示全部楼层
昨天自己写的个modbus的从机,支持03 06 10功能号,刚测试了一天,通信正常,暂时没发现bug,感觉freemodbus功能比较全面,但是处理机制比较繁琐。其实,只要帧识别出来了,解析就相对容易了。我的硬件平台是STM32+232,识别帧的思路是这样的: 关发送中断,开接收中断,开辟一个接收区,接收到数据就存入缓存,然后单片机空闲时就检测缓存,发现有数据进来,等待N个ms后(不是采用延时的方式,等待的原因,是因为有这种情况,MCU刚接收到第一个字节,就开始处理数据,处理第二个字节的数据时,实际上第二个字节还未进入接收区,即接收数据的速度跟不上处理数据的速度),对数据进行处理,由于没有采用3.5、1.5个字节帧识别的方式,处理数据时,收到的是两帧数据,或者更多,只解析第一个完整帧,其他的数据丢掉,对于03 06 10功能号,帧的长度是能够解析出来,对于其他的功能号应该也可以吧,帧识别出来后,解析就容易了。 感觉这种识别帧的方式还是不妥,欢迎各位拍砖,提出更好的方案,发几段相关的代码
void main(void)
{
bsp_ModbusRTUSlave_Init();
bsp_InitTimer();
LED_Init();
bsp_StartTimer(1,10);
bsp_StartTimer(2,500);
for(;;)
{
bsp_ModbusRTUSlave_decode();
if(bsp_CheckTimer(2) == 1)
{
bsp_StartTimer(2,500);
GPIOB->ODR ^=GPIO_Pin_9;
}
}
}
void USART2_ISR(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
/* Read one byte from the receive data register */
usart.data[usart.write] = USART_ReceiveData(USART2);
usart.write++;
if(usart.write==USART_DATA_SIZE) usart.write = USART_DATA_SIZE - 1; //防止缓冲区溢出出错
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
}
void bsp_ModbusRTUSlave_decode(void)
{
uint16_t dat_buff;
if(usart.write > 0 && usart.Rx_delay_CNT > 200)//缓冲区接收到数据 延时200ms再处理
{
usart.slaveID = usart.data[0];
usart.function = usart.data[1];
usart.exceptionCode = 0x00; // 错误类型清零
switch(usart.function)
{
//case 0x01:ReadCoils_Handle();break; //Read Coils 这种"位"是可读写的,在PLC中为可读写的线圈,如数字量输出、中间继电器等
//case 0x02:ReadDiscreteInputs_Handle();break; //Read Discrete Inputs 这种"位"是只读的,不能进行写操作,在PLC中为只读的线圈,如数字量输入
case 0x03:ReadHoldingRegisters_Handle();break; //Read Holding Registers 这种寄存器可读写 0x0001 <= Quantity of Registers <= 0x007D
//case 0x04:ReadInputRegisters_Handle();break; //Read Input Registers 这种寄存器只能读,不能写
//case 0x05:WriteSingleCoil_Handle();break; //Write Single Coil
case 0x06:WriteSingleRegister_Handle();break; //Write Single Register
//case 0x0F:WriteMultipleCoils_Handle();break; //Write Multiple Coils
case 0x10:WriteMultipleregisters_Handle();break; //Write Multiple registers 0x0001 <= Quantity of Registers <= 0x007B
default : break; //usart.exceptionCode = 0x01;注释掉了,用不着,而且还会有误判的情况,错误类型:功能号不支持
}
if(usart.exceptionCode != 0 && usart.slaveID == SLAVE_ID)
{
usart.send[0] = SLAVE_ID;
usart.send[1] = usart.function | 0x80;//function + 0x80
usart.send[2] = usart.exceptionCode;
dat_buff = ModbusRTU_CRC(usart.send,3);
usart.send[3] = (uint8_t)(dat_buff & 0x00FF);
usart.send[4] = (uint8_t)(dat_buff >> 8);
bsp_ModbusRTUSlave_Tx(usart.send, 5); //发送相应的消息给主机
}
UART_clear_buf();
}
}
static void WriteMultipleregisters_Handle(void)
{
uint8_t i;
uint16_t dat_CRC,dat_Address,dat_Quantity;
dat_CRC = ModbusRTU_CRC(usart.data, usart.data[6] + 7); // 检验前N个字节
//CRC添加到消息中时,低字节先加入,然后高字节。
if(dat_CRC == (uint16_t)(usart.data[usart.data[6] + 8] << 8 | usart.data[usart.data[6] + 7])) usart.CRCerror = 0; //CRC校验正确
else usart.CRCerror = 1; //CRC校验出错
if(usart.slaveID == SLAVE_ID && usart.CRCerror == 0)//从机地址正确 CRC校验正确 接着往下处理
{
dat_Quantity = (uint16_t)(usart.data[4] << 8 | usart.data[5]); //Quantity of Outputs 在消息中 高位在前 低位在后
if(dat_Quantity >= 0x0001 && dat_Quantity <= 0x007B && usart.data[6] == (dat_Quantity * 2)) //0x0001 <= Quantity of Outputs <= 0x007B
{
dat_Address = (uint16_t)(usart.data[2] << 8 | usart.data[3]); //Address在消息中 高位在前 低位在后
if((dat_Address + dat_Quantity) <= HOLDINGREG_DATA_SIZE) //Address 要在范围之内
{
for(i = 0; i < dat_Quantity; i++) //写入相应的寄存器的值
{
HoldingReg_data[dat_Address + i] = (uint16_t)(usart.data[i * 2 + 7] << 8 | usart.data[i * 2 + 8]);
}
for(i = 0; i < 6; i++) usart.send[i] = usart.data[i]; //将接收到的消息的前一部分+CRC反馈给主机
dat_CRC = ModbusRTU_CRC(usart.send,6);
usart.send[6] = (uint8_t)(dat_CRC & 0x00FF);
usart.send[7] = (uint8_t)(dat_CRC >> 8);
bsp_ModbusRTUSlave_Tx(usart.send, 8); //发送相应的消息给主机
}
else usart.exceptionCode = 0x02; //错误类型:Address 不在范围之内
}
else usart.exceptionCode = 0x03; //错误类型:Quantity of Outputs 不在范围之内
}
}
|
|