modbus.c
#include "modbus.h"
#include "crc16.h"
#include "uart2.h"
#define HoldRegStartAddr 0x0000 //保持寄存器起始地址
#define HoldRegCount 8 //保持寄存器数量
#define HoldMaxValue 1000 //寄存器最大值
extern u8 data_backup[DMA_REC_LEN]; //modbus接收到数据备份
extern u16 dataLen_backup; //modbus接收数据长度备份
u8 SendBuf[DMA_REC_LEN] = {0};
u16 StartRegAddr = HoldRegStartAddr;
u8 err = 0; //错误代码
u8 HoldReg[HoldRegCount*2] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8}; //存储8路数据值
/*
错误代码说明:
0x01 不是支持的功能码
0x02 起始地址不在规定范围内
0x03 寄存器数量不在规定范围内
0x04 数据校验错误
*/
//处理接收到的数据
// 接收: [地址][功能码][起始地址高][起始地址低][总寄存器数高][总寄存器数低][CRC低][CRC高]
void DisposeReceive( void )
{
u16 CRC16 = 0, CRC16Temp = 0;
if( data_backup[0] == SlaveID ) //地址等于本机地址 地址范围:1 - 32
{
CRC16 = App_Tab_Get_CRC16( data_backup, dataLen_backup - 2 ); //CRC校验 低字节在前 高字节在后 高字节为报文最后一个字节
CRC16Temp = ( ( u16 )( data_backup[dataLen_backup - 1] << 8 ) | data_backup[dataLen_backup - 2] );
if( CRC16 != CRC16Temp )
{
err = 4; //CRC校验错误
}
StartRegAddr = ( u16 )( data_backup[2] << 8 ) | data_backup[3];
if( StartRegAddr > (HoldRegStartAddr + HoldRegCount - 1) )
{
err = 2; //起始地址不在规定范围内 00 - 07 1 - 8号通道
}
if( err == 0 )
{
switch( data_backup[1] ) //功能码
{
case 3: //读多个寄存器
{
Modbus_03_Slave();
break;
}
case 6: //写单个寄存器
{
Modbus_06_Slave();
break;
}
case 16: //写多个寄存器
{
Modbus_16_Slave();
break;
}
default:
{
err = 1; //不支持该功能码
break;
}
}
}
if( err > 0 )
{
SendBuf[0] = data_backup[0];
SendBuf[1] = data_backup[1] | 0x80;
SendBuf[2] = err; //发送错误代码
CRC16Temp = App_Tab_Get_CRC16( SendBuf, 3 ); //计算CRC校验值
SendBuf[3] = CRC16Temp & 0xFF; //CRC低位
SendBuf[4] = ( CRC16Temp >> 8 ); //CRC高位
uart2_Send( SendBuf, 5 );
err = 0; //发送完数据后清除错误标志
}
}
}
/*
函数功能:读保持寄存器 03
主站请求报文: 0x01 0x03 0x0000 0x0001 0x840A 读从0开始的1个保持寄存器
从站正常响应报文: 0x01 0x03 0x02 0x09C4 0xBF87 读到的2字节数据为 0x09C4
*/
void Modbus_03_Slave( void )
{
u16 RegNum = 0;
u16 CRC16Temp = 0;
u8 i = 0;
RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5]; //获取寄存器数量
if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) ) //寄存器地址+寄存器数量 在规定范围内 <=8
{
SendBuf[0] = data_backup[0]; //地址
SendBuf[1] = data_backup[1]; //功能码
SendBuf[2] = RegNum * 2; //返回字节数量
for( i = 0; i < RegNum; i++ ) //循环读取保持寄存器内的值
{
SendBuf[3 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2];
SendBuf[4 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2 + 1];
}
CRC16Temp = App_Tab_Get_CRC16( SendBuf, RegNum * 2 + 3 ); //获取CRC校验值
SendBuf[RegNum * 2 + 3] = CRC16Temp & 0xFF; //CRC低位
SendBuf[RegNum * 2 + 4] = ( CRC16Temp >> 8 ); //CRC高位
uart2_Send( SendBuf, RegNum * 2 + 5 );
}
else
{
err = 3; //寄存器数量不在规定范围内
}
}
/*
函数功能:写单个保持寄存器 06
主站请求报文: 0x01 0x06 0x0000 0x1388 0x849C 写0号寄存器的值为0x1388
从站正常响应报文: 0x01 0x06 0x0000 0x1388 0x849C 0号寄存器的值为0x1388
*/
void Modbus_06_Slave( void )
{
u16 RegValue = 0;
u16 CRC16Temp = 0;
RegValue = ( u16 )( data_backup[4] << 8 ) | data_backup[5]; //获取寄存器值
if( RegValue <= HoldMaxValue ) //寄存器值不超过1000
{
HoldReg[StartRegAddr * 2] = data_backup[4]; //存储寄存器值
HoldReg[StartRegAddr * 2 + 1] = data_backup[5];
SendBuf[0] = data_backup[0]; //地址
SendBuf[1] = data_backup[1]; //功能码
SendBuf[2] = data_backup[2]; //地址高位
SendBuf[3] = data_backup[3]; //地址低位
SendBuf[4] = data_backup[4]; //值高位
SendBuf[5] = data_backup[5]; //值低位
CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 ); //获取CRC校验值
SendBuf[6] = CRC16Temp & 0xFF; //CRC低位
SendBuf[7] = ( CRC16Temp >> 8 ); //CRC高位
uart2_Send( SendBuf, 8 );
}
else
{
err = 3; //寄存器数值不在规定范围内
}
}
/*
函数功能:写多个连续保持寄存器值 16
主站请求报文: 0x01 0x10 0x7540 0x0002 0x04 0x0000 0x2710 0xB731 写从0x7540地址开始的2个保持寄存器值 共4字节
从站正常响应报文: 0x01 0x10 0x7540 0x0002 0x5A10 写从0x7540地址开始的2个保持寄存器值
*/
void Modbus_16_Slave( void )
{
u16 RegNum = 0;
u16 CRC16Temp = 0;
u8 i = 0;
RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5]; //获取寄存器数量
if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) ) //寄存器地址+寄存器数量 在规定范围内 <=8
{
for( i = 0; i < RegNum; i++ ) //存储寄存器设置值
{
HoldReg[StartRegAddr * 2 + i * 2] = data_backup[i * 2 + 7];
HoldReg[StartRegAddr * 2 + 1 + i * 2] = data_backup[i * 2 + 8];
}
SendBuf[0] = data_backup[0]; //起始地址
SendBuf[1] = data_backup[1]; //功能码
SendBuf[2] = data_backup[2]; //地址高位
SendBuf[3] = data_backup[3]; //地址低位
SendBuf[4] = data_backup[4]; //寄存器数量高位
SendBuf[5] = data_backup[5]; //寄存器数量低位
CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 ); //获取CRC校验值
SendBuf[6] = CRC16Temp & 0xFF; //CRC低位
SendBuf[7] = ( CRC16Temp >> 8 ); //CRC高位
uart2_Send( SendBuf, 8 );
}
else
{
err = 3; //寄存器数量不在规定范围内
}
}
|