Modbus RTU的Master及Slave功能的实现

这里的程序的地址是基于0基的地址,并不是上面的plc modbus地址,功能码3,0地址对应的是(uint16_t)HoldRegData[0]

先贴程序

modbus.h

#ifndef _MODBUS_H_
#define _MODBUS_H_
#include "stdint.h"
#include "usart.h"

#define InputReg_Max_Addr 64
#define HoldReg_Max_Addr 64
#define Coil_MaxAddr 1
#define InputBit_Max_Addr 1
#define TxBufLength 40
#define RxBufLength 40




typedef struct {
	uint8_t DeviceID;
	uint8_t FunCode;
	uint16_t Addr;
	uint16_t nWord;
	uint8_t * DataBuffer;
	uint8_t TxBuf[TxBufLength];
	uint16_t TxLength;
	uint8_t RxBuf[RxBufLength];
	uint16_t RxLength;
	uint16_t Timeout;
	UART_HandleTypeDef* huart;
}ModbusMasterHandler;

typedef struct {
	uint8_t DeviceID;
uint16_t InputRegData[InputReg_Max_Addr];
uint16_t HoldRegData[HoldReg_Max_Addr];
uint8_t CoilData[Coil_MaxAddr];
uint8_t InputBitData[InputBit_Max_Addr];
	uint8_t RxBuf[RxBufLength];
	uint16_t RxLength;
	uint8_t TxBuf[TxBufLength];
	uint16_t TxLength;
	uint16_t Timeout;
	UART_HandleTypeDef* huart;
}ModbusSlaveHandler;

typedef enum{
	NoError=0x00,
	Err_WrongMsg=0x01,
	Err_IllegalFunctionCode=0x81,
	Err_IllegalAddress=0x82,
	Err_IllegalValue=0x83,
	Err_Timeout=0x84,
	Err_CRC=0x85,
	Err_WrongFunctionCode=0x86,
	Err_NoDefine=0xff,
}Modbus_Error;


uint16_t swap(uint16_t orgin);
uint16_t CRC16(uint8_t * pucFrame, uint16_t usLen );      //计算CRC
Modbus_Error Modbus_Master_Req(ModbusMasterHandler* hMMH,uint8_t DeviceID,uint8_t FunCode,uint16_t Addr, uint16_t nWord, uint8_t* DataBuffer);    //用于发送Master请求
uint8_t Modbus_Master_Init(ModbusMasterHandler* hMMH, UART_HandleTypeDef* huart,uint16_t Timeout);         //用于初始化ModbusRTU Master handler
uint8_t Modbus_Master_TimeoutHandler(ModbusMasterHandler* hMMH);                           //用于处理超时后的数据错误

uint8_t Modbus_Slave_Init(ModbusSlaveHandler* hMMH,UART_HandleTypeDef* huart,uint8_t DeviceID,uint16_t Timeout);          //用于初始化ModbusRTU Slave handler
Modbus_Error Modbus_Slave_Respond(ModbusSlaveHandler* hMMH,uint16_t BufLength);                //用于接收modbus请求,并作出反馈
static Modbus_Error _Modbus_Slave_Handler(ModbusSlaveHandler* hMMH,uint16_t BufLength); 

uint8_t Modbus_Transmit(UART_HandleTypeDef* phuart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout);
uint8_t Modbus_Receive(UART_HandleTypeDef* phuart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout);



#endif

modbus.c

#include "modbus.h"

uint16_t InputRegData[InputReg_Max_Addr];
uint16_t HoldRegData[HoldReg_Max_Addr];
uint8_t CoilData[Coil_MaxAddr];
uint8_t InputBitData[InputBit_Max_Addr];




static const uint8_t aucCRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40
};

static const uint8_t aucCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
    0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
    0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
    0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 
    0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
    0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
    0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
    0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    0x41, 0x81, 0x80, 0x40
};

uint16_t CRC16( uint8_t * pucFrame, uint16_t usLen )
{
    uint8_t           ucCRCHi = 0xFF;
    uint8_t           ucCRCLo = 0xFF;
    int             iIndex;

    while( usLen-- )
    {
        iIndex = ucCRCLo ^ *( pucFrame++ );
        ucCRCLo = ( uint8_t )( ucCRCHi ^ aucCRCHi[iIndex] );
        ucCRCHi = aucCRCLo[iIndex];
    }
    return ( uint16_t )( ucCRCHi << 8 | ucCRCLo );
}

uint8_t Modbus_Master_Init(ModbusMasterHandler* hMMH, UART_HandleTypeDef* huart,uint16_t Timeout)
{
	hMMH->huart=huart;
	hMMH->Timeout=Timeout;
	return 0;
}

uint8_t Modbus_Slave_Init(ModbusSlaveHandler* hMMH,UART_HandleTypeDef* huart,uint8_t DeviceID,uint16_t Timeout)
{
	hMMH->huart=huart;
	hMMH->DeviceID=DeviceID;
	hMMH->Timeout=Timeout;
	return 0;
}

uint16_t swap(uint16_t orgin)
{
	return ((orgin&0xFF)<<8)|((orgin>>8)&0xFF);
}

