参考链接:Modbus协议解析--小白一看就懂的协议_“社会大学三年级”的博客-优快云博客_modbus协议详解
水平有限,仅供参考
//
//modbus_rtu
//地址(1字节)+功能码(1字节)+寄存器地址(2字节)+数据()+校验(2字节)
//
//
typedef volatile unsigned int WORD;//16位
typedef volatile unsigned char BYTE;//8位
typedef volatile unsigned char bit;//8位或1位
#define CRC_CHECK_NUM (0xA001)
#define MODBUS_RTU_ADDR (0X01)//当前设备的地址
#define REGISTER_NUM 20//寄存器个数
#define REGISTER_START_INDEX (0X0000)//寄存器的开始地址,为什么有这个参数?因为有的协议不是从寄存器0X0000开始的,所以有个开始寄存器的地址
WORD registerData[REGISTER_NUM];//寄存器里的数据
BYTE slaveStep;
#define RX_ARR_LENGTH 30
BYTE rxIndexLow;
BYTE rxIndexHigh;
BYTE rxBuf[RX_ARR_LENGTH];
#define TX_ARR_LENGTH 50
BYTE txIndexLow;
BYTE txIndexHigh;
BYTE txBuf[TX_ARR_LENGTH];
BYTE tempArr[TX_ARR_LENGTH];//临时用来求校验的中间数据
#define RX_DEAL_ARR_LENGTH 50
BYTE rxDealLength;//需要接收的长度
BYTE rx0X10FourByte;//0X10指令接收四个字节
BYTE rxDealIndex;
BYTE rxDealBuf[RX_DEAL_ARR_LENGTH];
WORD crc_modbusrtu(BYTE *ptr,BYTE len);
void tx_buf_load(BYTE dat);
BYTE comNoDataTimeCnt;//无数据计时
void uart_485_data_deal()//每2mS执行一次,这里可以自己调整
{
BYTE i,j,num;
WORD crc,startIndex,dat;
i=0;
while(i<4)//一次最多处理四个字节,这里可以自己调整一次多处理几个字节
{
i++;
if(rxIndexLow != rxIndexHigh)
{
comNoDataTimeCnt=0;//清接收错误计时
dat = rxBuf[rxIndexLow];
if(++rxIndexLow >= RX_ARR_LENGTH)rxIndexLow=0;
switch(slaveStep)
{
case 0://接收设备地址
if(dat == MODBUS_RTU_ADDR)
{
rxDealIndex = 0;
rxDealBuf[rxDealIndex++] = dat;
slaveStep = 1;
}
break;
case 1://接收指令
if(dat == 0X03 || dat == 0X06)//读指令、查询单个指令
{
rxDealBuf[rxDealIndex++] = dat;
rxDealLength = 8;//长度都为8
slaveStep = 4;
}
else if(dat == 0X10)//写多个指令
{
rxDealBuf[rxDealIndex++] = dat;
rx0X10FourByte = 0;//
slaveStep = 2;
}
else
{
slaveStep = 0;
}
break;
case 2://接收0X10开始地址及寄存器个数,4个字节
rxDealBuf[rxDealIndex++] = dat;
rx0X10FourByte++;
if(rx0X10FourByte >= 4)slaveStep = 3;
break;
case 3://0X10要接收的寄存器的字节长度,判断整个接收长度
rxDealBuf[rxDealIndex++] = dat;
rxDealLength = dat + 9;
if(rxDealLength >= RX_DEAL_ARR_LENGTH)slaveStep = 4;
else slaveStep = 0;
break;
case 4://数据处理
rxDealBuf[rxDealIndex++] = dat;
if(rxDealIndex >= rxDealLength)
{
crc = crc_modbusrtu(rxDealBuf,8);
if(crc == 0)
{
if(rxDealBuf[1] == 0x03)//主机查询
{
startIndex = (rxDealBuf[2]<<8)+rxDealBuf[3];
if(startIndex >= REGISTER_START_INDEX && startIndex < (REGISTER_START_INDEX + REGISTER_NUM))//开始地址在允许范围内
{
startIndex -= REGISTER_START_INDEX;
num = rxDealBuf[5];//要查询的个数
if(num > (REGISTER_NUM - startIndex))num = (REGISTER_NUM - startIndex);
//回发
i=0;
tempArr[i++] = 0x01;
tempArr[i++] = 0x03;
tempArr[i++] = num<<1;
for(j=0;j<num;j++)
{
tempArr[i++] = registerData[startIndex+j]>>8;//这里的数据需要根据情况修改
tempArr[i++] = registerData[startIndex+j]&0X00FF;//这里的数据需要根据情况修改
}
crc = crc_modbusrtu(tempArr,i);
tempArr[i++] = crc>>8;
tempArr[i++] = crc&0X00ff;
for(j=0;j<i;j++)
{
tx_buf_load(tempArr[j]);
}
}
}
else if(rxDealBuf[1] == 0x06)//主机修改单个数据
{
startIndex = (rxDealBuf[2]<<8)+rxDealBuf[3];
if(startIndex >= REGISTER_START_INDEX && startIndex < (REGISTER_START_INDEX + REGISTER_NUM))//开始地址在允许范围内
{
startIndex -= REGISTER_START_INDEX;
registerData[startIndex] = (RxDealBuf[4]<<8)+RxDealBuf[5];
//回发
for(i=0;i<8;i++)//成功原数据返回
{
tx_buf_load(rxDealBuf[i]);
}
}
}
else if(rxDealBuf[1] == 0x10)//主机修改多个数据
{
startIndex = (rxDealBuf[2]<<8)+rxDealBuf[3];
if(startIndex >= REGISTER_START_INDEX && startIndex < (REGISTER_START_INDEX + REGISTER_NUM))//开始地址在允许范围内
{
startIndex -= REGISTER_START_INDEX;
num = rxDealBuf[5];//修改个数
if(num > (REGISTER_NUM - startIndex))num = (REGISTER_NUM - startIndex);
for(i=0;i<num;i++)//写数据
{
dat = rxDealBuf[7+(i<<1)];
dat <<= 8;
dat |= rxDealBuf[8+(i<<1)];
registerData[startIndex+i] = dat;
}
//回发
for(i=0;i<6;i++)
{
tempArr[i] = rxDealBuf[i];
}
crc = crc_modbusrtu(tempArr,i);
tempArr[i++] = crc>>8;
tempArr[i++] = crc&0X00ff;
for(j=0;j<i;j++)
{
tx_buf_load(tempArr[j]);
}
}
}
}
slaveStep = 0;
}
break;
default:
slaveStep = 0;
break;
}
}
}
}
if(comNoDataTimeCnt < 6)//12ms无数据认定为错误数据
{
comNoDataTimeCnt++;
}
else
{
slaveStep = 0;
}
}
void interr_uart()//串口接收中断
{
//进来清中断
rxBuf[rxIndexHigh] = _uartRxData;//串口传过来的数据
if(++rxIndexHigh >= RX_ARR_LENGTH)rxIndexHigh=0;
}
void tx_buf_load(BYTE dat)//数据发送
{
if(txIndexLow != txIndexHigh)
{
//
_uartTxData = txBuf[txIndexLow];//发送数据
if(++txIndexLow >= TX_ARR_LENGTH)txIndexLow=0;
}
}
WORD crc_modbusrtu(BYTE *ptr, BYTE len)
{
BYTE i;
WORD crc = 0xffff;
WORD temp;
if (len == 0) {
len = 1;
}
while (len--) {
crc ^= *ptr;
for (i = 0; i<8; i++)
{
if (crc & 1) {
crc >>= 1;
crc ^= CRC_CHECK_NUM;
}
else {
crc >>= 1;
}
}
ptr++;
}
temp = crc>>8;
temp |= (crc<<8);
return(temp);
}