MODBUS RTU C语言 从机实现

本文详细介绍了Modbus RTU协议的结构和解析过程,包括地址、功能码、寄存器地址、数据和校验的处理。通过示例代码展示了如何接收和处理Modbus RTU协议的数据,涉及数据校验、寄存器操作以及不同指令的响应。此外,还提到了中断处理和无数据计时机制,确保通信的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接: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);
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值