Modbus_Error Modbus_Master_Req(ModbusMasterHandler* hMMH,uint8_t DeviceID,uint8_t FunCode,uint16_t Addr, uint16_t nWord, uint8_t* DataBuffer)
{
	uint16_t crc;

		hMMH->DeviceID=DeviceID;        //初始化数据
		hMMH->FunCode=FunCode;
		hMMH->Addr=Addr;
		hMMH->nWord=nWord;
		hMMH->DataBuffer=DataBuffer;
		for(int i=0;i<TxBufLength;i++)
		{
			hMMH->TxBuf[i]=0x00;
			hMMH->RxBuf[i]=0x00;
		}

//---------------合成发送帧---------------——————
		*(hMMH->TxBuf+0)=DeviceID;   //地址码
		*(hMMH->TxBuf+1)=FunCode;  //功能码
		*(hMMH->TxBuf+2)=Addr>>8&0xFF;   //地址位
		*(hMMH->TxBuf+3)=Addr&0xFF;			//地址位
		switch(FunCode)                    //编辑TxBuf
		{
			case 0x01:       //读1路或多路线圈状态
			case 0x02:       //读1路或多路开关量状态输入
			case 0x03:       //读1路或多路保持寄存器
			case 0x04:
				*(hMMH->TxBuf+4)=nWord>>8&0xFF;   //Word数据长度位
				*(hMMH->TxBuf+5)=nWord&0xFF;   //Word数据长度位
				crc=CRC16(hMMH->TxBuf,6);
				*(hMMH->TxBuf+6)=crc&0xFF;        //CRC
				*(hMMH->TxBuf+7)=crc>>8&0xFF;     //CRC
				hMMH->TxLength=8;                          
			break;
			case 0x05:        //写1路开关量输出
			case 0x06:          //写单个保持寄存器
				*(hMMH->TxBuf+4)=DataBuffer[1];   //控制命令
				*(hMMH->TxBuf+5)=DataBuffer[0];   //控制命令
				crc=CRC16(hMMH->TxBuf,6);
				*(hMMH->TxBuf+6)=crc&0xFF;        //CRC
				*(hMMH->TxBuf+7)=crc>>8&0xFF;     //CRC
				hMMH->TxLength=8;
			break;		
			case 0x10:        //写多个保持寄存器
				*(hMMH->TxBuf+4)=nWord>>8&0xFF;   //Word数据长度位
				*(hMMH->TxBuf+5)=nWord&0xFF;   //Word数据长度位
				*(hMMH->TxBuf+6)=(nWord*2)&0xFF;   //Word数据长度位
				for(int i=0;i<nWord;i++)
				{
					*(hMMH->TxBuf+7+i*2)=DataBuffer[1+i*2];    //数据高位
					*(hMMH->TxBuf+8+i*2)=DataBuffer[0+i*2];    //数据低位
				}
				crc=CRC16(hMMH->TxBuf,7+nWord*2);
				*(hMMH->TxBuf+7+nWord*2)=crc&0xFF;        //CRC
				*(hMMH->TxBuf+8+nWord*2)=crc>>8&0xFF;     //CRC
				hMMH->TxLength=9+nWord*2;
			break;
			default:
				return Err_WrongFunctionCode;
			}
		Modbus_Receive(hMMH->huart,hMMH->RxBuf,hMMH->RxLength,0); //读取一次清空接收缓冲区
		Modbus_Transmit(hMMH->huart,hMMH->TxBuf,hMMH->TxLength,hMMH->Timeout);

	
		
//——————————————————————计算预计接收长度————————————————

			switch(FunCode)                    //编辑TxBuf
			{
				case 0x01:       //读1路或多路线圈状态
				case 0x02:       //读1路或多路开关量状态输入
					hMMH->RxLength=6+(nWord-1)/8;
				break;
				case 0x03:       //读1路或多路保持寄存器
				case 0x04:					
					hMMH->RxLength=5+nWord*2;				
				break;
				case 0x05:        //写1路开关量输出
				case 0x06: 				//写单个保持寄存器
					hMMH->RxLength=hMMH->TxLength;	         
				break;		
				case 0x10:        //写多个保持寄存器
					hMMH->RxLength=8;	 
				break;
				default:
					return Err_WrongFunctionCode;
			}

//——————————————等待数据——————————————

				if(Modbus_Receive(hMMH->huart,hMMH->RxBuf,hMMH->RxLength,hMMH->Timeout))      //返回0是接收到预期长度的数据,其他值错误
				{
					return Modbus_Master_TimeoutHandler(hMMH);
				}

//——————————————处理数据————————————


			if(hMMH->TxBuf[0]!=hMMH->RxBuf[0]||hMMH->TxBuf[1]!=hMMH->RxBuf[1])
			{
				return Err_WrongMsg;
			}

			switch(FunCode)
			{
				case 0x01:
				case 0x02:
					if(hMMH->RxBuf[2]!=(nWord-1)/8+1){return Err_WrongMsg;}
					crc=CRC16(hMMH->RxBuf,4);
					if(*(uint16_t*)(hMMH->RxBuf+4)!=crc)
					{
						return Err_CRC;
					}
					DataBuffer[0]=hMMH->RxBuf[3];
					return NoError;
 
				case 0x03:
				case 0x04:
					if(hMMH->RxBuf[2]!=nWord*2){return Err_WrongMsg;}
					crc=CRC16(hMMH->RxBuf,3+nWord*2);
					if(*(uint16_t*)(hMMH->RxBuf+3+nWord*2)!=crc){return Err_CRC;}
					for(int i=0;i<nWord;i++)
					{
						DataBuffer[1+i*2]=*(hMMH->RxBuf+3+i*2);    //数据高位
						DataBuffer[0+i*2]=*(hMMH->RxBuf+4+i*2);    //数据低位
					}
					return NoError;
 
				case 0x05:
				case 0x06:
					for(int i=0;i<8;i++)
					{
						if(hMMH->RxBuf[i]!=hMMH->TxBuf[i]){return Err_WrongMsg;}
					}
					return NoError;
 	
				case 0x10:
					for(int i=0;i<6;i++)
					{
						if(hMMH->RxBuf[i]!=hMMH->TxBuf[i]){return Err_WrongMsg;}
					}
					crc=CRC16(hMMH->RxBuf,6);
					if(*(uint16_t*)(hMMH->RxBuf+6)!=crc){return Err_CRC;}
					return NoError;					
			}
			
	return Err_NoDefine;
}



Modbus_Error Modbus_Slave_Respond(ModbusSlaveHandler* hMMH,uint16_t BufLength)
{
	
	Modbus_Error result=	_Modbus_Slave_Handler(hMMH,BufLength);        //处理函数
	
	for(int i=0;i<RxBufLength;i++)       //清除缓存
	{
		hMMH->RxBuf[i]=0x00;
	}
	for(int i=0;i<TxBufLength;i++)
	{
		hMMH->TxBuf[i]=0x00;
	}
	
return result;	
}



