STM32F407 Modbus RTU主站例程
Modbus RTU主站实现,包含多从站管理、超时重发、错误处理、数据解析
一、系统架构
STM32F407 (Modbus主站)
↓
USART2/3/6 (RS485)
↓
MAX485收发器
↓
Modbus从站网络
↓
支持功能码:01, 02, 03, 04, 05, 06, 15, 16
核心特性:
- 多从站轮询(1-247)
- 自动重发(1-3次)
- CRC16校验
- 响应超时检测
- 异常响应处理
- 多串口支持
- 线程安全队列
二、硬件连接
1、RS485接口
STM32F407 MAX485
PA2 (USART2_TX) → DI
PA3 (USART2_RX) → RO
PA1 (DE/RE控制) → DE, RE
↓
A, B (RS485总线)
2、控制引脚配置
// DE/RE控制
#define RS485_DE_PORT GPIOA
#define RS485_DE_PIN GPIO_PIN_1
#define RS485_TX_MODE() HAL_GPIO_WritePin(RS485_DE_PORT, RS485_DE_PIN, GPIO_PIN_SET)
#define RS485_RX_MODE() HAL_GPIO_WritePin(RS485_DE_PORT, RS485_DE_PIN, GPIO_PIN_RESET)
三、程序架构
Modbus_Master/
├── modbus_master.h
├── modbus_master.c
├── modbus_crc.c
├── modbus_queue.c
├── modbus_rtu.c
└── main.c
四、Modbus主站核心实现
1、头文件定义
// modbus_master.h
#ifndef __MODBUS_MASTER_H
#define __MODBUS_MASTER_H
#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include <stdint.h>
#include <string.h>
// Modbus功能码
#define MODBUS_FC_READ_COILS 0x01
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
#define MODBUS_FC_READ_HOLDING_REGS 0x03
#define MODBUS_FC_READ_INPUT_REGS 0x04
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
#define MODBUS_FC_WRITE_SINGLE_REG 0x06
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
#define MODBUS_FC_WRITE_MULTIPLE_REGS 0x10
#define MODBUS_FC_READ_FILE_RECORD 0x14
#define MODBUS_FC_WRITE_FILE_RECORD 0x15
#define MODBUS_FC_MASK_WRITE_REG 0x16
#define MODBUS_FC_READ_WRITE_REGS 0x17
// 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_NO_RESP 0x0B
// 错误码
typedef enum {
MODBUS_OK = 0,
MODBUS_ERR_TIMEOUT,
MODBUS_ERR_CRC,
MODBUS_ERR_EXCEPTION,
MODBUS_ERR_RESP_LEN,
MODBUS_ERR_SLAVE_ADDR,
MODBUS_ERR_FUNC_CODE,
MODBUS_ERR_QUEUE_FULL,
MODBUS_ERR_BUSY,
MODBUS_ERR_PORT
} Modbus_Error_t;
// 串口端口
typedef enum {
MODBUS_PORT_1 = 0, // USART1
MODBUS_PORT_2, // USART2
MODBUS_PORT_3, // USART3
MODBUS_PORT_6 // USART6
} Modbus_Port_t;
// Modbus请求结构
typedef struct {
uint8_t slave_addr; // 从站地址
uint8_t func_code; // 功能码
uint16_t reg_addr; // 寄存器地址
uint16_t quantity; // 数量
uint8_t *data; // 数据指针
uint16_t data_len; // 数据长度
uint32_t timeout_ms; // 超时时间
uint8_t retry_count; // 重试次数
} Modbus_Request_t;
// Modbus响应结构
typedef struct {
uint8_t slave_addr; // 从站地址
uint8_t func_code; // 功能码
uint8_t exception; // 异常码
uint8_t *data; // 响应数据
uint16_t data_len; // 数据长度
Modbus_Error_t error; // 错误码
uint32_t timestamp; // 时间戳
} Modbus_Response_t;
// Modbus任务结果回调
typedef void (*Modbus_Callback_t)(Modbus_Response_t *response);
// Modbus任务
typedef struct {
Modbus_Request_t request;
Modbus_Response_t response;
Modbus_Callback_t callback;
uint8_t task_id;
uint8_t priority;
uint32_t deadline;
} Modbus_Task_t;
// Modbus主站句柄
typedef struct {
UART_HandleTypeDef *huart; // 串口句柄
GPIO_TypeDef *de_port; // DE控制端口
uint16_t de_pin; // DE控制引脚
uint32_t baudrate; // 波特率
uint8_t parity; // 校验位
uint8_t stopbits; // 停止位
// 缓冲区
uint8_t tx_buffer[256]; // 发送缓冲区
uint8_t rx_buffer[256]; // 接收缓冲区
uint16_t rx_index; // 接收索引
// 状态
uint8_t is_busy; // 忙标志
uint8_t is_initialized; // 初始化标志
uint32_t last_rx_time; // 最后接收时间
uint32_t inter_char_timeout; // 字符间超时
uint32_t frame_timeout; // 帧超时
// RTOS
QueueHandle_t request_queue; // 请求队列
SemaphoreHandle_t mutex; // 互斥锁
TaskHandle_t task_handle; // 任务句柄
// 统计
uint32_t tx_count; // 发送计数
uint32_t rx_count; // 接收计数
uint32_t error_count; // 错误计数
uint32_t timeout_count; // 超时计数
} Modbus_Handle_t;
// 函数声明
// 初始化
Modbus_Error_t Modbus_Init(Modbus_Handle_t *hmodbus, UART_HandleTypeDef *huart,
GPIO_TypeDef *de_port, uint16_t de_pin);
void Modbus_SetTimeout(Modbus_Handle_t *hmodbus, uint32_t inter_char_ms, uint32_t frame_ms);
// 基本功能
Modbus_Error_t Modbus_ReadCoils(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint8_t *coils, uint32_t timeout_ms);
Modbus_Error_t Modbus_ReadDiscreteInputs(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint8_t *inputs, uint32_t timeout_ms);
Modbus_Error_t Modbus_ReadHoldingRegisters(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint16_t *registers, uint32_t timeout_ms);
Modbus_Error_t Modbus_ReadInputRegisters(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint16_t *registers, uint32_t timeout_ms);
Modbus_Error_t Modbus_WriteSingleCoil(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t coil_addr, uint8_t value, uint32_t timeout_ms);
Modbus_Error_t Modbus_WriteSingleRegister(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t reg_addr, uint16_t value, uint32_t timeout_ms);
Modbus_Error_t Modbus_WriteMultipleCoils(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint8_t *coils, uint32_t timeout_ms);
Modbus_Error_t Modbus_WriteMultipleRegisters(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint16_t *registers, uint32_t timeout_ms);
// 高级功能
Modbus_Error_t Modbus_SendRequest(Modbus_Handle_t *hmodbus, Modbus_Request_t *request,
Modbus_Response_t *response);
uint8_t Modbus_AddTask(Modbus_Handle_t *hmodbus, Modbus_Task_t *task);
void Modbus_ProcessTasks(Modbus_Handle_t *hmodbus);
// 工具函数
uint16_t Modbus_CRC16(uint8_t *data, uint16_t length);
const char* Modbus_ErrorToString(Modbus_Error_t error);
const char* Modbus_ExceptionToString(uint8_t exception_code);
void Modbus_PrintFrame(uint8_t *frame, uint16_t len, const char *tag);
#endif
2、CRC16计算
// modbus_crc.c
#include "modbus_master.h"
// CRC16表(预计算)
static const uint16_t crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4140
};
// 计算CRC16
uint16_t Modbus_CRC16(uint8_t *data, uint16_t length)
{
uint8_t tmp;
uint16_t crc = 0xFFFF;
while(length--) {
tmp = *data++ ^ (uint8_t)crc;
crc >>= 8;
crc ^= crc16_table[tmp];
}
return crc;
}
// 添加CRC到帧
void Modbus_AddCRC(uint8_t *frame, uint16_t len)
{
uint16_t crc = Modbus_CRC16(frame, len);
frame[len] = crc & 0xFF; // CRC低字节
frame[len + 1] = crc >> 8; // CRC高字节
}
// 验证CRC
uint8_t Modbus_CheckCRC(uint8_t *frame, uint16_t len)
{
if(len < 2) return 0;
uint16_t crc_calc = Modbus_CRC16(frame, len - 2);
uint16_t crc_recv = (frame[len - 1] << 8) | frame[len - 2];
return (crc_calc == crc_recv);
}
3、初始化函数
// modbus_master.c
#include "modbus_master.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
// 全局句柄
static Modbus_Handle_t *modbus_handles[MODBUS_PORT_6 + 1] = {NULL};
// Modbus初始化
Modbus_Error_t Modbus_Init(Modbus_Handle_t *hmodbus, UART_HandleTypeDef *huart,
GPIO_TypeDef *de_port, uint16_t de_pin)
{
if(hmodbus == NULL || huart == NULL) {
return MODBUS_ERR_PORT;
}
// 清零句柄
memset(hmodbus, 0, sizeof(Modbus_Handle_t));
// 保存参数
hmodbus->huart = huart;
hmodbus->de_port = de_port;
hmodbus->de_pin = de_pin;
// 默认参数
hmodbus->baudrate = 9600;
hmodbus->parity = UART_PARITY_NONE;
hmodbus->stopbits = UART_STOPBITS_1;
hmodbus->inter_char_timeout = 5; // 5ms字符间超时
hmodbus->frame_timeout = 100; // 100ms帧超时
// 初始化DE引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = de_pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(de_port, &GPIO_InitStruct);
// 初始为接收模式
HAL_GPIO_WritePin(de_port, de_pin, GPIO_PIN_RESET);
// 创建RTOS对象
hmodbus->request_queue = xQueueCreate(10, sizeof(Modbus_Task_t));
hmodbus->mutex = xSemaphoreCreateMutex();
if(hmodbus->request_queue == NULL || hmodbus->mutex == NULL) {
return MODBUS_ERR_QUEUE_FULL;
}
// 注册句柄
if(huart->Instance == USART1) {
modbus_handles[MODBUS_PORT_1] = hmodbus;
} else if(huart->Instance == USART2) {
modbus_handles[MODBUS_PORT_2] = hmodbus;
} else if(huart->Instance == USART3) {
modbus_handles[MODBUS_PORT_3] = hmodbus;
} else if(huart->Instance == USART6) {
modbus_handles[MODBUS_PORT_6] = hmodbus;
}
hmodbus->is_initialized = 1;
printf("Modbus主站初始化成功 (UART: 0x%08lX)\r\n", (uint32_t)huart->Instance);
return MODBUS_OK;
}
// 设置超时
void Modbus_SetTimeout(Modbus_Handle_t *hmodbus, uint32_t inter_char_ms, uint32_t frame_ms)
{
if(hmodbus == NULL) return;
hmodbus->inter_char_timeout = inter_char_ms;
hmodbus->frame_timeout = frame_ms;
}
// 发送请求帧
static Modbus_Error_t Modbus_SendFrame(Modbus_Handle_t *hmodbus, uint8_t *frame, uint16_t len)
{
if(hmodbus == NULL || frame == NULL || len == 0) {
return MODBUS_ERR_PORT;
}
// 等待总线空闲
if(hmodbus->is_busy) {
return MODBUS_ERR_BUSY;
}
// 设置忙标志
hmodbus->is_busy = 1;
// 切换为发送模式
HAL_GPIO_WritePin(hmodbus->de_port, hmodbus->de_pin, GPIO_PIN_SET);
HAL_Delay(1); // 等待稳定
// 发送数据
HAL_StatusTypeDef status = HAL_UART_Transmit(hmodbus->huart, frame, len, 1000);
// 切换为接收模式
HAL_Delay(1);
HAL_GPIO_WritePin(hmodbus->de_port, hmodbus->de_pin, GPIO_PIN_RESET);
if(status != HAL_OK) {
hmodbus->is_busy = 0;
hmodbus->error_count++;
return MODBUS_ERR_PORT;
}
hmodbus->tx_count++;
hmodbus->last_rx_time = HAL_GetTick();
return MODBUS_OK;
}
// 接收响应帧
static Modbus_Error_t Modbus_ReceiveFrame(Modbus_Handle_t *hmodbus, uint8_t *buffer,
uint16_t *len, uint32_t timeout_ms)
{
if(hmodbus == NULL || buffer == NULL || len == NULL) {
return MODBUS_ERR_PORT;
}
uint32_t start_time = HAL_GetTick();
uint16_t index = 0;
uint8_t byte;
// 清空缓冲区
memset(buffer, 0, 256);
while(1) {
// 检查超时
if(HAL_GetTick() - start_time > timeout_ms) {
hmodbus->timeout_count++;
hmodbus->is_busy = 0;
return MODBUS_ERR_TIMEOUT;
}
// 尝试读取一个字节
if(HAL_UART_Receive(hmodbus->huart, &byte, 1, 10) == HAL_OK) {
buffer[index++] = byte;
hmodbus->last_rx_time = HAL_GetTick();
// 检查是否收到完整帧
if(index >= 5) { // 最小帧长度
// 检查字符间超时
if(HAL_GetTick() - hmodbus->last_rx_time > hmodbus->inter_char_timeout) {
*len = index;
hmodbus->rx_count++;
hmodbus->is_busy = 0;
// 验证帧
if(!Modbus_CheckCRC(buffer, index)) {
hmodbus->error_count++;
return MODBUS_ERR_CRC;
}
return MODBUS_OK;
}
}
// 防止缓冲区溢出
if(index >= 256) {
hmodbus->error_count++;
hmodbus->is_busy = 0;
return MODBUS_ERR_RESP_LEN;
}
}
}
}
4、功能码实现
// 读取保持寄存器 (0x03)
Modbus_Error_t Modbus_ReadHoldingRegisters(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint16_t *registers, uint32_t timeout_ms)
{
if(hmodbus == NULL || registers == NULL) {
return MODBUS_ERR_PORT;
}
if(quantity < 1 || quantity > 125) {
return MODBUS_ERR_ILLEGAL_DATA_VALUE;
}
// 构建请求帧
uint8_t tx_frame[8];
tx_frame[0] = slave_addr;
tx_frame[1] = MODBUS_FC_READ_HOLDING_REGS;
tx_frame[2] = (start_addr >> 8) & 0xFF;
tx_frame[3] = start_addr & 0xFF;
tx_frame[4] = (quantity >> 8) & 0xFF;
tx_frame[5] = quantity & 0xFF;
Modbus_AddCRC(tx_frame, 6);
// 发送请求
Modbus_Error_t err = Modbus_SendFrame(hmodbus, tx_frame, 8);
if(err != MODBUS_OK) {
return err;
}
// 接收响应
uint8_t rx_buffer[256];
uint16_t rx_len = 0;
err = Modbus_ReceiveFrame(hmodbus, rx_buffer, &rx_len, timeout_ms);
if(err != MODBUS_OK) {
return err;
}
// 解析响应
if(rx_len < 5) {
return MODBUS_ERR_RESP_LEN;
}
// 检查异常响应
if(rx_buffer[1] & 0x80) { // 异常响应
if(rx_len >= 5) {
hmodbus->error_count++;
return MODBUS_ERR_EXCEPTION;
}
return MODBUS_ERR_FUNC_CODE;
}
// 验证从站地址和功能码
if(rx_buffer[0] != slave_addr || rx_buffer[1] != MODBUS_FC_READ_HOLDING_REGS) {
return MODBUS_ERR_SLAVE_ADDR;
}
// 验证数据长度
uint8_t byte_count = rx_buffer[2];
if(byte_count != quantity * 2) {
return MODBUS_ERR_RESP_LEN;
}
// 解析寄存器数据
for(int i = 0; i < quantity; i++) {
registers[i] = (rx_buffer[3 + i*2] << 8) | rx_buffer[4 + i*2];
}
return MODBUS_OK;
}
// 写入单个寄存器 (0x06)
Modbus_Error_t Modbus_WriteSingleRegister(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t reg_addr, uint16_t value, uint32_t timeout_ms)
{
if(hmodbus == NULL) {
return MODBUS_ERR_PORT;
}
// 构建请求帧
uint8_t tx_frame[8];
tx_frame[0] = slave_addr;
tx_frame[1] = MODBUS_FC_WRITE_SINGLE_REG;
tx_frame[2] = (reg_addr >> 8) & 0xFF;
tx_frame[3] = reg_addr & 0xFF;
tx_frame[4] = (value >> 8) & 0xFF;
tx_frame[5] = value & 0xFF;
Modbus_AddCRC(tx_frame, 6);
// 发送请求
Modbus_Error_t err = Modbus_SendFrame(hmodbus, tx_frame, 8);
if(err != MODBUS_OK) {
return err;
}
// 接收响应
uint8_t rx_buffer[256];
uint16_t rx_len = 0;
err = Modbus_ReceiveFrame(hmodbus, rx_buffer, &rx_len, timeout_ms);
if(err != MODBUS_OK) {
return err;
}
// 解析响应
if(rx_len < 8) {
return MODBUS_ERR_RESP_LEN;
}
// 检查异常响应
if(rx_buffer[1] & 0x80) {
hmodbus->error_count++;
return MODBUS_ERR_EXCEPTION;
}
// 验证响应是否与请求一致
if(rx_buffer[0] != slave_addr ||
rx_buffer[1] != MODBUS_FC_WRITE_SINGLE_REG ||
rx_buffer[2] != tx_frame[2] ||
rx_buffer[3] != tx_frame[3] ||
rx_buffer[4] != tx_frame[4] ||
rx_buffer[5] != tx_frame[5]) {
return MODBUS_ERR_RESP_LEN;
}
return MODBUS_OK;
}
// 写入多个寄存器 (0x10)
Modbus_Error_t Modbus_WriteMultipleRegisters(Modbus_Handle_t *hmodbus, uint8_t slave_addr,
uint16_t start_addr, uint16_t quantity,
uint16_t *registers, uint32_t timeout_ms)
{
if(hmodbus == NULL || registers == NULL) {
return MODBUS_ERR_PORT;
}
if(quantity < 1 || quantity > 123) {
return MODBUS_ERR_ILLEGAL_DATA_VALUE;
}
// 构建请求帧
uint8_t tx_frame[256];
uint16_t tx_len = 0;
tx_frame[tx_len++] = slave_addr;
tx_frame[tx_len++] = MODBUS_FC_WRITE_MULTIPLE_REGS;
tx_frame[tx_len++] = (start_addr >> 8) & 0xFF;
tx_frame[tx_len++] = start_addr & 0xFF;
tx_frame[tx_len++] = (quantity >> 8) & 0xFF;
tx_frame[tx_len++] = quantity & 0xFF;
tx_frame[tx_len++] = quantity * 2; // 字节数
// 添加寄存器数据
for(int i = 0; i < quantity; i++) {
tx_frame[tx_len++] = (registers[i] >> 8) & 0xFF;
tx_frame[tx_len++] = registers[i] & 0xFF;
}
Modbus_AddCRC(tx_frame, tx_len);
tx_len += 2;
// 发送请求
Modbus_Error_t err = Modbus_SendFrame(hmodbus, tx_frame, tx_len);
if(err != MODBUS_OK) {
return err;
}
// 接收响应
uint8_t rx_buffer[256];
uint16_t rx_len = 0;
err = Modbus_ReceiveFrame(hmodbus, rx_buffer, &rx_len, timeout_ms);
if(err != MODBUS_OK) {
return err;
}
// 解析响应
if(rx_len < 8) {
return MODBUS_ERR_RESP_LEN;
}
// 检查异常响应
if(rx_buffer[1] & 0x80) {
hmodbus->error_count++;
return MODBUS_ERR_EXCEPTION;
}
// 验证响应
if(rx_buffer[0] != slave_addr ||
rx_buffer[1] != MODBUS_FC_WRITE_MULTIPLE_REGS ||
rx_buffer[2] != tx_frame[2] ||
rx_buffer[3] != tx_frame[3] ||
rx_buffer[4] != tx_frame[4] ||
rx_buffer[5] != tx_frame[5]) {
return MODBUS_ERR_RESP_LEN;
}
return MODBUS_OK;
}
5、高级API(支持重试)
// 发送请求(带重试)
Modbus_Error_t Modbus_SendRequest(Modbus_Handle_t *hmodbus, Modbus_Request_t *request,
Modbus_Response_t *response)
{
if(hmodbus == NULL || request == NULL || response == NULL) {
return MODBUS_ERR_PORT;
}
// 初始化响应
memset(response, 0, sizeof(Modbus_Response_t));
response->slave_addr = request->slave_addr;
response->func_code = request->func_code;
// 构建请求帧
uint8_t tx_frame[256];
uint16_t tx_len = 0;
tx_frame[tx_len++] = request->slave_addr;
tx_frame[tx_len++] = request->func_code;
// 根据功能码构建帧
switch(request->func_code) {
case MODBUS_FC_READ_HOLDING_REGS:
case MODBUS_FC_READ_INPUT_REGS:
case MODBUS_FC_READ_COILS:
case MODBUS_FC_READ_DISCRETE_INPUTS:
tx_frame[tx_len++] = (request->reg_addr >> 8) & 0xFF;
tx_frame[tx_len++] = request->reg_addr & 0xFF;
tx_frame[tx_len++] = (request->quantity >> 8) & 0xFF;
tx_frame[tx_len++] = request->quantity & 0xFF;
break;
case MODBUS_FC_WRITE_SINGLE_REG:
case MODBUS_FC_WRITE_SINGLE_COIL:
tx_frame[tx_len++] = (request->reg_addr >> 8) & 0xFF;
tx_frame[tx_len++] = request->reg_addr & 0xFF;
if(request->func_code == MODBUS_FC_WRITE_SINGLE_COIL) {
tx_frame[tx_len++] = request->data[0] ? 0xFF : 0x00;
tx_frame[tx_len++] = 0x00;
} else {
tx_frame[tx_len++] = (request->data[0] >> 8) & 0xFF;
tx_frame[tx_len++] = request->data[0] & 0xFF;
}
break;
case MODBUS_FC_WRITE_MULTIPLE_REGS:
tx_frame[tx_len++] = (request->reg_addr >> 8) & 0xFF;
tx_frame[tx_len++] = request->reg_addr & 0xFF;
tx_frame[tx_len++] = (request->quantity >> 8) & 0xFF;
tx_frame[tx_len++] = request->quantity & 0xFF;
tx_frame[tx_len++] = request->quantity * 2;
for(int i = 0; i < request->quantity; i++) {
tx_frame[tx_len++] = (request->data[i] >> 8) & 0xFF;
tx_frame[tx_len++] = request->data[i] & 0xFF;
}
break;
default:
return MODBUS_ERR_FUNC_CODE;
}
// 添加CRC
Modbus_AddCRC(tx_frame, tx_len);
tx_len += 2;
// 重试机制
uint8_t retry = 0;
Modbus_Error_t error = MODBUS_OK;
do {
// 发送请求
error = Modbus_SendFrame(hmodbus, tx_frame, tx_len);
if(error != MODBUS_OK) {
retry++;
continue;
}
// 接收响应
uint8_t rx_buffer[256];
uint16_t rx_len = 0;
error = Modbus_ReceiveFrame(hmodbus, rx_buffer, &rx_len, request->timeout_ms);
if(error != MODBUS_OK) {
retry++;
continue;
}
// 解析响应
if(rx_len < 5) {
error = MODBUS_ERR_RESP_LEN;
retry++;
continue;
}
// 检查异常响应
if(rx_buffer[1] & 0x80) {
response->exception = rx_buffer[2];
error = MODBUS_ERR_EXCEPTION;
break;
}
// 验证从站地址
if(rx_buffer[0] != request->slave_addr) {
error = MODBUS_ERR_SLAVE_ADDR;
retry++;
continue;
}
// 解析数据
switch(request->func_code) {
case MODBUS_FC_READ_HOLDING_REGS:
case MODBUS_FC_READ_INPUT_REGS:
if(response->data != NULL) {
uint8_t byte_count = rx_buffer[2];
for(int i = 0; i < byte_count/2; i++) {
uint16_t val = (rx_buffer[3 + i*2] << 8) | rx_buffer[4 + i*2];
response->data[i] = val;
}
response->data_len = byte_count/2;
}
break;
case MODBUS_FC_WRITE_SINGLE_REG:
case MODBUS_FC_WRITE_SINGLE_COIL:
// 确认写入成功
break;
case MODBUS_FC_WRITE_MULTIPLE_REGS:
// 确认写入成功
break;
}
break;
} while(retry < request->retry_count);
response->error = error;
response->timestamp = HAL_GetTick();
return error;
}
6、多从站轮询管理
// modbus_polling.c
#include "modbus_master.h"
// 从站定义
typedef struct {
uint8_t addr; // 从站地址
uint8_t enabled; // 是否启用
uint32_t poll_interval; // 轮询间隔
uint32_t last_poll_time; // 上次轮询时间
// 寄存器映射
struct {
uint16_t holding[100]; // 保持寄存器
uint16_t input[50]; // 输入寄存器
uint8_t coils[20]; // 线圈
uint8_t discrete[20]; // 离散输入
} regs;
// 状态
uint8_t online; // 在线状态
uint32_t last_response_time; // 最后响应时间
uint32_t error_count; // 错误计数
uint32_t timeout_count; // 超时计数
} Modbus_Slave_t;
// 轮询管理器
typedef struct {
Modbus_Handle_t *hmodbus; // Modbus句柄
Modbus_Slave_t slaves[32]; // 从站列表
uint8_t slave_count; // 从站数量
TaskHandle_t task_handle; // 轮询任务句柄
uint8_t polling_enabled; // 轮询使能
uint32_t poll_interval; // 默认轮询间隔
// 统计
uint32_t total_polls; // 总轮询次数
uint32_t success_polls; // 成功轮询次数
uint32_t failed_polls; // 失败轮询次数
} Modbus_PollManager_t;
static Modbus_PollManager_t poll_manager = {0};
// 初始化轮询管理器
Modbus_Error_t Modbus_PollManager_Init(Modbus_Handle_t *hmodbus)
{
if(hmodbus == NULL) {
return MODBUS_ERR_PORT;
}
memset(&poll_manager, 0, sizeof(Modbus_PollManager_t));
poll_manager.hmodbus = hmodbus;
poll_manager.poll_interval = 1000; // 默认1秒
poll_manager.polling_enabled = 1;
return MODBUS_OK;
}
// 添加从站
Modbus_Error_t Modbus_AddSlave(uint8_t addr, uint32_t interval_ms)
{
if(poll_manager.slave_count >= 32) {
return MODBUS_ERR_QUEUE_FULL;
}
Modbus_Slave_t *slave = &poll_manager.slaves[poll_manager.slave_count];
memset(slave, 0, sizeof(Modbus_Slave_t));
slave->addr = addr;
slave->enabled = 1;
slave->poll_interval = (interval_ms > 0) ? interval_ms : poll_manager.poll_interval;
slave->last_poll_time = 0;
slave->online = 0;
poll_manager.slave_count++;
return MODBUS_OK;
}
// 轮询单个从站
static void Poll_Slave(Modbus_Slave_t *slave)
{
if(!slave->enabled || !poll_manager.polling_enabled) {
return;
}
uint32_t current_time = HAL_GetTick();
// 检查是否到达轮询时间
if(current_time - slave->last_poll_time < slave->poll_interval) {
return;
}
slave->last_poll_time = current_time;
poll_manager.total_polls++;
// 读取保持寄存器 (示例: 读取10个寄存器,地址0开始)
uint16_t registers[10];
Modbus_Error_t err = Modbus_ReadHoldingRegisters(poll_manager.hmodbus,
slave->addr, 0, 10,
registers, 1000);
if(err == MODBUS_OK) {
// 保存数据
for(int i = 0; i < 10; i++) {
slave->regs.holding[i] = registers[i];
}
slave->online = 1;
slave->last_response_time = current_time;
poll_manager.success_polls++;
printf("从站 %d 轮询成功\r\n", slave->addr);
} else {
slave->error_count++;
slave->online = 0;
poll_manager.failed_polls++;
printf("从站 %d 轮询失败: %s\r\n", slave->addr,
Modbus_ErrorToString(err));
}
}
// 轮询任务
void Modbus_Polling_Task(void *argument)
{
printf("Modbus轮询任务启动\r\n");
while(1) {
// 轮询所有从站
for(int i = 0; i < poll_manager.slave_count; i++) {
Poll_Slave(&poll_manager.slaves[i]);
// 任务延时,避免同时轮询
vTaskDelay(pdMS_TO_TICKS(10));
}
// 主循环延时
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// 启动轮询
void Modbus_StartPolling(void)
{
xTaskCreate(Modbus_Polling_Task, "ModbusPoll", 512, NULL, 3,
&poll_manager.task_handle);
}
// 获取从站数据
Modbus_Error_t Modbus_GetSlaveData(uint8_t addr, uint16_t *registers, uint8_t reg_count)
{
for(int i = 0; i < poll_manager.slave_count; i++) {
if(poll_manager.slaves[i].addr == addr) {
if(reg_count > 100) reg_count = 100;
for(int j = 0; j < reg_count; j++) {
registers[j] = poll_manager.slaves[i].regs.holding[j];
}
return MODBUS_OK;
}
}
return MODBUS_ERR_SLAVE_ADDR;
}
7、任务队列管理
// modbus_queue.c
#include "modbus_master.h"
// 任务队列
#define MAX_TASKS 20
static Modbus_Task_t task_queue[MAX_TASKS];
static uint8_t task_count = 0;
static uint8_t task_id_counter = 0;
// 添加任务
uint8_t Modbus_AddTask(Modbus_Handle_t *hmodbus, Modbus_Task_t *task)
{
if(task_count >= MAX_TASKS) {
return 0;
}
if(hmodbus == NULL || task == NULL) {
return 0;
}
// 获取互斥锁
if(xSemaphoreTake(hmodbus->mutex, 100) != pdTRUE) {
return 0;
}
// 复制任务
memcpy(&task_queue[task_count], task, sizeof(Modbus_Task_t));
task_queue[task_count].task_id = task_id_counter++;
task_count++;
// 释放互斥锁
xSemaphoreGive(hmodbus->mutex);
return task_queue[task_count-1].task_id;
}
// 处理任务
void Modbus_ProcessTasks(Modbus_Handle_t *hmodbus)
{
if(task_count == 0 || hmodbus == NULL) {
return;
}
// 按优先级排序(简单实现)
for(int i = 0; i < task_count - 1; i++) {
for(int j = i + 1; j < task_count; j++) {
if(task_queue[j].priority > task_queue[i].priority) {
Modbus_Task_t temp = task_queue[i];
task_queue[i] = task_queue[j];
task_queue[j] = temp;
}
}
}
// 处理任务
for(int i = 0; i < task_count; i++) {
Modbus_Task_t *task = &task_queue[i];
// 检查截止时间
if(task->deadline > 0 && HAL_GetTick() > task->deadline) {
task->response.error = MODBUS_ERR_TIMEOUT;
continue;
}
// 执行请求
Modbus_Error_t err = Modbus_SendRequest(hmodbus, &task->request, &task->response);
// 调用回调
if(task->callback != NULL) {
task->callback(&task->response);
}
// 从队列中移除
for(int j = i; j < task_count - 1; j++) {
task_queue[j] = task_queue[j + 1];
}
task_count--;
i--;
// 任务间延时
vTaskDelay(pdMS_TO_TICKS(10));
}
}
8、调试和工具函数
// modbus_utils.c
#include "modbus_master.h"
#include <stdio.h>
// 错误码转字符串
const char* Modbus_ErrorToString(Modbus_Error_t error)
{
switch(error) {
case MODBUS_OK: return "OK";
case MODBUS_ERR_TIMEOUT: return "Timeout";
case MODBUS_ERR_CRC: return "CRC Error";
case MODBUS_ERR_EXCEPTION: return "Exception";
case MODBUS_ERR_RESP_LEN: return "Invalid Response Length";
case MODBUS_ERR_SLAVE_ADDR: return "Invalid Slave Address";
case MODBUS_ERR_FUNC_CODE: return "Invalid Function Code";
case MODBUS_ERR_QUEUE_FULL: return "Queue Full";
case MODBUS_ERR_BUSY: return "Busy";
case MODBUS_ERR_PORT: return "Port Error";
default: return "Unknown Error";
}
}
// 异常码转字符串
const char* Modbus_ExceptionToString(uint8_t exception_code)
{
switch(exception_code) {
case MODBUS_EXCEPTION_ILLEGAL_FUNCTION:
return "Illegal Function";
case MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS:
return "Illegal Data Address";
case MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE:
return "Illegal Data Value";
case MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE:
return "Slave Device Failure";
case MODBUS_EXCEPTION_ACKNOWLEDGE:
return "Acknowledge";
case MODBUS_EXCEPTION_SLAVE_DEVICE_BUSY:
return "Slave Device Busy";
case MODBUS_EXCEPTION_MEMORY_PARITY_ERROR:
return "Memory Parity Error";
case MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE:
return "Gateway Path Unavailable";
case MODBUS_EXCEPTION_GATEWAY_TARGET_NO_RESP:
return "Gateway Target No Response";
default: return "Unknown Exception";
}
}
// 打印帧数据
void Modbus_PrintFrame(uint8_t *frame, uint16_t len, const char *tag)
{
if(frame == NULL || len == 0) return;
printf("[%s] ", tag);
for(int i = 0; i < len; i++) {
printf("%02X ", frame[i]);
}
printf("\r\n");
}
// 打印统计信息
void Modbus_PrintStatistics(Modbus_Handle_t *hmodbus)
{
if(hmodbus == NULL) return;
printf("=== Modbus Statistics ===\r\n");
printf("Tx Count: %lu\r\n", hmodbus->tx_count);
printf("Rx Count: %lu\r\n", hmodbus->rx_count);
printf("Error Count: %lu\r\n", hmodbus->error_count);
printf("Timeout Count: %lu\r\n", hmodbus->timeout_count);
if(poll_manager.hmodbus == hmodbus) {
printf("Total Polls: %lu\r\n", poll_manager.total_polls);
printf("Success Polls: %lu\r\n", poll_manager.success_polls);
printf("Failed Polls: %lu\r\n", poll_manager.failed_polls);
}
printf("=========================\r\n");
}
9、主程序示例
// main.c
#include "main.h"
#include "modbus_master.h"
// Modbus句柄
Modbus_Handle_t hmodbus;
// 从站数据
typedef struct {
float temperature;
float humidity;
float pressure;
uint8_t status;
} Sensor_Data_t;
static Sensor_Data_t sensor_data = {0};
int main(void)
{
// HAL初始化
HAL_Init();
SystemClock_Config();
// 外设初始化
MX_GPIO_Init();
MX_USART2_UART_Init(); // Modbus使用USART2
MX_FreeRTOS_Init();
printf("STM32F407 Modbus RTU Master\r\n");
printf("============================\r\n");
// 1. 初始化Modbus主站
Modbus_Error_t err = Modbus_Init(&hmodbus, &huart2, GPIOA, GPIO_PIN_1);
if(err != MODBUS_OK) {
printf("Modbus初始化失败: %s\r\n", Modbus_ErrorToString(err));
Error_Handler();
}
// 2. 设置超时
Modbus_SetTimeout(&hmodbus, 5, 1000);
// 3. 初始化轮询管理器
Modbus_PollManager_Init(&hmodbus);
// 4. 添加从站
Modbus_AddSlave(1, 1000); // 从站1,1秒轮询
Modbus_AddSlave(2, 2000); // 从站2,2秒轮询
Modbus_AddSlave(3, 3000); // 从站3,3秒轮询
// 5. 启动轮询任务
Modbus_StartPolling();
// 6. 启动FreeRTOS调度器
osKernelStart();
while(1) { }
}
// 示例任务:定期读取传感器
void Sensor_Read_Task(void *argument)
{
uint16_t registers[10];
while(1) {
// 读取从站1的保持寄存器
Modbus_Error_t err = Modbus_ReadHoldingRegisters(&hmodbus, 1, 0, 10,
registers, 1000);
if(err == MODBUS_OK) {
// 解析数据
sensor_data.temperature = (float)registers[0] / 10.0f;
sensor_data.humidity = (float)registers[1] / 10.0f;
sensor_data.pressure = (float)((registers[2] << 16) | registers[3]) / 100.0f;
sensor_data.status = registers[4];
printf("传感器数据: %.1f°C, %.1f%%, %.1fhPa, 状态: %d\r\n",
sensor_data.temperature, sensor_data.humidity,
sensor_data.pressure, sensor_data.status);
} else {
printf("读取传感器失败: %s\r\n", Modbus_ErrorToString(err));
}
vTaskDelay(pdMS_TO_TICKS(5000)); // 5秒读取一次
}
}
// 示例任务:控制执行器
void Actuator_Control_Task(void *argument)
{
static uint8_t relay_state = 0;
while(1) {
// 切换继电器状态
relay_state = !relay_state;
// 写入单个线圈
Modbus_Error_t err = Modbus_WriteSingleCoil(&hmodbus, 1, 0,
relay_state, 1000);
if(err == MODBUS_OK) {
printf("继电器 %s\r\n", relay_state ? "打开" : "关闭");
} else {
printf("控制继电器失败: %s\r\n", Modbus_ErrorToString(err));
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10秒切换一次
}
}
// 任务回调示例
void Task_Callback(Modbus_Response_t *response)
{
if(response->error == MODBUS_OK) {
printf("任务完成: 从站%d, 功能码0x%02X\r\n",
response->slave_addr, response->func_code);
} else {
printf("任务失败: %s", Modbus_ErrorToString(response->error));
if(response->error == MODBUS_ERR_EXCEPTION) {
printf(" (异常: %s)", Modbus_ExceptionToString(response->exception));
}
printf("\r\n");
}
}
// 创建任务示例
void Create_Modbus_Tasks(void)
{
// 创建读取任务
xTaskCreate(Sensor_Read_Task, "SensorRead", 256, NULL, 2, NULL);
// 创建控制任务
xTaskCreate(Actuator_Control_Task, "ActuatorCtrl", 256, NULL, 2, NULL);
}
10、CubeMX配置
// usart.c
void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
}
// gpio.c
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// DE控制引脚
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
}
参考代码 STM32F407作为Modbus主站例程 www.youwenfan.com/contentcnu/70252.html
五、测试程序
// test_modbus.c
void Test_Modbus_Master(void)
{
printf("=== Modbus主站测试 ===\r\n");
// 1. 基本通信测试
printf("1. 基本通信测试...\r\n");
uint16_t test_reg[10];
Modbus_Error_t err = Modbus_ReadHoldingRegisters(&hmodbus, 1, 0, 10,
test_reg, 1000);
if(err == MODBUS_OK) {
printf(" 读取成功: ");
for(int i = 0; i < 10; i++) {
printf("%04X ", test_reg[i]);
}
printf("\r\n");
} else {
printf(" 读取失败: %s\r\n", Modbus_ErrorToString(err));
}
// 2. 写入测试
printf("2. 写入测试...\r\n");
err = Modbus_WriteSingleRegister(&hmodbus, 1, 100, 0x1234, 1000);
if(err == MODBUS_OK) {
printf(" 写入成功\r\n");
} else {
printf(" 写入失败: %s\r\n", Modbus_ErrorToString(err));
}
// 3. 批量写入测试
printf("3. 批量写入测试...\r\n");
uint16_t write_data[5] = {0x1111, 0x2222, 0x3333, 0x4444, 0x5555};
err = Modbus_WriteMultipleRegisters(&hmodbus, 1, 200, 5, write_data, 1000);
if(err == MODBUS_OK) {
printf(" 批量写入成功\r\n");
} else {
printf(" 批量写入失败: %s\r\n", Modbus_ErrorToString(err));
}
// 4. 任务队列测试
printf("4. 任务队列测试...\r\n");
Modbus_Task_t task = {0};
task.request.slave_addr = 1;
task.request.func_code = MODBUS_FC_READ_HOLDING_REGS;
task.request.reg_addr = 0;
task.request.quantity = 5;
task.request.timeout_ms = 1000;
task.request.retry_count = 3;
task.callback = Task_Callback;
task.priority = 5;
uint8_t task_id = Modbus_AddTask(&hmodbus, &task);
if(task_id) {
printf(" 任务添加成功, ID: %d\r\n", task_id);
} else {
printf(" 任务添加失败\r\n");
}
// 处理任务
Modbus_ProcessTasks(&hmodbus);
printf("测试完成!\r\n");
}
六、常见问题解决
| 问题 | 原因 | 解决 |
|---|---|---|
| 无响应 | DE/RE控制错误 | 检查控制引脚时序 |
| CRC错误 | 波特率不匹配 | 检查主从站波特率 |
| 响应超时 | 从站地址错误 | 检查从站地址 |
| 数据错误 | 寄存器地址错误 | 检查Modbus映射表 |
| 通信不稳定 | 终端电阻缺失 | 总线两端加120Ω电阻 |

浙公网安备 33010602011771号