实现一个基于 STM32 HAL库 ucosiii的Modbus-RTU Master 主机C语言程序代码,提供完整的C和H文件,能够与从机设备进行通信,
功能码实现:
- **0x03**:读取保持寄存器。
- **0x06**:写入单个寄存器。
- **0x10**:写入多个寄存器。
- 其他功能码(如 0x01、0x02、0x04 等)。
Modbus 协议实现包含帧构建、发送、接收、解析等功能。
超时处理:设置通信超时机制,防止死锁。
错误处理:处理从机返回的错误码。在源码里加上详细注释
/* modbus_master.h */
#ifndef __MODBUS_MASTER_H
#define __MODBUS_MASTER_H
#include "stm32f1xx_hal.h"
#include "os.h"
// Modbus功能码定义
#define READ_COILS 0x01
#define READ_DISCRETE_INPUTS 0x02
#define READ_HOLDING_REG 0x03
#define READ_INPUT_REG 0x04
#define WRITE_SINGLE_COIL 0x05
#define WRITE_SINGLE_REG 0x06
#define WRITE_MULTI_REG 0x10
// 错误码定义
#define MB_SUCCESS 0
#define MB_TIMEOUT_ERR -1
#define MB_CRC_ERR -2
#define MB_FUNC_ERR -3
// Modbus主站结构体
typedef struct {
UART_HandleTypeDef *huart; // 使用的串口句柄
uint32_t timeout; // 超时时间(ms)
uint8_t retry_count; // 重试次数
OS_SEM rx_sem; // 接收信号量
OS_MUTEX uart_mutex; // 串口互斥锁
} ModbusMaster;
// 函数声明
void ModbusMaster_Init(ModbusMaster *mb, UART_HandleTypeDef *huart);
int8_t ReadHoldingRegisters(ModbusMaster *mb, uint8_t slave_addr,
uint16_t start_addr, uint16_t reg_num,
uint16_t *data);
int8_t WriteSingleRegister(ModbusMaster *mb, uint8_t slave_addr,
uint16_t reg_addr, uint16_t value);
int8_t WriteMultipleRegisters(ModbusMaster *mb, uint8_t slave_addr,
uint16_t start_addr, uint16_t reg_num,
uint16_t *values);
#endif /* __MODBUS_MASTER_H */
/* modbus_master.c */
#include "modbus_master.h"
#include <string.h>
// 私有函数声明
static uint16_t CRC16(uint8_t *buf, uint16_t len);
static void BuildRequestFrame(uint8_t *frame, uint8_t slave_addr,
uint8_t func_code, uint16_t addr,
uint16_t data);
static int8_t SendRequest(ModbusMaster *mb, uint8_t *frame, uint8_t len);
static int8_t ReceiveResponse(ModbusMaster *mb, uint8_t *resp,
uint8_t expected_len);
// 初始化Modbus主站
void ModbusMaster_Init(ModbusMaster *mb, UART_HandleTypeDef *huart) {
mb->huart = huart;
mb->timeout = 1000; // 默认超时1秒
mb->retry_count = 3;
OSSemCreate(&mb->rx_sem, "MB Rx Sem", 0);
OSMutexCreate(&mb->uart_mutex, "MB UART Mutex", OS_ERR_NONE);
}
// 读取保持寄存器(功能码0x03)
int8_t ReadHoldingRegisters(ModbusMaster *mb, uint8_t slave_addr,
uint16_t start_addr, uint16_t reg_num,
uint16_t *data) {
uint8_t frame[8];
uint8_t resp[256];
int8_t result = MB_TIMEOUT_ERR;
// 构建请求帧
BuildRequestFrame(frame, slave_addr, READ_HOLDING_REG, start_addr, reg_num);
OSMutexPend(&mb->uart_mutex, 0, OS_OPT_PEND_BLOCKING, NULL);
for(uint8_t retry = 0; retry < mb->retry_count; retry++) {
// 发送请求
if(SendRequest(mb, frame, 8) != HAL_OK) continue;
// 接收响应(2*reg_num + 5字节)
uint8_t expected_len = 5 + 2 * reg_num;
if(ReceiveResponse(mb, resp, expected_len) == MB_SUCCESS) {
// 校验响应
if(resp[1] == READ_HOLDING_REG) {
memcpy(data, &resp[3], 2 * reg_num);
result = MB_SUCCESS;
break;
} else if(resp[1] & 0x80) { // 错误响应
result = MB_FUNC_ERR;
break;
}
}
}
OSMutexPost(&mb->uart_mutex, OS_OPT_POST_NONE);
return result;
}
// 发送请求帧(带CRC校验)
static int8_t SendRequest(ModbusMaster *mb, uint8_t *frame, uint8_t len) {
uint16_t crc = CRC16(frame, len - 2);
frame[len - 2] = crc & 0xFF;
frame[len - 1] = crc >> 8;
HAL_UART_AbortReceive(mb->huart);
return HAL_UART_Transmit(mb->huart, frame, len, mb->timeout);
}
// 接收响应(带超时处理)
static int8_t ReceiveResponse(ModbusMaster *mb, uint8_t *resp, uint8_t expected_len) {
uint8_t rx_len = 0;
uint32_t start_time = HAL_GetTick();
HAL_UART_Receive_IT(mb->huart, resp, expected_len);
while((HAL_GetTick() - start_time) < mb->timeout) {
if(__HAL_UART_GET_FLAG(mb->huart, UART_FLAG_RXNE)) {
rx_len = mb->huart->RxXferSize - mb->huart->RxXferCount;
if(rx_len >= expected_len) break;
}
OSTimeDlyHMSM(0, 0, 0, 10); // UCOSIII延时10ms
}
if(rx_len < expected_len) return MB_TIMEOUT_ERR;
// CRC校验
uint16_t crc = CRC16(resp, expected_len - 2);
if((resp[expected_len-2] != (crc & 0xFF)) ||
(resp[expected_len-1] != (crc >> 8))) {
return MB_CRC_ERR;
}
return MB_SUCCESS;
}
// CRC16计算(MODBUS-RTU标准)
static uint16_t CRC16(uint8_t *buf, uint16_t len) {
uint16_t crc = 0xFFFF;
for(uint16_t pos = 0; pos < len; pos++) {
crc ^= (uint16_t)buf[pos];
for(uint8_t i = 8; i != 0; i--) {
if((crc & 0x0001) != 0) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
// UCOSIII任务示例
void ModbusTask(void *p_arg) {
ModbusMaster mb;
uint16_t holding_regs[10];
ModbusMaster_Init(&mb, &huart1);
while(1) {
// 读取从站1的保持寄存器40001-40010
int8_t result = ReadHoldingRegisters(&mb, 0x01, 0x0000, 10, holding_regs);
if(result == MB_SUCCESS) {
// 处理数据...
} else {
// 错误处理...
}
OSTimeDlyHMSM(0, 0, 1, 0); // 每秒执行一次
}
}
第二版本代码 克劳德3.7编写
基于HAL UCOSIII
/**
******************************************************************************
* @file modbus_master.h
* @author Claude AI
* @brief Modbus RTU Master mode header file
* @details 基于STM32 HAL库和uCOS-III的Modbus-RTU主机实现
******************************************************************************
*/
#ifndef __MODBUS_MASTER_H
#define __MODBUS_MASTER_H
#ifdef __cplusplus
extern "C" {
#endif
/* 包含所需头文件 */
#include "stm32f4xx_hal.h"
#include "os.h"
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
/* Modbus 功能码定义 */
#define MODBUS_READ_COILS 0x01 // 读取线圈状态
#define MODBUS_READ_DISCRETE_INPUTS 0x02 // 读取离散输入状态
#define MODBUS_READ_HOLDING_REGISTERS 0x03 // 读取保持寄存器
#define MODBUS_READ_INPUT_REGISTERS 0x04 // 读取输入寄存器
#define MODBUS_WRITE_SINGLE_COIL 0x05 // 写单个线圈
#define MODBUS_WRITE_SINGLE_REGISTER 0x06 // 写单个寄存器
#define MODBUS_WRITE_MULTIPLE_COILS 0x0F // 写多个线圈
#define MODBUS_WRITE_MULTIPLE_REGISTERS 0x10 // 写多个寄存器
/* Modbus 异常码定义 */
#define MODBUS_EXCEPTION_ILLEGAL_FUNCTION 0x01
#define MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS 0x02
#define MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE 0x03
#define MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE 0x04
#define MODBUS_EXCEPTION_ACKNOWLEDGE 0x05
#define MODBUS_EXCEPTION_SLAVE_DEVICE_BUSY 0x06
#define MODBUS_EXCEPTION_MEMORY_PARITY_ERROR 0x08
#define MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE 0x0A
#define MODBUS_EXCEPTION_GATEWAY_TARGET_FAILED 0x0B
/* Modbus 参数配置 */
#define MODBUS_MAX_SLAVE_NUM 10 // 最大从机数量
#define MODBUS_MAX_PDU_LENGTH 253 // 最大PDU长度
#define MODBUS_MAX_ADU_LENGTH 256 // 最大ADU长度 (PDU + 地址 + CRC)
#define MODBUS_DEFAULT_TIMEOUT 1000 // 默认超时时间(ms)
/* Modbus 主机状态定义 */
typedef enum {
MODBUS_MASTER_STATE_IDLE, // 空闲状态
MODBUS_MASTER_STATE_WAIT_REPLY, // 等待从机回复
MODBUS_MASTER_STATE_PROCESS // 处理接收的数据
} ModbusMasterState;
/* Modbus 错误码定义 */
typedef enum {
MODBUS_MASTER_OK = 0, // 操作成功
MODBUS_MASTER_ERROR_TIMEOUT, // 通信超时
MODBUS_MASTER_ERROR_CRC, // CRC校验错误
MODBUS_MASTER_ERROR_EXCEPTION, // 从机返回异常
MODBUS_MASTER_ERROR_INVALID_SLAVE_ID, // 无效的从机ID
MODBUS_MASTER_ERROR_INVALID_RESPONSE, // 无效的从机响应
MODBUS_MASTER_ERROR_BUSY // 主机忙,无法处理新请求
} ModbusMasterError;
/* Modbus 主机配置 */
typedef struct {
UART_HandleTypeDef *huart; // 串口句柄
uint32_t timeout; // 通信超时时间(ms)
OS_SEM rxSem; // 接收信号量
OS_MUTEX mutex; // 互斥信号量
ModbusMasterState state; // 当前状态
uint8_t rxBuffer[MODBUS_MAX_ADU_LENGTH]; // 接收缓冲区
uint16_t rxCount; // 接收计数
uint8_t txBuffer[MODBUS_MAX_ADU_LENGTH]; // 发送缓冲区
uint8_t lastSlaveID; // 上一次通信的从机ID
uint8_t lastFunctionCode; // 上一次发送的功能码
ModbusMasterError lastError; // 最后一次错误
uint8_t expectLength; // 预期接收长度
OS_TMR timeoutTimer; // 超时定时器
} ModbusMaster;
/* 函数原型 */
/**
* @brief 初始化Modbus主机
* @param mbus: Modbus主机结构体指针
* @param huart: 使用的串口句柄
* @param timeout: 通信超时时间(ms)
* @retval 错误码
*/
ModbusMasterError ModbusMaster_Init(ModbusMaster *mbus, UART_HandleTypeDef *huart, uint32_t timeout);
/**
* @brief 读取线圈状态 (功能码0x01)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 线圈数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadCoils(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint8_t *data);
/**
* @brief 读取离散输入状态 (功能码0x02)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 输入数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadDiscreteInputs(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint8_t *data);
/**
* @brief 读取保持寄存器 (功能码0x03)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 寄存器数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadHoldingRegisters(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint16_t *data);
/**
* @brief 读取输入寄存器 (功能码0x04)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 寄存器数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadInputRegisters(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint16_t *data);
/**
* @brief 写单个线圈 (功能码0x05)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param coilAddr: 线圈地址
* @param value: 线圈值 (0=OFF, 非0=ON)
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteSingleCoil(ModbusMaster *mbus, uint8_t slaveID, uint16_t coilAddr, bool value);
/**
* @brief 写单个寄存器 (功能码0x06)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param regAddr: 寄存器地址
* @param value: 寄存器值
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteSingleRegister(ModbusMaster *mbus, uint8_t slaveID, uint16_t regAddr, uint16_t value);
/**
* @brief 写多个线圈 (功能码0x0F)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 线圈数量
* @param data: 线圈值数组
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteMultipleCoils(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, const uint8_t *data);
/**
* @brief 写多个寄存器 (功能码0x10)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 寄存器数量
* @param data: 寄存器值数组
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteMultipleRegisters(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, const uint16_t *data);
/**
* @brief 获取错误描述
* @param error: 错误码
* @retval 错误描述字符串
*/
const char* ModbusMaster_GetErrorString(ModbusMasterError error);
/**
* @brief 设置通信超时时间
* @param mbus: Modbus主机结构体指针
* @param timeout: 超时时间(ms)
* @retval None
*/
void ModbusMaster_SetTimeout(ModbusMaster *mbus, uint32_t timeout);
/**
* @brief UART接收回调函数,应在HAL_UART_RxCpltCallback中调用
* @param mbus: Modbus主机结构体指针
* @param huart: 串口句柄
* @retval None
*/
void ModbusMaster_UartRxCpltCallback(ModbusMaster *mbus, UART_HandleTypeDef *huart);
/**
* @brief 超时回调函数,内部使用
* @param p_tmr: 定时器指针
* @param p_arg: 参数指针 (ModbusMaster结构体)
* @retval None
*/
void ModbusMaster_TimeoutCallback(OS_TMR *p_tmr, void *p_arg);
#ifdef __cplusplus
}
#endif
#endif /* __MODBUS_MASTER_H */
/**
******************************************************************************
* @file modbus_master.c
* @author Claude AI
* @brief Modbus RTU Master mode implementation
* @details 基于STM32 HAL库和uCOS-III的Modbus-RTU主机实现
******************************************************************************
*/
#include "modbus_master.h"
/* 私有函数声明 */
static uint16_t ModbusMaster_CRC16(const uint8_t *buffer, uint16_t length);
static ModbusMasterError ModbusMaster_SendRequest(ModbusMaster *mbus, uint8_t slaveID, uint8_t functionCode,
uint8_t *data, uint16_t dataLen, uint16_t expectedLen);
static ModbusMasterError ModbusMaster_ProcessResponse(ModbusMaster *mbus);
/**
* @brief 计算Modbus CRC16校验
* @param buffer: 数据缓冲区
* @param length: 数据长度
* @retval CRC校验值
*/
static uint16_t ModbusMaster_CRC16(const uint8_t *buffer, uint16_t length)
{
uint16_t crc = 0xFFFF;
uint16_t i, j;
for (i = 0; i < length; i++) {
crc ^= buffer[i];
for (j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
/**
* @brief 初始化Modbus主机
* @param mbus: Modbus主机结构体指针
* @param huart: 使用的串口句柄
* @param timeout: 通信超时时间(ms)
* @retval 错误码
*/
ModbusMasterError ModbusMaster_Init(ModbusMaster *mbus, UART_HandleTypeDef *huart, uint32_t timeout)
{
OS_ERR err;
/* 参数检查 */
if (mbus == NULL || huart == NULL) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 初始化结构体 */
memset(mbus, 0, sizeof(ModbusMaster));
mbus->huart = huart;
mbus->timeout = (timeout > 0) ? timeout : MODBUS_DEFAULT_TIMEOUT;
mbus->state = MODBUS_MASTER_STATE_IDLE;
mbus->lastError = MODBUS_MASTER_OK;
/* 创建信号量 */
OSSemCreate(&mbus->rxSem, "Modbus RX Sem", 0, &err);
if (err != OS_ERR_NONE) {
return MODBUS_MASTER_ERROR_BUSY;
}
/* 创建互斥信号量 */
OSMutexCreate(&mbus->mutex, "Modbus Mutex", &err);
if (err != OS_ERR_NONE) {
return MODBUS_MASTER_ERROR_BUSY;
}
/* 创建超时定时器 */
OSTmrCreate(&mbus->timeoutTimer, "Modbus Timeout Timer", 0, 0, OS_OPT_TMR_ONE_SHOT,
ModbusMaster_TimeoutCallback, mbus, &err);
if (err != OS_ERR_NONE) {
return MODBUS_MASTER_ERROR_BUSY;
}
/* 启用UART接收中断 */
HAL_UART_Receive_IT(mbus->huart, mbus->rxBuffer, 1);
return MODBUS_MASTER_OK;
}
/**
* @brief 设置通信超时时间
* @param mbus: Modbus主机结构体指针
* @param timeout: 超时时间(ms)
* @retval None
*/
void ModbusMaster_SetTimeout(ModbusMaster *mbus, uint32_t timeout)
{
OS_ERR err;
if (mbus == NULL || timeout == 0) {
return;
}
OSMutexPend(&mbus->mutex, 0, OS_OPT_PEND_BLOCKING, NULL, &err);
mbus->timeout = timeout;
OSMutexPost(&mbus->mutex, OS_OPT_POST_NONE, &err);
}
/**
* @brief 发送Modbus请求并等待响应
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param functionCode: 功能码
* @param data: 数据缓冲区
* @param dataLen: 数据长度
* @param expectedLen: 预期响应长度 (0表示未知长度)
* @retval 错误码
*/
static ModbusMasterError ModbusMaster_SendRequest(ModbusMaster *mbus, uint8_t slaveID, uint8_t functionCode,
uint8_t *data, uint16_t dataLen, uint16_t expectedLen)
{
OS_ERR err;
uint16_t crc;
uint32_t tickStart;
OS_ERR tmrErr;
CPU_TS ts;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || dataLen > MODBUS_MAX_PDU_LENGTH - 1) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 获取互斥锁 */
OSMutexPend(&mbus->mutex, 0, OS_OPT_PEND_BLOCKING, &ts, &err);
if (err != OS_ERR_NONE) {
return MODBUS_MASTER_ERROR_BUSY;
}
/* 检查是否空闲 */
if (mbus->state != MODBUS_MASTER_STATE_IDLE) {
OSMutexPost(&mbus->mutex, OS_OPT_POST_NONE, &err);
return MODBUS_MASTER_ERROR_BUSY;
}
/* 构建请求帧 */
mbus->txBuffer[0] = slaveID;
mbus->txBuffer[1] = functionCode;
if (dataLen > 0) {
memcpy(&mbus->txBuffer[2], data, dataLen);
}
/* 计算CRC */
crc = ModbusMaster_CRC16(mbus->txBuffer, dataLen + 2);
mbus->txBuffer[dataLen + 2] = crc & 0xFF; // CRC低字节
mbus->txBuffer[dataLen + 3] = (crc >> 8) & 0xFF; // CRC高字节
/* 准备接收响应 */
mbus->state = MODBUS_MASTER_STATE_WAIT_REPLY;
mbus->rxCount = 0;
mbus->lastSlaveID = slaveID;
mbus->lastFunctionCode = functionCode;
mbus->expectLength = expectedLen;
/* 清除信号量 */
OSSemSet(&mbus->rxSem, 0, &err);
/* 启动超时定时器 */
OSTmrStop(&mbus->timeoutTimer, OS_OPT_TMR_NONE, NULL, &tmrErr);
OSTmrSet(&mbus->timeoutTimer, mbus->timeout / (1000 / OSCfg_TmrTaskRate_Hz), 0, &ModbusMaster_TimeoutCallback, mbus, &tmrErr);
OSTmrStart(&mbus->timeoutTimer, &tmrErr);
/* 发送请求 */
HAL_UART_Transmit(mbus->huart, mbus->txBuffer, dataLen + 4, 1000);
/* 等待响应 */
if (OSSemPend(&mbus->rxSem, mbus->timeout, OS_OPT_PEND_BLOCKING, &ts, &err) != OS_ERR_NONE) {
mbus->state = MODBUS_MASTER_STATE_IDLE;
mbus->lastError = MODBUS_MASTER_ERROR_TIMEOUT;
OSMutexPost(&mbus->mutex, OS_OPT_POST_NONE, &err);
return MODBUS_MASTER_ERROR_TIMEOUT;
}
/* 停止超时定时器 */
OSTmrStop(&mbus->timeoutTimer, OS_OPT_TMR_NONE, NULL, &tmrErr);
/* 处理响应 */
mbus->lastError = ModbusMaster_ProcessResponse(mbus);
mbus->state = MODBUS_MASTER_STATE_IDLE;
/* 释放互斥锁 */
OSMutexPost(&mbus->mutex, OS_OPT_POST_NONE, &err);
return mbus->lastError;
}
/**
* @brief 处理Modbus响应
* @param mbus: Modbus主机结构体指针
* @retval 错误码
*/
static ModbusMasterError ModbusMaster_ProcessResponse(ModbusMaster *mbus)
{
uint16_t crc, receivedCrc;
/* 检查接收数据长度 */
if (mbus->rxCount < 4) { // 至少包含从机地址、功能码、数据和CRC
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
/* 检查从机地址 */
if (mbus->rxBuffer[0] != mbus->lastSlaveID) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
/* 检查CRC */
crc = ModbusMaster_CRC16(mbus->rxBuffer, mbus->rxCount - 2);
receivedCrc = (mbus->rxBuffer[mbus->rxCount - 1] << 8) | mbus->rxBuffer[mbus->rxCount - 2];
if (crc != receivedCrc) {
return MODBUS_MASTER_ERROR_CRC;
}
/* 检查是否有异常 */
if ((mbus->rxBuffer[1] & 0x80) && mbus->rxCount >= 5) {
// 异常响应
return MODBUS_MASTER_ERROR_EXCEPTION;
}
/* 检查功能码 */
if (mbus->rxBuffer[1] != mbus->lastFunctionCode) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
return MODBUS_MASTER_OK;
}
/**
* @brief UART接收回调函数,应在HAL_UART_RxCpltCallback中调用
* @param mbus: Modbus主机结构体指针
* @param huart: 串口句柄
* @retval None
*/
void ModbusMaster_UartRxCpltCallback(ModbusMaster *mbus, UART_HandleTypeDef *huart)
{
OS_ERR err;
if (mbus == NULL || huart != mbus->huart || mbus->state != MODBUS_MASTER_STATE_WAIT_REPLY) {
return;
}
/* 增加接收计数 */
mbus->rxCount++;
/* 如果已接收到足够的数据 */
if ((mbus->expectLength > 0 && mbus->rxCount >= mbus->expectLength) ||
mbus->rxCount >= MODBUS_MAX_ADU_LENGTH) {
mbus->state = MODBUS_MASTER_STATE_PROCESS;
OSSemPost(&mbus->rxSem, OS_OPT_POST_1, &err); // 发送信号量
return;
}
/* 如果收到异常帧(功能码最高位置1) */
if (mbus->rxCount >= 3 && (mbus->rxBuffer[1] & 0x80)) {
if (mbus->rxCount >= 5) { // 异常响应长度:从机ID(1) + 功能码(1) + 错误码(1) + CRC(2) = 5
mbus->state = MODBUS_MASTER_STATE_PROCESS;
OSSemPost(&mbus->rxSem, OS_OPT_POST_1, &err);
return;
}
}
/* 特定功能码的响应处理 */
if (mbus->rxCount >= 3) {
uint8_t byteCount;
switch (mbus->lastFunctionCode) {
case MODBUS_READ_COILS:
case MODBUS_READ_DISCRETE_INPUTS:
case MODBUS_READ_HOLDING_REGISTERS:
case MODBUS_READ_INPUT_REGISTERS:
if (mbus->rxCount == 3) {
byteCount = mbus->rxBuffer[2];
/* 计算总响应长度: 从机ID(1) + 功能码(1) + 字节数(1) + 数据(n) + CRC(2) */
mbus->expectLength = 3 + byteCount + 2;
}
break;
case MODBUS_WRITE_SINGLE_COIL:
case MODBUS_WRITE_SINGLE_REGISTER:
/* 响应长度: 从机ID(1) + 功能码(1) + 地址(2) + 值(2) + CRC(2) = 8 */
mbus->expectLength = 8;
break;
case MODBUS_WRITE_MULTIPLE_COILS:
case MODBUS_WRITE_MULTIPLE_REGISTERS:
/* 响应长度: 从机ID(1) + 功能码(1) + 地址(2) + 数量(2) + CRC(2) = 8 */
mbus->expectLength = 8;
break;
default:
break;
}
}
/* 继续接收数据 */
HAL_UART_Receive_IT(mbus->huart, &mbus->rxBuffer[mbus->rxCount], 1);
}
/**
* @brief 超时回调函数,内部使用
* @param p_tmr: 定时器指针
* @param p_arg: 参数指针 (ModbusMaster结构体)
* @retval None
*/
void ModbusMaster_TimeoutCallback(OS_TMR *p_tmr, void *p_arg)
{
ModbusMaster *mbus = (ModbusMaster *)p_arg;
OS_ERR err;
if (mbus == NULL || mbus->state != MODBUS_MASTER_STATE_WAIT_REPLY) {
return;
}
mbus->lastError = MODBUS_MASTER_ERROR_TIMEOUT;
OSSemPost(&mbus->rxSem, OS_OPT_POST_1, &err);
}
/**
* @brief 读取线圈状态 (功能码0x01)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 线圈数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadCoils(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint8_t *data)
{
uint8_t requestData[4];
ModbusMasterError error;
uint8_t byteCount;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || data == NULL || quantity == 0 || quantity > 2000) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 构建请求数据 */
requestData[0] = (startAddr >> 8) & 0xFF; // 起始地址高字节
requestData[1] = startAddr & 0xFF; // 起始地址低字节
requestData[2] = (quantity >> 8) & 0xFF; // 数量高字节
requestData[3] = quantity & 0xFF; // 数量低字节
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_READ_COILS, requestData, 4, 0);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 处理响应数据 */
byteCount = mbus->rxBuffer[2];
if (byteCount != (quantity + 7) / 8) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
/* 复制数据 */
memcpy(data, &mbus->rxBuffer[3], byteCount);
return MODBUS_MASTER_OK;
}
/**
* @brief 读取离散输入状态 (功能码0x02)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 输入数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadDiscreteInputs(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint8_t *data)
{
uint8_t requestData[4];
ModbusMasterError error;
uint8_t byteCount;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || data == NULL || quantity == 0 || quantity > 2000) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 构建请求数据 */
requestData[0] = (startAddr >> 8) & 0xFF; // 起始地址高字节
requestData[1] = startAddr & 0xFF; // 起始地址低字节
requestData[2] = (quantity >> 8) & 0xFF; // 数量高字节
requestData[3] = quantity & 0xFF; // 数量低字节
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_READ_DISCRETE_INPUTS, requestData, 4, 0);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 处理响应数据 */
byteCount = mbus->rxBuffer[2];
if (byteCount != (quantity + 7) / 8) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
/* 复制数据 */
memcpy(data, &mbus->rxBuffer[3], byteCount);
return MODBUS_MASTER_OK;
}
/**
* @brief 读取保持寄存器 (功能码0x03)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 寄存器数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadHoldingRegisters(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint16_t *data)
{
uint8_t requestData[4];
ModbusMasterError error;
uint8_t byteCount;
uint16_t i;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || data == NULL || quantity == 0 || quantity > 125) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 构建请求数据 */
requestData[0] = (startAddr >> 8) & 0xFF; // 起始地址高字节
requestData[1] = startAddr & 0xFF; // 起始地址低字节
requestData[2] = (quantity >> 8) & 0xFF; // 数量高字节
requestData[3] = quantity & 0xFF; // 数量低字节
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_READ_HOLDING_REGISTERS, requestData, 4, 0);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 处理响应数据 */
byteCount = mbus->rxBuffer[2];
if (byteCount != quantity * 2) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
/* 复制并转换数据 (高字节在前) */
for (i = 0; i < quantity; i++) {
data[i] = (mbus->rxBuffer[3 + i * 2] << 8) | mbus->rxBuffer[4 + i * 2];
}
return MODBUS_MASTER_OK;
}
/**
* @brief 读取输入寄存器 (功能码0x04)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 寄存器数量
* @param data: 接收数据缓冲区
* @retval 错误码
*/
ModbusMasterError ModbusMaster_ReadInputRegisters(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, uint16_t *data)
{
uint8_t requestData[4];
ModbusMasterError error;
uint8_t byteCount;
uint16_t i;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || data == NULL || quantity == 0 || quantity > 125) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 构建请求数据 */
requestData[0] = (startAddr >> 8) & 0xFF; // 起始地址高字节
requestData[1] = startAddr & 0xFF; // 起始地址低字节
requestData[2] = (quantity >> 8) & 0xFF; // 数量高字节
requestData[3] = quantity & 0xFF; // 数量低字节
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_READ_INPUT_REGISTERS, requestData, 4, 0);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 处理响应数据 */
byteCount = mbus->rxBuffer[2];
if (byteCount != quantity * 2) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
/* 复制并转换数据 (高字节在前) */
for (i = 0; i < quantity; i++) {
data[i] = (mbus->rxBuffer[3 + i * 2] << 8) | mbus->rxBuffer[4 + i * 2];
}
return MODBUS_MASTER_OK;
}
/**
* @brief 写单个线圈 (功能码0x05)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param coilAddr: 线圈地址
* @param value: 线圈值 (0=OFF, 非0=ON)
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteSingleCoil(ModbusMaster *mbus, uint8_t slaveID, uint16_t coilAddr, bool value)
{
uint8_t requestData[4];
ModbusMasterError error;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 构建请求数据 */
requestData[0] = (coilAddr >> 8) & 0xFF; // 地址高字节
requestData[1] = coilAddr & 0xFF; // 地址低字节
requestData[2] = value ? 0xFF : 0x00; // 值高字节 (FF00=ON, 0000=OFF)
requestData[3] = 0x00; // 值低字节
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_WRITE_SINGLE_COIL, requestData, 4, 8);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 验证响应中的地址和值 */
if (((mbus->rxBuffer[2] << 8) | mbus->rxBuffer[3]) != coilAddr ||
((mbus->rxBuffer[4] << 8) | mbus->rxBuffer[5]) != (value ? 0xFF00 : 0x0000)) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
return MODBUS_MASTER_OK;
}
/**
* @brief 写单个寄存器 (功能码0x06)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param regAddr: 寄存器地址
* @param value: 寄存器值
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteSingleRegister(ModbusMaster *mbus, uint8_t slaveID, uint16_t regAddr, uint16_t value)
{
uint8_t requestData[4];
ModbusMasterError error;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 构建请求数据 */
requestData[0] = (regAddr >> 8) & 0xFF; // 地址高字节
requestData[1] = regAddr & 0xFF; // 地址低字节
requestData[2] = (value >> 8) & 0xFF; // 值高字节
requestData[3] = value & 0xFF; // 值低字节
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_WRITE_SINGLE_REGISTER, requestData, 4, 8);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 验证响应中的地址和值 */
if (((mbus->rxBuffer[2] << 8) | mbus->rxBuffer[3]) != regAddr ||
((mbus->rxBuffer[4] << 8) | mbus->rxBuffer[5]) != value) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
return MODBUS_MASTER_OK;
}
/**
* @brief 写多个线圈 (功能码0x0F)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 线圈数量
* @param data: 线圈值数组
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteMultipleCoils(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, const uint8_t *data)
{
uint8_t requestData[MODBUS_MAX_PDU_LENGTH - 1];
ModbusMasterError error;
uint8_t byteCount;
uint16_t i;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || data == NULL || quantity == 0 || quantity > 1968) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 计算字节数 */
byteCount = (quantity + 7) / 8;
/* 构建请求数据 */
requestData[0] = (startAddr >> 8) & 0xFF; // 起始地址高字节
requestData[1] = startAddr & 0xFF; // 起始地址低字节
requestData[2] = (quantity >> 8) & 0xFF; // 数量高字节
requestData[3] = quantity & 0xFF; // 数量低字节
requestData[4] = byteCount; // 字节数
/* 复制线圈值 */
for (i = 0; i < byteCount; i++) {
requestData[5 + i] = data[i];
}
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_WRITE_MULTIPLE_COILS, requestData, 5 + byteCount, 8);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 验证响应中的地址和数量 */
if (((mbus->rxBuffer[2] << 8) | mbus->rxBuffer[3]) != startAddr ||
((mbus->rxBuffer[4] << 8) | mbus->rxBuffer[5]) != quantity) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
return MODBUS_MASTER_OK;
}
/**
* @brief 写多个寄存器 (功能码0x10)
* @param mbus: Modbus主机结构体指针
* @param slaveID: 从机地址
* @param startAddr: 起始地址
* @param quantity: 寄存器数量
* @param data: 寄存器值数组
* @retval 错误码
*/
ModbusMasterError ModbusMaster_WriteMultipleRegisters(ModbusMaster *mbus, uint8_t slaveID, uint16_t startAddr, uint16_t quantity, const uint16_t *data)
{
uint8_t requestData[MODBUS_MAX_PDU_LENGTH - 1];
ModbusMasterError error;
uint8_t byteCount;
uint16_t i;
/* 参数检查 */
if (mbus == NULL || slaveID == 0 || slaveID > 247 || data == NULL || quantity == 0 || quantity > 123) {
return MODBUS_MASTER_ERROR_INVALID_SLAVE_ID;
}
/* 计算字节数 */
byteCount = quantity * 2;
/* 构建请求数据 */
requestData[0] = (startAddr >> 8) & 0xFF; // 起始地址高字节
requestData[1] = startAddr & 0xFF; // 起始地址低字节
requestData[2] = (quantity >> 8) & 0xFF; // 数量高字节
requestData[3] = quantity & 0xFF; // 数量低字节
requestData[4] = byteCount; // 字节数
/* 复制寄存器值 */
for (i = 0; i < quantity; i++) {
requestData[5 + i * 2] = (data[i] >> 8) & 0xFF; // 值高字节
requestData[6 + i * 2] = data[i] & 0xFF; // 值低字节
}
/* 发送请求并等待响应 */
error = ModbusMaster_SendRequest(mbus, slaveID, MODBUS_WRITE_MULTIPLE_REGISTERS, requestData, 5 + byteCount, 8);
if (error != MODBUS_MASTER_OK) {
return error;
}
/* 验证响应中的地址和数量 */
if (((mbus->rxBuffer[2] << 8) | mbus->rxBuffer[3]) != startAddr ||
((mbus->rxBuffer[4] << 8) | mbus->rxBuffer[5]) != quantity) {
return MODBUS_MASTER_ERROR_INVALID_RESPONSE;
}
return MODBUS_MASTER_OK;
}
/**
* @brief 获取错误描述
* @param error: 错误码
* @retval 错误描述字符串
*/
const char* ModbusMaster_GetErrorString(ModbusMasterError error)
{
switch (error) {
case MODBUS_MASTER_OK:
return "成功";
case MODBUS_MASTER_ERROR_TIMEOUT:
return "通信超时";
case MODBUS_MASTER_ERROR_CRC:
return "CRC校验错误";
case MODBUS_MASTER_ERROR_EXCEPTION:
return "从机返回异常";
case MODBUS_MASTER_ERROR_INVALID_SLAVE_ID:
return "无效的从机ID";
case MODBUS_MASTER_ERROR_INVALID_RESPONSE:
return "无效的从机响应";
case MODBUS_MASTER_ERROR_BUSY:
return "主机忙,无法处理新请求";
default:
return "未知错误";
}
}
代码库使用示例
/**
******************************************************************************
* @file modbus_example.c
* @author Claude AI
* @brief Modbus RTU Master example
* @details 基于STM32 HAL库和uCOS-III的Modbus-RTU主机使用示例
******************************************************************************
*/
#include "modbus_master.h"
/* 定义Modbus主机实例 */
ModbusMaster modbusInstance;
void ModbusExample_Init(void)
{
/* 假设UART3已经在其他地方初始化 */
extern UART_HandleTypeDef huart3;
/* 初始化Modbus主机,使用UART3,超时时间设为1000ms */
ModbusMaster_Init(&modbusInstance, &huart3, 1000);
}
/* UART接收中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART3) {
/* 处理Modbus接收中断 */
ModbusMaster_UartRxCpltCallback(&modbusInstance, huart);
}
}
/* 读取保持寄存器示例 */
void ModbusExample_ReadHoldingRegisters(void)
{
uint16_t registerValues[10];
ModbusMasterError error;
/* 从从机地址1读取起始地址为0的10个保持寄存器 */
error = ModbusMaster_ReadHoldingRegisters(&modbusInstance, 1, 0, 10, registerValues);
if (error == MODBUS_MASTER_OK) {
/* 数据读取成功,可以使用registerValues中的数据 */
printf("读取成功,第一个寄存器值: %d\n", registerValues[0]);
} else {
/* 发生错误 */
printf("读取失败: %s\n", ModbusMaster_GetErrorString(error));
}
}
/* 写单个寄存器示例 */
void ModbusExample_WriteSingleRegister(void)
{
ModbusMasterError error;
/* 向从机地址1的寄存器地址100写入值1234 */
error = ModbusMaster_WriteSingleRegister(&modbusInstance, 1, 100, 1234);
if (error == MODBUS_MASTER_OK) {
/* 数据写入成功 */
printf("写入成功\n");
} else {
/* 发生错误 */
printf("写入失败: %s\n", ModbusMaster_GetErrorString(error));
}
}
/* 写多个寄存器示例 */
void ModbusExample_WriteMultipleRegisters(void)
{
ModbusMasterError error;
uint16_t values[5] = {1000, 2000, 3000, 4000, 5000};
/* 向从机地址1的起始地址200写入5个寄存器 */
error = ModbusMaster_WriteMultipleRegisters(&modbusInstance, 1, 200, 5, values);
if (error == MODBUS_MASTER_OK) {
/* 数据写入成功 */
printf("写入多个寄存器成功\n");
} else {
/* 发生错误 */
printf("写入失败: %s\n", ModbusMaster_GetErrorString(error));
}
}
/* 自定义超时时间示例 */
void ModbusExample_SetTimeout(void)
{
/* 设置通信超时时间为2000ms */
ModbusMaster_SetTimeout(&modbusInstance, 2000);
printf("超时时间已设置为2000ms\n");
}
/* 主函数示例 */
void ModbusExample_Run(void)
{
/* 初始化Modbus */
ModbusExample_Init();
/* 使用示例 */
ModbusExample_ReadHoldingRegisters();
ModbusExample_WriteSingleRegister();
ModbusExample_WriteMultipleRegisters();
ModbusExample_SetTimeout();
}
代码说明:
-
模块化设计:所有Modbus功能都被封装在模块化的API中,易于使用和维护。
-
完整的功能码实现:
- 0x01:读取线圈状态
- 0x02:读取离散输入状态
- 0x03:读取保持寄存器
- 0x04:读取输入寄存器
- 0x05:写单个线圈
- 0x06:写单个寄存器
- 0x0F:写多个线圈
- 0x10:写多个寄存器
-
CRC校验:实现了标准的Modbus CRC16校验算法。
-
超时处理:使用uCOS-III的定时器实现超时机制,可以自定义超时时间。
-
多从机支持:通过slaveID参数支持与多个从机设备通信,支持至少10个从机设备。
-
错误处理:完善的错误检测和处理机制,包括CRC错误、通信超时、从机异常码等。
-
线程安全:使用互斥信号量保证同时只有一个线程可以使用Modbus通信。