static Modbus_Error _Modbus_Slave_Handler(ModbusSlaveHandler* hMMH,uint16_t BufLength)
{

	if(hMMH->RxBuf[0]!=hMMH->DeviceID)return NoError;     //判断地址
	
	if(BufLength<8) return Err_WrongMsg;//初步判断长度
	
		uint8_t DeviceID=hMMH->RxBuf[0];                    
	uint8_t FunCode=hMMH->RxBuf[1];
	uint16_t Address=hMMH->RxBuf[3]|(hMMH->RxBuf[2]<<8);
	uint16_t nWord=hMMH->RxBuf[5]|(hMMH->RxBuf[4]<<8);
	uint16_t crc;
	Modbus_Error Result=NoError;                               //需要返回信息的错误,写入Result,不需要返回信息的错误,直接return
	
	//-----------------判断收到的语句合法性----------------
	switch(FunCode)
	{
		case 0x1:
		case 0x2:
		case 0x3:
		case 0x4:	
		case 0x5:
		case 0x6:
			hMMH->RxLength=8;
			crc=CRC16(hMMH->RxBuf,6);   
			if((hMMH->RxBuf[7]<<8|hMMH->RxBuf[6])!=crc){return Err_CRC;}//判断CRC
		break;
		case 0x10:
			hMMH->RxLength=9+nWord*2;
			if(hMMH->RxLength>BufLength){return Err_WrongMsg;}     //判断消息长度是否合理
			crc=CRC16(hMMH->RxBuf,7+nWord*2);

			if((hMMH->RxBuf[8+nWord*2]<<8|hMMH->RxBuf[7+nWord*2])!=crc){return Err_CRC;}//判断CRC	
		break;
		defalut:
			Result= Err_WrongFunctionCode;                 //判断功能码合法性
		break;
	}
	

		//------------------------判断地址合法性
	switch(FunCode)
	{
		case 0x1:
		case 0x5:
			if(Address+nWord>Coil_MaxAddr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x2:
			if(Address+nWord>InputBit_Max_Addr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x3:
			if(Address+nWord>HoldReg_Max_Addr||nWord>15){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x4:	
			if(Address+nWord>InputReg_Max_Addr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x6:
			if(Address+1>HoldReg_Max_Addr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x10:
			if(Address+nWord>HoldReg_Max_Addr||nWord>15){Result=Err_IllegalAddress;}         //判断地址合法性		
		break;

	}
	

	if (Result==NoError)
	{
	//--------------------------做出正确反应--------------------
		
		hMMH->TxBuf[0]=hMMH->RxBuf[0];
		hMMH->TxBuf[1]=hMMH->RxBuf[1];
		uint8_t NumOfByte,BitInByte;
		switch(FunCode)
		{
			case 0x01:
				hMMH->TxBuf[2]=(nWord-1)/8+1;
				for(int i=0;i<nWord;i++)
				{
					NumOfByte=i/8;
					BitInByte=i%8;
					hMMH->TxBuf[3+NumOfByte]|=(CoilData[Address+i]!=0)<<BitInByte;      //将字节数据转成Byte里面一个位
				}
				crc=CRC16(hMMH->TxBuf,3+NumOfByte);
				hMMH->TxBuf[4+NumOfByte]=(crc>>8)&0xFF;
				hMMH->TxBuf[5+NumOfByte]=crc&0xFF;
				hMMH->TxLength=6+NumOfByte;
				break;
			case 0x02:
				hMMH->TxBuf[2]=(nWord-1)/8+1;
				for(int i=0;i<nWord;i++)
				{
					NumOfByte=i/8;
					BitInByte=i%8;
					hMMH->TxBuf[3+NumOfByte]|=(InputBitData[Address+i]!=0)<<BitInByte;      //将字节数据转成Byte里面一个位
				}
				crc=CRC16(hMMH->TxBuf,3+NumOfByte);
				hMMH->TxBuf[4+NumOfByte]=(crc>>8)&0xFF;
				hMMH->TxBuf[5+NumOfByte]=crc&0xFF;
				hMMH->TxLength=6+NumOfByte;
			break;
			case 0x03:
				hMMH->TxBuf[2]=nWord*2;
				for(int i=0;i<nWord;i++)
				{
					hMMH->TxBuf[3+i*2]=(*(hMMH->HoldRegData+Address+i)>>8)&0xFF;                     //读取数据
					hMMH->TxBuf[4+i*2]=	(*(hMMH->HoldRegData+Address+i))&0xFF;		
				}
				crc=CRC16(hMMH->TxBuf,3+nWord*2);
				hMMH->TxBuf[3+nWord*2]=(crc>>8)&0xFF;
				hMMH->TxBuf[4+nWord*2]=crc&0xFF;
				hMMH->TxLength=5+nWord*2;		
			break;
			case 0x04:
				hMMH->TxBuf[2]=nWord*2;
				for(int i=0;i<nWord;i++)
				{
					hMMH->TxBuf[3+i*2]=(*(hMMH->InputRegData+Address+i)>>8)&0xFF;                        //读取数据
					hMMH->TxBuf[4+i*2]=(*(hMMH->InputRegData+Address+i))&0xFF;			
				}
				crc=CRC16(hMMH->TxBuf,3+nWord*2);
				hMMH->TxBuf[3+nWord*2]=(crc>>8)&0xFF;
				hMMH->TxBuf[4+nWord*2]=crc&0xFF;
				hMMH->TxLength=5+nWord*2;		
			break;			
			case 0x05:
				CoilData[Address]=(hMMH->RxBuf[4]||hMMH->RxBuf[5]);          //其中一个大于0就置1,否则置0
				for(int i=0;i<8;i++)                           //返回语句和接收完全一样
				{
					hMMH->TxBuf[i]=hMMH->RxBuf[i];
				}
				hMMH->TxLength=8;
			break;
			case 0x06:		
					*(hMMH->HoldRegData+Address)=hMMH->RxBuf[4]<<8|hMMH->RxBuf[5];               //写入数据
				for(int i=0;i<8;i++)                           //返回语句和接收完全一样
				{
					hMMH->TxBuf[i]=hMMH->RxBuf[i];
				}
				hMMH->TxLength=8;
			break;
			case 0x10:
				for(int i=0;i<nWord;i++)
				{
					*(hMMH->HoldRegData+Address+i)=hMMH->RxBuf[7+i*2]<<8|hMMH->RxBuf[8+i*2];               //写入数据
				}	
				for(int i=0;i<7;i++)                                                 //制作返回信息
				{
					hMMH->TxBuf[i]=hMMH->RxBuf[i];
				}
				crc=CRC16(hMMH->TxBuf,7);
				hMMH->TxBuf[7]=(crc>>8)&0xFF;
				hMMH->TxBuf[8]=crc&0xFF;
				hMMH->TxLength=9;	
			break;
		}	
	}
	else
	{
		hMMH->TxBuf[0]=hMMH->RxBuf[0];        //制作返回的错误信息
		hMMH->TxBuf[1]=hMMH->RxBuf[1]|0x80;
		hMMH->TxBuf[2]=Result;
		crc=CRC16(hMMH->TxBuf,3);
		hMMH->TxBuf[3]=(crc>>8)&0xFF;
		hMMH->TxBuf[4]=crc&0xFF;
		hMMH->TxLength=5;	
	}

Modbus_Transmit(hMMH->huart,hMMH->TxBuf,hMMH->TxLength,100);	
}
	
	
	
	
	
	



uint8_t Modbus_Master_TimeoutHandler(ModbusMasterHandler* hMMH)
{
	//判断串口是否返回5个字节的错误代码,通过CRC判断帧完整
	uint16_t crc=CRC16(hMMH->RxBuf,3);
	if(*(uint16_t*)(hMMH->RxBuf+3)==crc)//直接返回信息里的错误码
	{

		return hMMH->RxBuf[2];
	}

	return Err_Timeout;
}



uint8_t Modbus_Transmit(UART_HandleTypeDef* huart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout)
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
	uint8_t Result=HAL_UART_Transmit(huart,TxBuf,TxSize,Timeout);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
	return Result;
	//发送完成后返回0,否则返回非0
}
uint8_t Modbus_Receive(UART_HandleTypeDef* huart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout)
{
	
	return HAL_UART_Receive(huart,RxBuf,RxSize,Timeout);	
	//接收成功返回0,否则返回非0
}


main.c部分


Modbus_Slave_Init(&hMMHSlave,&huart2,1,100);
Modbus_Master_Init(&hMMHMaster,&huart2,100);
 while(1)
  {

	

	HAL_Delay(10);
//		HAL_UART_Transmit(&huart2,(uint8_t*)WriteBuf,4,100);
/*		
if(Modbus_Master_Req(&hMMHMaster,1,0x01,0x0000,2,(uint8_t*)&WriteBuf)==0x00)
{
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);

}
*/
	HAL_UART_Receive(&huart2,(uint8_t*)hMMHSlave.RxBuf,40,100);	
		if(hMMHSlave.RxBuf[0]>0)
		{
if(Modbus_Slave_Respond(&hMMHSlave,40)==0x00)	
		{
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_12);
}	
		}

这是自己写的程序,包括了Master及Slave的程序

为了方便移植,使用了阻塞式的uart通讯方式,移植时仅需要几步

modbus.h的头文件中,定义了modbus master几个主要变量

#define InputReg_Max_Addr 64
#define HoldReg_Max_Addr 64
#define Coil_MaxAddr 1
#define InputBit_Max_Addr 1
#define TxBufLength 40
#define RxBufLength 40

前四个是作为slave中的寄存器数量,寄存器建立在ModbusSlaveHandler;内部,只需要根据自己需要修改寄存器大小,及缓存大小,看自己用到的modbus命令的长度。

需要用户自定义两个阻塞式函数

uint8_t Modbus_Transmit(UART_HandleTypeDef* phuart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout);
uint8_t Modbus_Receive(UART_HandleTypeDef* phuart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout);

这里很简单地利用的cubemx配置的uart通讯,使用了HAL库的Transmit及Receive,如果是其他库,需要自己实现阻塞,因为master需要等待客户端返回才能完成通讯,所以阻塞是比较好的实现方式。

uint8_t Modbus_Transmit(UART_HandleTypeDef* huart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout)
{
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
    uint8_t Result=HAL_UART_Transmit(huart,TxBuf,TxSize,Timeout);
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
    return Result;
    //发送完成后返回0,否则返回非0
}

uint8_t Modbus_Receive(UART_HandleTypeDef* huart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout)
{
    
    return HAL_UART_Receive(huart,RxBuf,RxSize,Timeout);    
    //接收成功返回0,否则返回非0
}

第二步是,在初始化时,显示对Master或者Slave的初始化

放在while前面

第三步是在需要的时候调用master或者slave的处理函数

 当使用Slave前,用户的数据要事先存入结构体中,这个是当然的

需要程序的可以到这里下载

(1条消息) UART2_485.zip-嵌入式文档类资源-CSDN文库

版权声明:本文为CSDN博主「alwayslzz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/alwayslzz/article/details/122331964

这里的程序的地址是基于0基的地址,并不是上面的plc modbus地址,功能码3,0地址对应的是(uint16_t)HoldRegData[0]

先贴程序

modbus.h

#ifndef _MODBUS_H_
#define _MODBUS_H_
#include "stdint.h"
#include "usart.h"

#define InputReg_Max_Addr 64
#define HoldReg_Max_Addr 64
#define Coil_MaxAddr 1
#define InputBit_Max_Addr 1
#define TxBufLength 40
#define RxBufLength 40




typedef struct {
	uint8_t DeviceID;
	uint8_t FunCode;
	uint16_t Addr;
	uint16_t nWord;
	uint8_t * DataBuffer;
	uint8_t TxBuf[TxBufLength];
	uint16_t TxLength;
	uint8_t RxBuf[RxBufLength];
	uint16_t RxLength;
	uint16_t Timeout;
	UART_HandleTypeDef* huart;
}ModbusMasterHandler;

typedef struct {
	uint8_t DeviceID;
uint16_t InputRegData[InputReg_Max_Addr];
uint16_t HoldRegData[HoldReg_Max_Addr];
uint8_t CoilData[Coil_MaxAddr];
uint8_t InputBitData[InputBit_Max_Addr];
	uint8_t RxBuf[RxBufLength];
	uint16_t RxLength;
	uint8_t TxBuf[TxBufLength];
	uint16_t TxLength;
	uint16_t Timeout;
	UART_HandleTypeDef* huart;
}ModbusSlaveHandler;

typedef enum{
	NoError=0x00,
	Err_WrongMsg=0x01,
	Err_IllegalFunctionCode=0x81,
	Err_IllegalAddress=0x82,
	Err_IllegalValue=0x83,
	Err_Timeout=0x84,
	Err_CRC=0x85,
	Err_WrongFunctionCode=0x86,
	Err_NoDefine=0xff,
}Modbus_Error;


uint16_t swap(uint16_t orgin);
uint16_t CRC16(uint8_t * pucFrame, uint16_t usLen );      //计算CRC
Modbus_Error Modbus_Master_Req(ModbusMasterHandler* hMMH,uint8_t DeviceID,uint8_t FunCode,uint16_t Addr, uint16_t nWord, uint8_t* DataBuffer);    //用于发送Master请求
uint8_t Modbus_Master_Init(ModbusMasterHandler* hMMH, UART_HandleTypeDef* huart,uint16_t Timeout);         //用于初始化ModbusRTU Master handler
uint8_t Modbus_Master_TimeoutHandler(ModbusMasterHandler* hMMH);                           //用于处理超时后的数据错误

uint8_t Modbus_Slave_Init(ModbusSlaveHandler* hMMH,UART_HandleTypeDef* huart,uint8_t DeviceID,uint16_t Timeout);          //用于初始化ModbusRTU Slave handler
Modbus_Error Modbus_Slave_Respond(ModbusSlaveHandler* hMMH,uint16_t BufLength);                //用于接收modbus请求,并作出反馈
static Modbus_Error _Modbus_Slave_Handler(ModbusSlaveHandler* hMMH,uint16_t BufLength); 

uint8_t Modbus_Transmit(UART_HandleTypeDef* phuart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout);
uint8_t Modbus_Receive(UART_HandleTypeDef* phuart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout);



#endif

modbus.c

#include "modbus.h"

uint16_t InputRegData[InputReg_Max_Addr];
uint16_t HoldRegData[HoldReg_Max_Addr];
uint8_t CoilData[Coil_MaxAddr];
uint8_t InputBitData[InputBit_Max_Addr];




static const uint8_t aucCRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40
};

static const uint8_t aucCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
    0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
    0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
    0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 
    0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
    0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
    0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
    0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    0x41, 0x81, 0x80, 0x40
};

uint16_t CRC16( uint8_t * pucFrame, uint16_t usLen )
{
    uint8_t           ucCRCHi = 0xFF;
    uint8_t           ucCRCLo = 0xFF;
    int             iIndex;

    while( usLen-- )
    {
        iIndex = ucCRCLo ^ *( pucFrame++ );
        ucCRCLo = ( uint8_t )( ucCRCHi ^ aucCRCHi[iIndex] );
        ucCRCHi = aucCRCLo[iIndex];
    }
    return ( uint16_t )( ucCRCHi << 8 | ucCRCLo );
}

uint8_t Modbus_Master_Init(ModbusMasterHandler* hMMH, UART_HandleTypeDef* huart,uint16_t Timeout)
{
	hMMH->huart=huart;
	hMMH->Timeout=Timeout;
	return 0;
}

uint8_t Modbus_Slave_Init(ModbusSlaveHandler* hMMH,UART_HandleTypeDef* huart,uint8_t DeviceID,uint16_t Timeout)
{
	hMMH->huart=huart;
	hMMH->DeviceID=DeviceID;
	hMMH->Timeout=Timeout;
	return 0;
}

uint16_t swap(uint16_t orgin)
{
	return ((orgin&0xFF)<<8)|((orgin>>8)&0xFF);
}

Modbus_Error Modbus_Master_Req(ModbusMasterHandler* hMMH,uint8_t DeviceID,uint8_t FunCode,uint16_t Addr, uint16_t nWord, uint8_t* DataBuffer)
{
	uint16_t crc;

		hMMH->DeviceID=DeviceID;        //初始化数据
		hMMH->FunCode=FunCode;
		hMMH->Addr=Addr;
		hMMH->nWord=nWord;
		hMMH->DataBuffer=DataBuffer;
		for(int i=0;i<TxBufLength;i++)
		{
			hMMH->TxBuf[i]=0x00;
			hMMH->RxBuf[i]=0x00;
		}

//---------------合成发送帧---------------——————
		*(hMMH->TxBuf+0)=DeviceID;   //地址码
		*(hMMH->TxBuf+1)=FunCode;  //功能码
		*(hMMH->TxBuf+2)=Addr>>8&0xFF;   //地址位
		*(hMMH->TxBuf+3)=Addr&0xFF;			//地址位
		switch(FunCode)                    //编辑TxBuf
		{
			case 0x01:       //读1路或多路线圈状态
			case 0x02:       //读1路或多路开关量状态输入
			case 0x03:       //读1路或多路保持寄存器
			case 0x04:
				*(hMMH->TxBuf+4)=nWord>>8&0xFF;   //Word数据长度位
				*(hMMH->TxBuf+5)=nWord&0xFF;   //Word数据长度位
				crc=CRC16(hMMH->TxBuf,6);
				*(hMMH->TxBuf+6)=crc&0xFF;        //CRC
				*(hMMH->TxBuf+7)=crc>>8&0xFF;     //CRC
				hMMH->TxLength=8;                          
			break;
			case 0x05:        //写1路开关量输出
			case 0x06:          //写单个保持寄存器
				*(hMMH->TxBuf+4)=DataBuffer[1];   //控制命令
				*(hMMH->TxBuf+5)=DataBuffer[0];   //控制命令
				crc=CRC16(hMMH->TxBuf,6);
				*(hMMH->TxBuf+6)=crc&0xFF;        //CRC
				*(hMMH->TxBuf+7)=crc>>8&0xFF;     //CRC
				hMMH->TxLength=8;
			break;		
			case 0x10:        //写多个保持寄存器
				*(hMMH->TxBuf+4)=nWord>>8&0xFF;   //Word数据长度位
				*(hMMH->TxBuf+5)=nWord&0xFF;   //Word数据长度位
				*(hMMH->TxBuf+6)=(nWord*2)&0xFF;   //Word数据长度位
				for(int i=0;i<nWord;i++)
				{
					*(hMMH->TxBuf+7+i*2)=DataBuffer[1+i*2];    //数据高位
					*(hMMH->TxBuf+8+i*2)=DataBuffer[0+i*2];    //数据低位
				}
				crc=CRC16(hMMH->TxBuf,7+nWord*2);
				*(hMMH->TxBuf+7+nWord*2)=crc&0xFF;        //CRC
				*(hMMH->TxBuf+8+nWord*2)=crc>>8&0xFF;     //CRC
				hMMH->TxLength=9+nWord*2;
			break;
			default:
				return Err_WrongFunctionCode;
			}
		Modbus_Receive(hMMH->huart,hMMH->RxBuf,hMMH->RxLength,0); //读取一次清空接收缓冲区
		Modbus_Transmit(hMMH->huart,hMMH->TxBuf,hMMH->TxLength,hMMH->Timeout);

	
		
//——————————————————————计算预计接收长度————————————————

			switch(FunCode)                    //编辑TxBuf
			{
				case 0x01:       //读1路或多路线圈状态
				case 0x02:       //读1路或多路开关量状态输入
					hMMH->RxLength=6+(nWord-1)/8;
				break;
				case 0x03:       //读1路或多路保持寄存器
				case 0x04:					
					hMMH->RxLength=5+nWord*2;				
				break;
				case 0x05:        //写1路开关量输出
				case 0x06: 				//写单个保持寄存器
					hMMH->RxLength=hMMH->TxLength;	         
				break;		
				case 0x10:        //写多个保持寄存器
					hMMH->RxLength=8;	 
				break;
				default:
					return Err_WrongFunctionCode;
			}

//——————————————等待数据——————————————

				if(Modbus_Receive(hMMH->huart,hMMH->RxBuf,hMMH->RxLength,hMMH->Timeout))      //返回0是接收到预期长度的数据,其他值错误
				{
					return Modbus_Master_TimeoutHandler(hMMH);
				}

//——————————————处理数据————————————


			if(hMMH->TxBuf[0]!=hMMH->RxBuf[0]||hMMH->TxBuf[1]!=hMMH->RxBuf[1])
			{
				return Err_WrongMsg;
			}

			switch(FunCode)
			{
				case 0x01:
				case 0x02:
					if(hMMH->RxBuf[2]!=(nWord-1)/8+1){return Err_WrongMsg;}
					crc=CRC16(hMMH->RxBuf,4);
					if(*(uint16_t*)(hMMH->RxBuf+4)!=crc)
					{
						return Err_CRC;
					}
					DataBuffer[0]=hMMH->RxBuf[3];
					return NoError;
 
				case 0x03:
				case 0x04:
					if(hMMH->RxBuf[2]!=nWord*2){return Err_WrongMsg;}
					crc=CRC16(hMMH->RxBuf,3+nWord*2);
					if(*(uint16_t*)(hMMH->RxBuf+3+nWord*2)!=crc){return Err_CRC;}
					for(int i=0;i<nWord;i++)
					{
						DataBuffer[1+i*2]=*(hMMH->RxBuf+3+i*2);    //数据高位
						DataBuffer[0+i*2]=*(hMMH->RxBuf+4+i*2);    //数据低位
					}
					return NoError;
 
				case 0x05:
				case 0x06:
					for(int i=0;i<8;i++)
					{
						if(hMMH->RxBuf[i]!=hMMH->TxBuf[i]){return Err_WrongMsg;}
					}
					return NoError;
 	
				case 0x10:
					for(int i=0;i<6;i++)
					{
						if(hMMH->RxBuf[i]!=hMMH->TxBuf[i]){return Err_WrongMsg;}
					}
					crc=CRC16(hMMH->RxBuf,6);
					if(*(uint16_t*)(hMMH->RxBuf+6)!=crc){return Err_CRC;}
					return NoError;					
			}
			
	return Err_NoDefine;
}



Modbus_Error Modbus_Slave_Respond(ModbusSlaveHandler* hMMH,uint16_t BufLength)
{
	
	Modbus_Error result=	_Modbus_Slave_Handler(hMMH,BufLength);        //处理函数
	
	for(int i=0;i<RxBufLength;i++)       //清除缓存
	{
		hMMH->RxBuf[i]=0x00;
	}
	for(int i=0;i<TxBufLength;i++)
	{
		hMMH->TxBuf[i]=0x00;
	}
	
return result;	
}



static Modbus_Error _Modbus_Slave_Handler(ModbusSlaveHandler* hMMH,uint16_t BufLength)
{

	if(hMMH->RxBuf[0]!=hMMH->DeviceID)return NoError;     //判断地址
	
	if(BufLength<8) return Err_WrongMsg;//初步判断长度
	
		uint8_t DeviceID=hMMH->RxBuf[0];                    
	uint8_t FunCode=hMMH->RxBuf[1];
	uint16_t Address=hMMH->RxBuf[3]|(hMMH->RxBuf[2]<<8);
	uint16_t nWord=hMMH->RxBuf[5]|(hMMH->RxBuf[4]<<8);
	uint16_t crc;
	Modbus_Error Result=NoError;                               //需要返回信息的错误,写入Result,不需要返回信息的错误,直接return
	
	//-----------------判断收到的语句合法性----------------
	switch(FunCode)
	{
		case 0x1:
		case 0x2:
		case 0x3:
		case 0x4:	
		case 0x5:
		case 0x6:
			hMMH->RxLength=8;
			crc=CRC16(hMMH->RxBuf,6);   
			if((hMMH->RxBuf[7]<<8|hMMH->RxBuf[6])!=crc){return Err_CRC;}//判断CRC
		break;
		case 0x10:
			hMMH->RxLength=9+nWord*2;
			if(hMMH->RxLength>BufLength){return Err_WrongMsg;}     //判断消息长度是否合理
			crc=CRC16(hMMH->RxBuf,7+nWord*2);

			if((hMMH->RxBuf[8+nWord*2]<<8|hMMH->RxBuf[7+nWord*2])!=crc){return Err_CRC;}//判断CRC	
		break;
		defalut:
			Result= Err_WrongFunctionCode;                 //判断功能码合法性
		break;
	}
	

		//------------------------判断地址合法性
	switch(FunCode)
	{
		case 0x1:
		case 0x5:
			if(Address+nWord>Coil_MaxAddr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x2:
			if(Address+nWord>InputBit_Max_Addr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x3:
			if(Address+nWord>HoldReg_Max_Addr||nWord>15){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x4:	
			if(Address+nWord>InputReg_Max_Addr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x6:
			if(Address+1>HoldReg_Max_Addr){Result= Err_IllegalAddress;}         //判断地址合法性
		break;
		case 0x10:
			if(Address+nWord>HoldReg_Max_Addr||nWord>15){Result=Err_IllegalAddress;}         //判断地址合法性		
		break;

	}
	

	if (Result==NoError)
	{
	//--------------------------做出正确反应--------------------
		
		hMMH->TxBuf[0]=hMMH->RxBuf[0];
		hMMH->TxBuf[1]=hMMH->RxBuf[1];
		uint8_t NumOfByte,BitInByte;
		switch(FunCode)
		{
			case 0x01:
				hMMH->TxBuf[2]=(nWord-1)/8+1;
				for(int i=0;i<nWord;i++)
				{
					NumOfByte=i/8;
					BitInByte=i%8;
					hMMH->TxBuf[3+NumOfByte]|=(CoilData[Address+i]!=0)<<BitInByte;      //将字节数据转成Byte里面一个位
				}
				crc=CRC16(hMMH->TxBuf,3+NumOfByte);
				hMMH->TxBuf[4+NumOfByte]=(crc>>8)&0xFF;
				hMMH->TxBuf[5+NumOfByte]=crc&0xFF;
				hMMH->TxLength=6+NumOfByte;
				break;
			case 0x02:
				hMMH->TxBuf[2]=(nWord-1)/8+1;
				for(int i=0;i<nWord;i++)
				{
					NumOfByte=i/8;
					BitInByte=i%8;
					hMMH->TxBuf[3+NumOfByte]|=(InputBitData[Address+i]!=0)<<BitInByte;      //将字节数据转成Byte里面一个位
				}
				crc=CRC16(hMMH->TxBuf,3+NumOfByte);
				hMMH->TxBuf[4+NumOfByte]=(crc>>8)&0xFF;
				hMMH->TxBuf[5+NumOfByte]=crc&0xFF;
				hMMH->TxLength=6+NumOfByte;
			break;
			case 0x03:
				hMMH->TxBuf[2]=nWord*2;
				for(int i=0;i<nWord;i++)
				{
					hMMH->TxBuf[3+i*2]=(*(hMMH->HoldRegData+Address+i)>>8)&0xFF;                     //读取数据
					hMMH->TxBuf[4+i*2]=	(*(hMMH->HoldRegData+Address+i))&0xFF;		
				}
				crc=CRC16(hMMH->TxBuf,3+nWord*2);
				hMMH->TxBuf[3+nWord*2]=(crc>>8)&0xFF;
				hMMH->TxBuf[4+nWord*2]=crc&0xFF;
				hMMH->TxLength=5+nWord*2;		
			break;
			case 0x04:
				hMMH->TxBuf[2]=nWord*2;
				for(int i=0;i<nWord;i++)
				{
					hMMH->TxBuf[3+i*2]=(*(hMMH->InputRegData+Address+i)>>8)&0xFF;                        //读取数据
					hMMH->TxBuf[4+i*2]=(*(hMMH->InputRegData+Address+i))&0xFF;			
				}
				crc=CRC16(hMMH->TxBuf,3+nWord*2);
				hMMH->TxBuf[3+nWord*2]=(crc>>8)&0xFF;
				hMMH->TxBuf[4+nWord*2]=crc&0xFF;
				hMMH->TxLength=5+nWord*2;		
			break;			
			case 0x05:
				CoilData[Address]=(hMMH->RxBuf[4]||hMMH->RxBuf[5]);          //其中一个大于0就置1,否则置0
				for(int i=0;i<8;i++)                           //返回语句和接收完全一样
				{
					hMMH->TxBuf[i]=hMMH->RxBuf[i];
				}
				hMMH->TxLength=8;
			break;
			case 0x06:		
					*(hMMH->HoldRegData+Address)=hMMH->RxBuf[4]<<8|hMMH->RxBuf[5];               //写入数据
				for(int i=0;i<8;i++)                           //返回语句和接收完全一样
				{
					hMMH->TxBuf[i]=hMMH->RxBuf[i];
				}
				hMMH->TxLength=8;
			break;
			case 0x10:
				for(int i=0;i<nWord;i++)
				{
					*(hMMH->HoldRegData+Address+i)=hMMH->RxBuf[7+i*2]<<8|hMMH->RxBuf[8+i*2];               //写入数据
				}	
				for(int i=0;i<7;i++)                                                 //制作返回信息
				{
					hMMH->TxBuf[i]=hMMH->RxBuf[i];
				}
				crc=CRC16(hMMH->TxBuf,7);
				hMMH->TxBuf[7]=(crc>>8)&0xFF;
				hMMH->TxBuf[8]=crc&0xFF;
				hMMH->TxLength=9;	
			break;
		}	
	}
	else
	{
		hMMH->TxBuf[0]=hMMH->RxBuf[0];        //制作返回的错误信息
		hMMH->TxBuf[1]=hMMH->RxBuf[1]|0x80;
		hMMH->TxBuf[2]=Result;
		crc=CRC16(hMMH->TxBuf,3);
		hMMH->TxBuf[3]=(crc>>8)&0xFF;
		hMMH->TxBuf[4]=crc&0xFF;
		hMMH->TxLength=5;	
	}

Modbus_Transmit(hMMH->huart,hMMH->TxBuf,hMMH->TxLength,100);	
}
	
	
	
	
	
	



uint8_t Modbus_Master_TimeoutHandler(ModbusMasterHandler* hMMH)
{
	//判断串口是否返回5个字节的错误代码,通过CRC判断帧完整
	uint16_t crc=CRC16(hMMH->RxBuf,3);
	if(*(uint16_t*)(hMMH->RxBuf+3)==crc)//直接返回信息里的错误码
	{

		return hMMH->RxBuf[2];
	}

	return Err_Timeout;
}



uint8_t Modbus_Transmit(UART_HandleTypeDef* huart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout)
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
	uint8_t Result=HAL_UART_Transmit(huart,TxBuf,TxSize,Timeout);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
	return Result;
	//发送完成后返回0,否则返回非0
}
uint8_t Modbus_Receive(UART_HandleTypeDef* huart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout)
{
	
	return HAL_UART_Receive(huart,RxBuf,RxSize,Timeout);	
	//接收成功返回0,否则返回非0
}


main.c部分


Modbus_Slave_Init(&hMMHSlave,&huart2,1,100);
Modbus_Master_Init(&hMMHMaster,&huart2,100);
 while(1)
  {

	

	HAL_Delay(10);
//		HAL_UART_Transmit(&huart2,(uint8_t*)WriteBuf,4,100);
/*		
if(Modbus_Master_Req(&hMMHMaster,1,0x01,0x0000,2,(uint8_t*)&WriteBuf)==0x00)
{
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_11);

}
*/
	HAL_UART_Receive(&huart2,(uint8_t*)hMMHSlave.RxBuf,40,100);	
		if(hMMHSlave.RxBuf[0]>0)
		{
if(Modbus_Slave_Respond(&hMMHSlave,40)==0x00)	
		{
	HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_12);
}	
		}

这是自己写的程序,包括了Master及Slave的程序

为了方便移植,使用了阻塞式的uart通讯方式,移植时仅需要几步

modbus.h的头文件中,定义了modbus master几个主要变量

#define InputReg_Max_Addr 64
#define HoldReg_Max_Addr 64
#define Coil_MaxAddr 1
#define InputBit_Max_Addr 1
#define TxBufLength 40
#define RxBufLength 40

前四个是作为slave中的寄存器数量,寄存器建立在ModbusSlaveHandler;内部,只需要根据自己需要修改寄存器大小,及缓存大小,看自己用到的modbus命令的长度。

需要用户自定义两个阻塞式函数

uint8_t Modbus_Transmit(UART_HandleTypeDef* phuart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout);
uint8_t Modbus_Receive(UART_HandleTypeDef* phuart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout);

这里很简单地利用的cubemx配置的uart通讯,使用了HAL库的Transmit及Receive,如果是其他库,需要自己实现阻塞,因为master需要等待客户端返回才能完成通讯,所以阻塞是比较好的实现方式。

uint8_t Modbus_Transmit(UART_HandleTypeDef* huart,uint8_t * TxBuf,uint16_t TxSize,uint32_t Timeout)
{
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
    uint8_t Result=HAL_UART_Transmit(huart,TxBuf,TxSize,Timeout);
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
    return Result;
    //发送完成后返回0,否则返回非0
}

uint8_t Modbus_Receive(UART_HandleTypeDef* huart,uint8_t * RxBuf,uint16_t RxSize,uint32_t Timeout)
{
    
    return HAL_UART_Receive(huart,RxBuf,RxSize,Timeout);    
    //接收成功返回0,否则返回非0
}

第二步是,在初始化时,显示对Master或者Slave的初始化

放在while前面

第三步是在需要的时候调用master或者slave的处理函数

 当使用Slave前,用户的数据要事先存入结构体中,这个是当然的

需要程序的可以到这里下载

(1条消息) UART2_485.zip-嵌入式文档类资源-CSDN文库

版权声明:本文为CSDN博主「alwayslzz」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/alwayslzz/article/details/122331964

生成海报
点赞 0

alwayslzz

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32+W5500网络通信

1. W5500模块实现数据通信 引脚初始化,此连线方式用于三个模块功能的整体实现 PA3 -> W5500_RSTPA4 -> W5500_SCSPA5 -> W5500_SCKPA6 -> W5500_MISOPA7 ->

stm32f1xx-freemodbus-RTU 移植

STM32F103芯片的 freemodbus RTU的移植和使用。1 示例代码 代码示例上传在 gitee上,仓库地址为freemodbus移植示例 2 freemodbus介绍 Freemodbus是一个奥地利人写的Modb

pwm电机调速的原理介绍与代码实现

1、pwm实现调速的原理与介绍 PWM(Pulse Width Modulation)脉冲宽度调制。 1)占空比 pwm占空比就是一个脉冲周期内有效电平在整个周期所占的比例。 通过调节PWM的占空比就能调节IO口上电压的持续