我的ModBus主机-协议篇

该章节是整个系列中最简单的由"Modbus_Master.c"和"Modbus_Master.h"两个文件组成。
首先来看"Modbus_Master.h"

#ifndef __MODBUSMASTER_H
#define __MODBUSMASTER_H

#include "Header.h"
#include "UartDebug.h"
#include "Delay.h"

struct ModbusMasterDevice
{
	struct UartDebugMember *UDM;
	uint8_t State;
	uint16_t DelayTime;
	uint16_t Input_Reg[8];  
	uint16_t Hold_Reg[8]; 
	uint8_t RIR_Update;
	uint8_t WHR_Success;
};

extern struct ModbusMasterDevice MMDPort1;

void ModBusMasterInit(void);
uint8_t WriteHoldReg(struct ModbusMasterDevice *MBM,uint8_t DeviceAddr,uint16_t Addr,uint16_t Len,uint16_t *Data);
uint8_t ReadHoldInputReg(struct ModbusMasterDevice *MBM,uint8_t DeviceAddr,uint8_t Com,uint16_t Addr,uint16_t Len);

#endif 

该文件包含了最基本的"Header.h"和UART篇介绍的 “UartDebug.h"以及我个人最常用的"Delay.h”。
为了更方便的讲解后续代码我们首先介绍一下"Delay.c"中的三个函数。

