目录
- 1. Modbus 异常响应报文
- 1.1 功能码域
- 1.2 数据域
- 示例:异常响应结构
- 示例:异常响应功能码和异常码说明
- 2. Modbus 异常码处理说明
- 3. 异常码函数实现
- 4. 异常码处理的调用示例
- 示例:功能码解析时返回异常
- 示例:未实现的功能码处理
- 5. 主程序调用结构
- 6. 总结
- 7. 结束语
- 相关文章:
在 Modbus 协议中,异常码是用来表示错误状态的。每当接收到一个无效请求或在处理请求时发生错误,主机或从机需要返回一个异常响应。
1. Modbus 异常响应报文
在 Modbus 协议中,异常响应报文与正常响应报文在以下两个域中有所不同:
1.1 功能码域
- 正常响应:
在正常响应中,服务器利用功能码域来应答最初请求的功能码。
- 所有功能码的**最高有效位(MSB)**都为
0
(即它们的值小于十六进制0x80
)。
- 异常响应:
在异常响应中,服务器会将功能码的MSB 设置为1
。
- 这使得异常响应中的功能码值比正常响应中的功能码值高出十六进制
0x80
。
设置 MSB 的目的:
通过设置功能码的 MSB,客户端应用程序可以:
- 识别该响应是异常响应。
- 检测数据域中返回的异常码。
场景 | 功能码(十六进制) | MSB |
正常响应 |
|
|
异常响应 |
|
|
1.2 数据域
- 正常响应:
在正常响应中,服务器可以在数据域中返回数据或统计表(即客户端请求的内容)。 - 异常响应:
在异常响应中,服务器会在数据域中放置一个异常码。
- 该异常码定义了导致异常的服务器状态。
示例:异常响应结构
字段 | 长度(字节) | 描述 |
从机地址 | 1 | 响应的服务器(从机)的地址。 |
功能码 | 1 | 原始功能码 + |
异常码 | 1 | 表示具体错误的异常码。 |
CRC 校验码 | 2 | 用于校验数据正确性的 CRC 校验。 |
示例:异常响应功能码和异常码说明
- 异常功能码:
- 假设原始请求功能码为
0x06
(写单个寄存器),如果发生异常,则响应的功能码为0x86
。
2. Modbus 异常码处理说明
- 异常响应格式:
- 异常响应的功能码为请求功能码的高位加上
0x80
。 - 数据域只包含一个字节,表示异常码。
- 常见 Modbus 异常码:
异常码 | 名称 | 含义 |
0x01 | 非法功能 | 功能码不被支持 |
0x02 | 非法数据地址 | 地址超出范围 |
0x03 | 非法数据值 | 数据值无效 |
0x04 | 从机设备故障 | 从机设备发生不可恢复的错误 |
0x05 | 确认 | 请求正在处理 |
0x06 | 从机忙 | 从机忙于处理长操作,无法接受新请求 |
0x08 | 内存奇偶校验错误 | 存储设备存在错误 |
0x0A | 网关路径不可用 | 网关无法路由请求 |
0x0B | 网关目标设备响应失败 | 网关未接收到目标设备的响应 |
- 实现 Modbus 异常码返回函数:
- 该函数根据请求的功能码和异常码生成完整的异常帧。
3. 异常码函数实现
/**
* @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. 异常码处理的调用示例
在解析接收到的数据帧时,根据功能码或数据有效性,调用异常码处理函数返回异常响应。
示例:功能码解析时返回异常
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;
}
// 正常处理逻辑...
}
示例:未实现的功能码处理
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. 主程序调用结构
主循环中将接收的数据解析并按需返回异常:
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
异常码处理有了更深入的理解和认识。- 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持!点我关注❤️