前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【嵌入式】Modbus协议异常码函数 - 原理及C语言实现

【嵌入式】Modbus协议异常码函数 - 原理及C语言实现

作者头像
LuckiBit
发布2025-01-20 19:47:33
发布2025-01-20 19:47:33
26900
代码可运行
举报
文章被收录于专栏:C语言C语言
运行总次数:0
代码可运行
在 Modbus 协议中,异常码是用来表示错误状态的。每当接收到一个无效请求或在处理请求时发生错误,主机或从机需要返回一个异常响应。

1. Modbus 异常响应报文

在 Modbus 协议中,异常响应报文与正常响应报文在以下两个域中有所不同:

1.1 功能码域
  • 正常响应: 在正常响应中,服务器利用功能码域来应答最初请求的功能码。
    • 所有功能码的**最高有效位(MSB)**都为 0(即它们的值小于十六进制 0x80)。
  • 异常响应: 在异常响应中,服务器会将功能码的MSB 设置为 1
    • 这使得异常响应中的功能码值比正常响应中的功能码值高出十六进制 0x80

设置 MSB 的目的: 通过设置功能码的 MSB,客户端应用程序可以:

  • 识别该响应是异常响应。
  • 检测数据域中返回的异常码。

场景

功能码(十六进制)

MSB

正常响应

0x03(例如:读取保持寄存器)

0

异常响应

0x83

1

1.2 数据域
  • 正常响应: 在正常响应中,服务器可以在数据域中返回数据或统计表(即客户端请求的内容)。
  • 异常响应: 在异常响应中,服务器会在数据域中放置一个异常码
    • 该异常码定义了导致异常的服务器状态。
示例:异常响应结构

字段

长度(字节)

描述

从机地址

1

响应的服务器(从机)的地址。

功能码

1

原始功能码 + 0x80(MSB 设置为 1)。

异常码

1

表示具体错误的异常码。

CRC 校验码

2

用于校验数据正确性的 CRC 校验。

示例:异常响应功能码和异常码说明
  1. 异常功能码:
    • 假设原始请求功能码为 0x06(写单个寄存器),如果发生异常,则响应的功能码为 0x86

2. Modbus 异常码处理说明

  1. 异常响应格式
    • 异常响应的功能码为请求功能码的高位加上 0x80
    • 数据域只包含一个字节,表示异常码。
  2. 常见 Modbus 异常码: 异常码名称含义0x01非法功能功能码不被支持0x02非法数据地址地址超出范围0x03非法数据值数据值无效0x04从机设备故障从机设备发生不可恢复的错误0x05确认请求正在处理0x06从机忙从机忙于处理长操作,无法接受新请求0x08内存奇偶校验错误存储设备存在错误0x0A网关路径不可用网关无法路由请求0x0B网关目标设备响应失败网关未接收到目标设备的响应
  3. 实现 Modbus 异常码返回函数
    • 该函数根据请求的功能码和异常码生成完整的异常帧。

3. 异常码函数实现

代码语言:javascript
代码运行次数:0
运行
复制
/**
 * @brief 生成 Modbus 异常响应帧
 * @param buffer 存储异常响应帧的缓冲区
 * @param function_code 请求的功能码
 * @param exception_code 异常码
 * @return 返回异常响应帧的长度
 */
unsigned char modbus_exception_response(unsigned char *buffer, unsigned char function_code, unsigned char exception_code)
{
    unsigned int crc;

    buffer[0] = modbus.slave_address;              // 从机地址
    buffer[1] = function_code | 0x80;             // 异常功能码
    buffer[2] = exception_code;                   // 异常码

    crc = crc16(buffer, 3);                       // 计算 CRC 校验
    buffer[3] = crc & 0xFF;                       // CRC 低字节
    buffer[4] = (crc >> 8) & 0xFF;                // CRC 高字节

    return 5;                                     // 返回帧长度
}

4. 异常码处理的调用示例

在解析接收到的数据帧时,根据功能码或数据有效性,调用异常码处理函数返回异常响应。

示例:功能码解析时返回异常
代码语言:javascript
代码运行次数:0
运行
复制
void modbus03_handler(const unsigned char *request, unsigned char length)
{
    unsigned char response[256];
    unsigned char response_length;

    if (length < 6)  // 请求长度不正确
    {
        response_length = modbus_exception_response(response, 0x03, 0x03);  // 非法数据值
        uart_send_frame(response, response_length);  // 发送异常响应
        return;
    }

    // 数据解析和处理逻辑...

    // 如果某些数据超出范围
    if (invalid_data)
    {
        response_length = modbus_exception_response(response, 0x03, 0x02);  // 非法数据地址
        uart_send_frame(response, response_length);
        return;
    }

    // 正常处理逻辑...
}
示例:未实现的功能码处理
代码语言:javascript
代码运行次数:0
运行
复制
void modbus_function_handler(unsigned char function_code, const unsigned char *request, unsigned char length)
{
    unsigned char response[256];
    unsigned char response_length;

    switch (function_code)
    {
        case 0x03:
            modbus03_handler(request, length);
            break;
        case 0x06:
            modbus06_handler(request, length);
            break;
        case 0x10:
            modbus10_handler(request, length);
            break;
        default:  // 非法功能码
            response_length = modbus_exception_response(response, function_code, 0x01);  // 非法功能
            uart_send_frame(response, response_length);
            break;
    }
}

5. 主程序调用结构

主循环中将接收的数据解析并按需返回异常:

代码语言:javascript
代码运行次数:0
运行
复制
void process_uart_data(void)
{
    if (rx_complete)
    {
        rx_complete = 0;  // 清除标志位

        if (rx_index < 2)  // 数据帧长度不足
        {
            unsigned char response[256];
            unsigned char response_length = modbus_exception_response(response, 0x00, 0x03);  // 非法数据值
            uart_send_frame(response, response_length);
            return;
        }

        unsigned char function_code = rx_buffer[1];
        modbus_function_handler(function_code, rx_buffer, rx_index);

        rx_index = 0;  // 清空缓冲区
    }
}

6. 总结

  • 扩展性:异常码函数可以直接调用,适用于所有功能码。
  • 模块化:每个功能码独立封装,方便维护和扩展。
  • 标准化:按照 Modbus 协议定义,异常响应帧格式清晰。
  • 稳定性:接收到错误请求时,从机能快速返回明确的异常信息,提高系统稳定性和容错性。

通过这种实现,Modbus 协议的功能和异常处理更清晰规范,满足嵌入式设备的实际需求。

7. 结束语

本节内容已经全部介绍完毕,希望通过这篇文章,大家对 Modbus 异常码处理有了更深入的理解和认识。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Modbus 异常响应报文
    • 1.1 功能码域
    • 1.2 数据域
    • 示例:异常响应结构
    • 示例:异常响应功能码和异常码说明
  • 2. Modbus 异常码处理说明
  • 3. 异常码函数实现
  • 4. 异常码处理的调用示例
    • 示例:功能码解析时返回异常
    • 示例:未实现的功能码处理
  • 5. 主程序调用结构
  • 6. 总结
  • 7. 结束语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档