这里的程序的地址是基于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
暂无评论