Modbus adu 关键代码(支持 RTU及ASCII方式 )
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Modbus adu 关键代码(支持 RTU及ASCII方式 )相关的知识,希望对你有一定的参考价值。
参考技术A ```/**
封装ADU数据
@param const MbFrameType frameType 帧类型
@param const unsigned char devAddr 设备地址
@param const unsigned char funCode 功能码
@param const unsigned char* pData 数据
@param const nsigned char dataLen 数据长度
@param unsigned char* pOutAduData 封装ADU数据
@return pOutAduData据的长度
**/
unsigned char packMbAduData(const MbFrameType frameType, const unsigned char devAddr, const unsigned char* pData,const unsigned char dataLen, unsigned char* pOutAduData)
if (frameType == FrameRtu)
return packMbRtuAduData(devAddr, pData, dataLen, pOutAduData);
else if (frameType == FrameAscii)
return packMbAsciiAduData(devAddr, pData, dataLen, pOutAduData);
else
return 0;
/**
封装基于RTU的PDU数据
@param const unsigned char devAddr 设备地址
@param const unsigned char funCode 功能码
@param const unsigned char* pData 数据
@param const nsigned char dataLen 数据长度
@param unsigned char* pOutRtuData 封装的RTU PDU数据
@return pOutRtuData据的长度
**/
unsigned char packMbRtuAduData(const unsigned char devAddr, const unsigned char* pData,const unsigned char dataLen, unsigned char* pOutRtuData)
unsigned short crc = 0;
unsigned char req_pdu_len = packMbRtuPduData(devAddr, pData, dataLen, pOutRtuData);
if (req_pdu_len > 0)
crc = calcMbRtuPduCrc(pOutRtuData, req_pdu_len);
pOutRtuData[req_pdu_len++] = (crc & 0x00FF);
pOutRtuData[req_pdu_len++] = (crc & 0xFF00) >> 8;
return req_pdu_len;
/**
封装基于ASCII的PDU数据
@param const unsigned char devAddr 设备地址
@param const unsigned char* pData 数据
@param const nsigned char dataLen 数据长度
@param unsigned char* pOutAsciiCmd 封装报的ASCII PDU数据
@return pOutAsciiCmd据的长度
**/
unsigned char packMbAsciiAduData(const unsigned char devAddr, const unsigned char* pData, const unsigned char dataLen, unsigned char* pOutAsciiCmd)
unsigned short i = 0, j = 0, lrc = 0;
unsigned char fl;
char sz[3];
unsigned char req_pdu_len = 0;
unsigned char req_pdu_bin[PDU_MAX_LEN];
req_pdu_len = packMbRtuPduData(devAddr, pData, dataLen, req_pdu_bin);
if (req_pdu_len > 0)
pOutAsciiCmd[j++] = ':';
for (i = 0; i < req_pdu_len; i++)
fl = req_pdu_bin[i];
sprintf(sz, "%02X", fl);
pOutAsciiCmd[j++] = sz[0];
pOutAsciiCmd[j++] = sz[1];
lrc = calcMbAsciiPduLrc(req_pdu_bin, req_pdu_len);
sprintf(sz, "%02X", lrc);
pOutAsciiCmd[j++] = sz[0];
pOutAsciiCmd[j++] = sz[1];
pOutAsciiCmd[j++] = '\r';
pOutAsciiCmd[j++] = '\n';
return j;
/**
基于ASCII的PDU数据转为RTU Pdu数据
@param const unsigned char* pAsciiPdu ASCII的PDU数据
@param const unsigned char asciiPduLen ASCII的PDU数据的长度
@param unsigned char* pRtuPduData 转换后的RTU Pdu数据
@return RTU Pdu数据长度
**/
unsigned char MbAsciiPdu2RtuPdu(const unsigned char* pAsciiPdu, unsigned char asciiPduLen, unsigned char* pRtuPduData)
unsigned char j = 0;
unsigned char i = 0;
char sz[2];
int dv;
unsigned fc = 0;
// 取出地址和功能码
if (asciiPduLen >= 4)
sscanf((char*)pAsciiPdu + 0, "%2s", sz);
sscanf(sz, "%x", &dv);
pRtuPduData[j++] = dv;
sscanf((char*)pAsciiPdu + 2, "%2s", sz);
sscanf(sz, "%x", &dv);
fc = dv;
pRtuPduData[j++] = dv;
// asciiPduLen - 5: 去掉‘:’和LRC及\r\n的长度;i += 2:ASCII协议中每两字节对应RTU中的一个字节
for (i = 4; i < asciiPduLen - 5; i += 2)
sscanf((char*)pAsciiPdu + i, "%2s", sz);
sscanf(sz, "%x", &dv);
if (fc == ReadHoldingRegister)
dv /= 2;
pRtuPduData[j++] = dv;
return j;
/**
以下三个方法用来校验响应数据
@param const MbFrameType frameType 帧类型
@param const unsigned char* pRspData ASCII的PDU数据
@param const unsigned char rspDataLen ASCII的PDU数据的长度
@return 成功返回0,否则返回>1
**/
unsigned char checkMbResponseData(const MbFrameType frameType, const unsigned char* pRspData, const unsigned char rspDataLen)
if (frameType == FrameAscii)
return checkMbResponseAsciiData(pRspData, rspDataLen);
else if (frameType == FrameRtu)
return checkMbResponseRtuData(pRspData, rspDataLen);
else
return UnknowFrameType;
unsigned char checkMbResponseAsciiData(const unsigned char* pRspData, const unsigned char rspDataLen)
char sz[2];
char i, fc, len;
int dv;
unsigned char lrc = 0, rsp_lcr = 0;
if (!pRspData || rspDataLen < 3) return ParamError;
sscanf((char*)pRspData + 3, "%2s", sz);
sscanf(sz, "%x", &dv);
fc = dv;
sscanf((char*)pRspData + 5, "%2s", sz);
sscanf(sz, "%x", &dv);
len = dv;
// 响应数据中的LRC
if (fc >= ReadCoilStatus && fc <= ReadInputRegister) // 功能码0x01 ---- 0x04
sscanf((char*)pRspData + 7 + len, "%2s", sz);
sscanf(sz, "%x", &dv);
rsp_lcr = dv;
else if (fc >= WriteSingleCoil && fc <= WriteSingleRegister) // 功能码0x05---0x06
sscanf((char*)pRspData + 14, "%2s", sz);
sscanf(sz, "%x", &dv);
rsp_lcr = dv;
else if (fc >= MaskWriteRegister) // 功能码0x16
sscanf((char*)pRspData + 18, "%2s", sz);
sscanf(sz, "%x", &dv);
rsp_lcr = dv;
else
return InvalidFunctionCode;
// 重新计算LRC
for (i = 1; i < rspDataLen - 5; i += 2)
sscanf((char*)pRspData + i, "%2s", sz);
sscanf(sz, "%x", &dv);
lrc += dv;
lrc = (~lrc) + 1;
//printf("---fc:%d,len:%dlrc:%02X %0X----", fc, len, lrc, rsp_lcr);
if (lrc == rsp_lcr) return MB_OK;
return MemoryParityError;
unsigned char checkMbResponseRtuData(const unsigned char* pRspData, const unsigned char rspDataLen)
char fc, len;
unsigned short int crc, rsp_rcr;
if (!pRspData || rspDataLen < 3) return ParamError;
fc = pRspData[1];
len = pRspData[2];
// 响应数据中的CRC
if (fc >= ReadCoilStatus && fc <= ReadInputRegister) // 功能码0x01 ---- 0x04
rsp_rcr = (pRspData[3 + len] << 8) + pRspData[4 + len];
else if (fc >= WriteSingleCoil && fc <= WriteSingleRegister) // 功能码0x05---0x06
rsp_rcr = (pRspData[6] << 8) + pRspData[7];
else if (fc >= MaskWriteRegister) // 功能码0x16
rsp_rcr = (pRspData[8] << 8) + pRspData[9];
else
return InvalidFunctionCode;
// 重新计算的CRC
crc = calcMbRtuPduCrc(pRspData, rspDataLen - 2);
crc = u16ToBigEndian(crc);
if (rsp_rcr == crc) return MB_OK;
return MemoryParityError;
/**
指低地址存放最高有效字节
**/
unsigned short u16ToBigEndian(unsigned short v)
return ((v & 0xFF00) >> 8) + ((v & 0x00FF) << 8);
/**
指高地址存放最低有效字节
**/
unsigned short u16ToLittleEndian(unsigned short v)
return ((v & 0xFF00) >> 8) + ((v & 0x00FF) << 8);
/**
以5个方法RS485通信数据转STM32数据
**/
short int toHostInt16(unsigned char *pv)
unsigned short int v = (pv[1] << 8) + pv[0];
return (short int)*((short int*)&v);
int toHostInt32(unsigned char *pv)
unsigned int v = ((pv[2] << 24) + (pv[3] << 16) + (pv[0] << 8) + pv[1]);
return (int)*((int*)&v);
__int64 toHostInt64(unsigned char *pv)
unsigned __int64 v = (((__int64)pv[4] << 56) + ((__int64)pv[5] << 48) + ((__int64)pv[6] << 40) + ((__int64)pv[7] << 32) + //hight bits
(pv[0] << 24) + (pv[1] << 16) + (pv[2] << 8) + pv[3]); //low bits
return (__int64)*((__int64*)&v);
float toHostFloat32(unsigned char *pv)
unsigned int v = ((pv[2] << 24) + (pv[3] << 16) + (pv[0] << 8) + pv[1]);
return (float)*((float*)&v);
double toHostFloat64(unsigned char *pv)
unsigned __int64 v = (((__int64)pv[4] << 56) + ((__int64)pv[5] << 48) + ((__int64)pv[6] << 40) + ((__int64)pv[7] << 32) + //hight bits
(pv[0] << 24) + (pv[1] << 16) + (pv[2] << 8) + pv[3]); //low bits
return (double)*((double*)&v);
```
关于modbus_rtu
一/数据帧的分析,1写数据2读数据
二/帧的模型,adu与pdu 应用单元与协议单元
三/数据类型与对应的功能码
功能码02操作只读的位(输入引脚)
功能码01 05操作读写位(输出引脚)
功能码03 06操作读写字(比如设定的时间或温度)
功能码04操作只读的字(比如检测的温度/温度什么的)
四/mcu的接收缓存处理过程。
以上是关于Modbus adu 关键代码(支持 RTU及ASCII方式 )的主要内容,如果未能解决你的问题,请参考以下文章
物联网RTU(Modbus TCP协议)Java接口开发及Modbus Slave仿真使用