sidhb 2025-05-16 17:09 采纳率: 0%
浏览 6

关于#python#的问题:print("CRC error: {recv_crc:} vs {calc_crc:}", recv_crc, calc_crc)


def modbus_crc(data: bytearray) -> int:
    """计算Modbus CRC16校验码"""
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for i in range(8):
            if (crc & 1) != 0:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return crc

def read_holding_registers():
    """读取保持寄存器"""
    try:
        # 构造请求帧
        req = bytearray([
            SLAVE_ADDR,         # 从机地址
            0x03,               # 功能码
            (START_REG >> 8) & 0xFF,    # 起始地址高字节
            START_REG & 0xFF,           # 起始地址低字节
            (REG_COUNT >> 8) & 0xFF,    # 寄存器数量高字节
            REG_COUNT & 0xFF            # 寄存器数量低字节
        ])
        
        # 计算并添加CRC
        crc = modbus_crc(req)
        req.append(crc & 0xFF)
        req.append((crc >> 8) & 0xFF)
        
        # 发送请求
        uart.write(req)
        
        # 等待响应(根据设备调整等待时间)
        utime.sleep_ms(20)
        resp = uart.read()
        
        if not resp:
            print("No response from device")
            return None
            
        # 校验响应
        if len(resp) < 5:
            print("Invalid response length")
            return None
            
        # 验证CRC
        recv_crc = (resp[-1] << 8) | resp[-2]
        calc_crc = modbus_crc(resp[:-2])
        if recv_crc != calc_crc:
            print("CRC error: {recv_crc:} vs {calc_crc:}", recv_crc, calc_crc)
            return None

为什么CRC校验失败,给出修改后的代码

  • 写回答

4条回答 默认 最新

  • 檀越@新空间 2025-05-16 17:10
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    问题分析

    在你的代码中,CRC校验失败可能有以下几个原因:

    1. 发送和接收的数据格式不一致

      • 在发送数据时,你已经计算了CRC并将其附加到请求帧中。
      • 但在接收数据时,你直接使用uart.read()读取整个响应,而没有正确解析响应的结构。
    2. 响应数据中可能包含额外的信息

      • Modbus协议规定,响应数据的前两个字节是功能码(0x03)和寄存器数量的字节数。
      • 如果响应中包含异常状态字节(例如错误状态),需要额外处理。
    3. CRC计算范围

      • 在计算CRC时,你只对响应数据的前部分进行了CRC计算。如果响应中包含额外信息(如异常状态字节),可能导致CRC校验失败。
    4. 调试输出格式问题

      • 打印CRC错误时,格式字符串中的占位符 {recv_crc:}{calc_crc:} 没有正确使用,导致输出不准确。

    修改后的代码

    以下是修复后的代码,解决了上述问题,并确保CRC校验逻辑正确。

    def read_holding_registers():
        """读取保持寄存器"""
        try:
            # 构造请求帧
            req = bytearray([
                SLAVE_ADDR,         # 从机地址
                0x03,               # 功能码
                (START_REG >> 8) & 0xFF,    # 起始地址高字节
                START_REG & 0xFF,           # 起始地址低字节
                (REG_COUNT >> 8) & 0xFF,    # 寄存器数量高字节
                REG_COUNT & 0xFF            # 寄存器数量低字节
            ])
            
            # 计算并添加CRC
            crc = modbus_crc(req)
            req.append(crc & 0xFF)
            req.append((crc >> 8) & 0xFF)
            
            # 发送请求
            uart.write(req)
            
            # 等待响应(根据设备调整等待时间)
            utime.sleep_ms(20)
            resp = uart.read()
            
            if not resp:
                print("No response from device")
                return None
                
            # 校验响应长度
            if len(resp) < 5:
                print("Invalid response length")
                return None
            
            # 解析响应
            # 响应格式:[slave_addr, function_code, byte_count, register_data..., crc_low, crc_high]
            slave_addr = resp[0]
            function_code = resp[1]
            byte_count = resp[2]
            
            # 验证功能码是否匹配
            if function_code != 0x03:
                print(f"Unexpected function code: {function_code}")
                return None
            
            # 提取寄存器数据
            register_data = resp[3:3 + byte_count]
            
            # 验证CRC
            recv_crc = (resp[-1] << 8) | resp[-2]
            calc_crc = modbus_crc(resp[:-2])
            
            if recv_crc != calc_crc:
                print(f"CRC error: received={recv_crc}, calculated={calc_crc}")
                return None
            
            # 返回寄存器数据
            return register_data
    
        except Exception as e:
            print(f"Error: {e}")
            return None
    

    修改点详解

    1. 解析响应数据

      • 响应的第一个字节是从机地址,第二个字节是功能码(0x03表示成功读取寄存器),第三个字节是寄存器数据的字节数。
      • 使用 resp[3:3 + byte_count] 提取寄存器数据部分。
    2. CRC校验范围

      • 在计算CRC时,只对响应数据的前部分(包括从机地址、功能码、寄存器数量和寄存器数据)进行计算。
      • 排除CRC校验字段本身(即最后两个字节)。
    3. 调试输出格式修正

      • 使用 f-string 格式化输出,确保CRC值显示正确。
    4. 异常处理

      • 增加了异常捕获,便于排查其他潜在问题。

    示例输出

    假设CRC校验失败,程序会打印类似以下内容:

    CRC error: received=4321, calculated=5678
    

    通过这种方式,可以更清晰地定位问题所在。


    总结

    通过以上修改,代码逻辑更加严谨,能够正确处理Modbus通信中的CRC校验问题。如果你仍有其他疑问,请提供更多上下文信息,例如硬件配置或具体错误日志,以便进一步分析。

    评论

报告相同问题?

问题事件

  • 创建了问题 5月16日