void Set_Delay_Time(uint16_t Time,uint16_t *DelayTime_Count)
{
	*DelayTime_Count=Time;
}
void DelayTimeCount_ms(uint16_t *DelayTime_Count)
{
	if(*DelayTime_Count==0)
	{
		*DelayTime_Count=0;
	}
	else
	{
		*DelayTime_Count-=1;
	}
}
uint8_t CheckDelay(uint16_t *DelayTime_Count)
{
	if(*DelayTime_Count==0)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

第一个函数“void Set_Delay_Time(uint16_t Time,uint16_t *DelayTime_Count)”就是把Time赋值给*DelayTime_Count,具体使用示例会在下文体现;
第二个函数“void DelayTimeCount_ms(uint16_t *DelayTime_Count)”就是把输入的变量作累减直到0,该函数需要每隔1ms或者其他时间来周期执行;
第三个函数“uint8_t CheckDelay(uint16_t *DelayTime_Count)”就是查询输入的变量是否为0并返回相应的值,具体使用示例会在下文体现。
好了我们接着看"Modbus_Master.h"。
“struct UartDebugMember *UDM;” ModBus主机使用的端口信息;
“uint8_t State;” ModBus主机当前的工作状态;
“uint16_t DelayTime;” 从机未响应计时;
“uint16_t Input_Reg[8];” 输入寄存器;
“uint16_t Hold_Reg[8];” 保持寄存器;
“uint8_t RIR_Update;” 读取输入/保持寄存器成功标志;
“uint8_t WHR_Success;” 写保持寄存器成功标志。
从代码中可以看出我们的ModBus主机仅实现了读取输入/保持寄存器和写保持寄存器两个功能,因为这两个功能最常用,其他的功能我也懒得写了。现在看看具体代码。

#include "Modbus_Master.h"

struct ModbusMasterDevice MMDPort1;

void ModBusMasterInit(void)
{
	MMDPort1.UDM = &U_D_Uart7;
}
/*******************************************************************************
*Function Name    : CRC16_CHECK
*Input            :
*Return           :
*Description      : 
*******************************************************************************/
static unsigned short int CRC16_CHECK(unsigned char *Buf, unsigned char CRC_CNT)
{
	unsigned short int CRC16_Temp;
	unsigned char i,j;
	CRC16_Temp = 0xffff;

	for (i=0;i<CRC_CNT; i++)
	{      
		CRC16_Temp ^= Buf[i];
		for (j=0;j<8;j++) 
		{
			if (CRC16_Temp & 0x01)
				CRC16_Temp = (CRC16_Temp >>1 ) ^ 0xa001;
			else
				CRC16_Temp = CRC16_Temp >> 1;
		}
	}
	return CRC16_Temp;
}
uint8_t WriteHoldReg(struct ModbusMasterDevice *MBM,uint8_t DeviceAddr,uint16_t Addr,uint16_t Len,uint16_t *Data)
{
	uint16_t CRC16=0;
	uint16_t CRC16_Receive=0;
	uint8_t i=0;
	
	switch(MBM->State)
	{
		case 0:
		{
			MBM->UDM->TransmitBuf[0] = DeviceAddr;
			MBM->UDM->TransmitBuf[1] = 0x10;
			MBM->UDM->TransmitBuf[2] = Addr>>8;
			MBM->UDM->TransmitBuf[3] = Addr;
			MBM->UDM->TransmitBuf[4] = Len>>8;
			MBM->UDM->TransmitBuf[5] = Len;
			MBM->UDM->TransmitBuf[6] = Len*2;
			for(i=0;i<Len;i++)
			{
				MBM->UDM->TransmitBuf[i*2+7] = *(Data+i)>>8;
				MBM->UDM->TransmitBuf[i*2+8] = *(Data+i);
			}
			CRC16 = CRC16_CHECK(MBM->UDM->TransmitBuf,7+Len*2);
			MBM->UDM->TransmitBuf[8+Len*2] = CRC16>>8;
			MBM->UDM->TransmitBuf[7+Len*2] = CRC16;
			TransmitData(MBM->UDM,MBM->UDM->TransmitBuf,9+Len*2);
			Set_Delay_Time(100,&MBM->DelayTime);
			MBM->State ++;
		}break;
		case 1:
		{
			if(MBM->UDM->ReceiveFinish)
			{
				CRC16_Receive=(MBM->UDM->ReceiveBuf[MBM->UDM->ReceivePoint-1]<<8)|MBM->UDM->ReceiveBuf[MBM->UDM->ReceivePoint-2];	
				CRC16 = CRC16_CHECK(MBM->UDM->ReceiveBuf,MBM->UDM->ReceivePoint-2);
				if(CRC16_Receive == CRC16)
				{
					/*这么写是有bug的,算了先这么着吧*/
					MBM->WHR_Success = 1;
				}
				MBM->State = 0;
                ClearRxData(MBM->UDM);
				return 1;
			}
			else if(CheckDelay(&MBM->DelayTime) == 0)
			{
				MBM->State = 0;
				ClearRxData(MBM->UDM);
				return 1;
			}
		}break;
	}
	return 0;	
}
uint8_t ReadHoldInputReg(struct ModbusMasterDevice *MBM,uint8_t DeviceAddr,uint8_t Com,uint16_t Addr,uint16_t Len)
{
	uint16_t CRC16=0;
	uint16_t CRC16_Receive=0;
	uint8_t i=0;
	
	switch(MBM->State)
	{
		case 0:
		{
			MBM->UDM->TransmitBuf[0] = DeviceAddr;
			MBM->UDM->TransmitBuf[1] = Com;
			MBM->UDM->TransmitBuf[2] = Addr>>8;
			MBM->UDM->TransmitBuf[3] = Addr;
			MBM->UDM->TransmitBuf[4] = Len>>8;
			MBM->UDM->TransmitBuf[5] = Len;
			CRC16 = CRC16_CHECK(MBM->UDM->TransmitBuf,6);
			MBM->UDM->TransmitBuf[7] = CRC16>>8;
			MBM->UDM->TransmitBuf[6] = CRC16;
			TransmitData(MBM->UDM,MBM->UDM->TransmitBuf,8);
			Set_Delay_Time(100,&MBM->DelayTime);
			MBM->State ++;
		}break;
		case 1:
		{
			if(MBM->UDM->ReceiveFinish)
			{
				CRC16_Receive=(MBM->UDM->ReceiveBuf[MBM->UDM->ReceivePoint-1]<<8)|MBM->UDM->ReceiveBuf[MBM->UDM->ReceivePoint-2];	
				CRC16 = CRC16_CHECK(MBM->UDM->ReceiveBuf,MBM->UDM->ReceivePoint-2);
				if(CRC16_Receive == CRC16)
				{
					for(i=0;i<MBM->UDM->ReceiveBuf[2]/2;i++)
					{
						if(Com == 0x04)
						{
							MBM->Input_Reg[Addr+i] = (MBM->UDM->ReceiveBuf[3+i*2])<<8 | MBM->UDM->ReceiveBuf[4+i*2];
						}
						else
						{
							MBM->Hold_Reg[Addr+i] = (MBM->UDM->ReceiveBuf[3+i*2])<<8 | MBM->UDM->ReceiveBuf[4+i*2];
						}
					}
					MBM->RIR_Update = 1;
				}
				MBM->State = 0;
                ClearRxData(MBM->UDM);
				return 1;
			}
			else if(CheckDelay(&MBM->DelayTime) == 0)
			{
				MBM->State = 0;
				ClearRxData(MBM->UDM);
				return 1;
			}
		}break;
	}
	return 0;
}

可以看到首先定义了一个ModbusMasterDevice实体MMDPort1,然后初始化该实体将其要使用的端口与U_D_Uart7对接起来。
其余的代码就很简单了,需要注意的有两个地方,一是我们把给从机发数据和接收从机数据分成了两部分来完成。为什么要这么做呢?这是为了当从机响应不及时或者从机不在线时MCU可以去做其他的事情而不必在这里死等。二是当收到从机应答(无论CRC校验是否成功)或者从机响应超时(这里就涉及到前文中提及的“delay”三个函数)都需要执行ClearRxData()并返回1,这点非常重要。
在该函数中没有做重发机制,并不是没有重发机制而是在更往上的层中来实现的后面会介绍到。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值