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方式 )的主要内容,如果未能解决你的问题,请参考以下文章

关于modbus_rtu

物联网RTU(Modbus TCP协议)Java接口开发及Modbus Slave仿真使用

ModBus(RTU TCP UDP通信)及利用socket通信(DTU)实现Modbus-RTU通信协议

VB Modbus RTU CRC 校验

MODBUS RTU协议原理及功能码解析

modbus rtu协议介绍及开发