前面文章里,我们需要实现通过modbus tcp传输传感器数据:

一个Modbus-TCP传感器数据传输方案
这里有基于freemodbus的详细实现:

代码功能分析

1. 函数参数和变量

eMBException eMBFuncReadHoldingRegister(UCHAR *pucFrame, USHORT *usLen)
  • pucFrame: 指向接收到的Modbus请求帧的指针
  • usLen: 指向帧长度的指针,函数会更新它为响应帧长度
  • 返回值: eMBException 异常状态码

2. 请求帧长度验证

if(*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN))
  • 检查请求帧长度是否正确
  • MB_PDU_FUNC_READ_SIZE = 4 (地址2字节 + 寄存器数量2字节)
  • MB_PDU_SIZE_MIN 是最小PDU长度

3. 解析请求参数

usRegAddress = (USHORT)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);
usRegAddress |= (USHORT)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);
usRegAddress++;

usRegCount = (USHORT)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8);
usRegCount |= (USHORT)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1]);

解析过程:

  1. 寄存器地址解析

    • 从请求帧中读取2字节的起始地址(大端格式)
    • usRegAddress++:Modbus协议中地址从1开始,转换为从0开始的内部地址
  2. 寄存器数量解析

    • 从请求帧中读取2字节的要读取的寄存器数量

4. 寄存器数量验证

if((usRegCount >= 1) && (usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX))
  • MB_PDU_FUNC_READ_REGCNT_MAX = 0x007D (125个寄存器)
  • 检查请求的寄存器数量是否在有效范围内(1-125)

5. 构建响应帧

pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;

*pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER;  // 功能码03
*usLen += 1;

*pucFrameCur++ = (UCHAR)(usRegCount * 2);        // 数据字节数
*usLen += 1;

响应帧结构:

[功能码(1B)] [字节数(1B)] [数据(n×2B)]

6. 调用回调函数获取数据

eRegStatus = eMBRegHoldingCB(pucFrameCur, usRegAddress, usRegCount, MB_REG_READ);

回调函数参数:

  • pucFrameCur: 响应数据缓冲区指针
  • usRegAddress: 起始寄存器地址
  • usRegCount: 寄存器数量
  • MB_REG_READ: 操作模式(读取)

7. 错误处理和长度更新

if(eRegStatus != MB_ENOERR) {
    eStatus = prveMBError2Exception(eRegStatus);
} else {
    *usLen += usRegCount * 2;  // 更新总响应长度
}

对于LoRa网关项目的具体应用

请求示例分析

假设主站请求读取40005开始的10个寄存器:

请求帧: 01 03 00 04 00 0A XX XX
解析结果:
- 起始地址: 0x0004 → 40005 (Modbus地址)
- 内部地址: 0x0004 → 需要转换为您的寄存器映射
- 寄存器数量: 0x000A → 10个寄存器

在您的项目中需要实现的关键部分

1. 寄存器地址映射转换:

// 在eMBRegHoldingCB回调函数中需要实现地址转换
USHORT internal_address = ConvertModbusToInternal(usAddress);

2. 数据打包逻辑:

eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, 
                            USHORT usAddress, 
                            USHORT usNRegs, 
                            eMBRegisterMode eMode)
{
    // 根据请求的地址范围返回对应的传感器数据
    if(usAddress >= REG_DATA_START && usAddress < REG_DATA_START + 516) {
        // 返回传感器数据
        for(int i = 0; i < usNRegs; i++) {
            USHORT reg_value = GetSensorRegisterValue(usAddress + i);
            pucRegBuffer[i*2] = (reg_value >> 8) & 0xFF;
            pucRegBuffer[i*2+1] = reg_value & 0xFF;
        }
        return MB_ENOERR;
    }
    return MB_ENOREG;
}

3. 分包处理:
由于最大只能读取125个寄存器(250字节),您的604字节数据需要分5次读取:

  • 包1: 40005-40129 (125寄存器)
  • 包2: 40130-40254 (125寄存器)
  • 包3: 40255-40379 (125寄存器)
  • 包4: 40380-40504 (125寄存器)
  • 包5: 40505-40520 (16寄存器)

这个函数是Modbus从站的核心处理逻辑,负责解析主站请求并返回对应的保持寄存器数据。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