基于STM32F103的RS485 MODBUS从站例程

STM32F103 RS485 MODBUS从站实现,包含RTU协议解析、寄存器映射、CRC校验、中断处理等功能,可直接用于工业现场设备通信。

一、系统架构

STM32F103 RS485 MODBUS从站架构:
├── 硬件层
│   ├── STM32F103C8T6/CBT6 微控制器
│   ├── SP3485/MAX485 RS485收发器
│   ├── 120Ω终端电阻
│   └── TVS防静电保护
├── 驱动层
│   ├── USART DMA传输
│   ├── GPIO控制DE/RE
│   ├── 定时器3.5字符超时检测
│   └、 中断优先级管理
├── 协议层
│   ├── MODBUS RTU帧解析
│   ├── CRC16校验计算
│   ├── 功能码处理(01/02/03/04/05/06/15/16)
│   └、 异常响应生成
├── 应用层
│   ├── 保持寄存器映射
│   ├── 输入寄存器映射
│   ├── 线圈状态映射
│   └、 离散输入映射
└── 工具层
    ├── 参数配置存储
    ├── 设备ID管理
    ├── 通信诊断
    └、 看门狗保护

二、硬件连接

2.1 RS485接口连接

STM32F103    SP3485    RS485总线
PA2(TX1) --> DI        --
PA3(RX1) <-- RO        --
PA1      --> DE/RE     --
PB0      --> LED       -- 通信指示灯
GND      --> GND       --
3.3V     --> VCC       --
A        --> A+        --> 设备1 A+
B        --> B-        --> 设备1 B-

2.2 寄存器映射表

寄存器地址 名称 读写权限 说明
40001 设备状态 只读 运行状态字
40002 设备温度 只读 温度值×10
40003 运行时间 只读 小时数
40004 错误代码 只读 错误状态
40005 控制命令 读写 控制指令
40006 设定速度 读写 速度设定值
40007 实际速度 只读 实际速度值
40008 报警阈值 读写 报警温度阈值

三、核心代码实现

3.1 头文件定义 (modbus_slave.h)

#ifndef MODBUS_SLAVE_H
#define MODBUS_SLAVE_H

#include "stm32f10x.h"
#include <stdint.h>
#include <string.h>

// MODBUS配置参数
#define MODBUS_SLAVE_ID       0x01        // 从站地址
#define MODBUS_BAUDRATE       9600        // 波特率
#define MODBUS_PARITY         USART_Parity_Even  // 偶校验
#define MODBUS_DATA_BITS      8           // 数据位
#define MODBUS_STOP_BITS      1           // 停止位

// 功能码定义
#define MB_FUNC_READ_COILS            0x01
#define MB_FUNC_READ_DISCRETE_INPUTS  0x02
#define MB_FUNC_READ_HOLDING_REG     0x03
#define MB_FUNC_READ_INPUT_REG       0x04
#define MB_FUNC_WRITE_SINGLE_COIL    0x05
#define MB_FUNC_WRITE_SINGLE_REG     0x06
#define MB_FUNC_WRITE_MULTIPLE_COILS  0x0F
#define MB_FUNC_WRITE_MULTIPLE_REG    0x10

// 异常码定义
#define MB_EX_NONE                    0x00
#define MB_EX_ILLEGAL_FUNCTION        0x01
#define MB_EX_ILLEGAL_DATA_ADDRESS    0x02
#define MB_EX_ILLEGAL_DATA_VALUE      0x03
#define MB_EX_SLAVE_DEVICE_FAILURE    0x04
#define MB_EX_ACKNOWLEDGE             0x05
#define MB_EX_SLAVE_DEVICE_BUSY       0x06

// 寄存器地址定义
#define REG_START_ADDR        0x0000
#define REG_HOLDING_START     0x0000
#define REG_INPUT_START       0x0000
#define REG_COIL_START        0x0000
#define REG_DISCRETE_START    0x0000

// 寄存器数量定义
#define REG_HOLDING_NREGS     100
#define REG_INPUT_NREGS       50
#define REG_COIL_NREGS        32
#define REG_DISCRETE_NREGS    32

// 帧超时定义(3.5个字符时间)
#define MODBUS_FRAME_TIMEOUT  4  // 4ms @ 9600bps

// MODBUS数据结构
typedef struct {
    uint8_t slave_id;           // 从站地址
    uint8_t function_code;      // 功能码
    uint16_t start_addr;        // 起始地址
    uint16_t reg_count;         // 寄存器数量
    uint8_t *data;              // 数据指针
    uint16_t data_len;          // 数据长度
    uint8_t exception_code;      // 异常码
} ModbusFrame;

// 寄存器映射结构
typedef struct {
    uint16_t holding_regs[REG_HOLDING_NREGS];    // 保持寄存器
    uint16_t input_regs[REG_INPUT_NREGS];        // 输入寄存器
    uint8_t coil_status[REG_COIL_NREGS];         // 线圈状态
    uint8_t discrete_inputs[REG_DISCRETE_NREGS]; // 离散输入
} RegisterMap;

// 通信状态结构
typedef struct {
    uint32_t total_frames;       // 总帧数
    uint32_t error_frames;       // 错误帧数
    uint32_t timeout_frames;     // 超时帧数
    uint32_t crc_errors;         // CRC错误数
    uint32_t last_comm_time;     // 最后通信时间
    uint8_t comm_status;         // 通信状态
} CommStatus;

// 函数声明
void Modbus_Init(void);
void Modbus_ProcessFrame(uint8_t *rx_buf, uint16_t rx_len, uint8_t *tx_buf, uint16_t *tx_len);
uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t length);
void Modbus_SendResponse(uint8_t *tx_buf, uint16_t tx_len);
void Modbus_HandleException(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_ReadHoldingRegisters(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_ReadInputRegisters(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_WriteSingleRegister(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_WriteMultipleRegisters(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_ReadCoils(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_WriteSingleCoil(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len);
void Modbus_UpdateRegisters(void);

// 外部变量声明
extern RegisterMap g_reg_map;
extern CommStatus g_comm_status;
extern volatile uint8_t g_frame_received;

#endif // MODBUS_SLAVE_H

3.2 主程序实现 (main.c)

#include "stm32f10x.h"
#include "modbus_slave.h"
#include "usart.h"
#include "timer.h"
#include "gpio.h"
#include "delay.h"

// 全局变量
RegisterMap g_reg_map;
CommStatus g_comm_status;
volatile uint8_t g_frame_received = 0;
volatile uint8_t g_frame_timeout = 0;
uint8_t rx_buffer[256];
uint8_t tx_buffer[256];
uint16_t rx_length = 0;

// 系统初始化
void System_Init(void)
{
    // 初始化系统时钟
    SystemClock_Init();
    
    // 初始化延时
    Delay_Init();
    
    // 初始化GPIO
    GPIO_Init_Config();
    
    // 初始化USART1 (RS485)
    USART1_Init(MODBUS_BAUDRATE);
    
    // 初始化定时器(用于3.5字符超时检测)
    TIM3_Init(MODBUS_FRAME_TIMEOUT);
    
    // 初始化MODBUS
    Modbus_Init();
    
    // 初始化寄存器默认值
    g_reg_map.holding_regs[0] = 0x1234;  // 设备ID
    g_reg_map.holding_regs[1] = 250;     // 温度值 25.0℃
    g_reg_map.holding_regs[2] = 1000;    // 运行时间
    g_reg_map.holding_regs[3] = 0x0000;   // 错误代码
    g_reg_map.holding_regs[4] = 0x0001;   // 控制命令
    g_reg_map.holding_regs[5] = 50;      // 设定速度 50%
    g_reg_map.holding_regs[6] = 48;      // 实际速度 48%
    g_reg_map.holding_regs[7] = 300;     // 报警阈值 30.0℃
    
    // 初始化通信状态
    memset(&g_comm_status, 0, sizeof(CommStatus));
    g_comm_status.comm_status = 1;  // 通信正常
}

int main(void)
{
    uint16_t tx_len;
    
    // 系统初始化
    System_Init();
    
    // 主循环
    while(1)
    {
        // 检查是否接收到完整帧
        if(g_frame_received)
        {
            g_frame_received = 0;
            
            // 处理MODBUS帧
            Modbus_ProcessFrame(rx_buffer, rx_length, tx_buffer, &tx_len);
            
            // 发送响应
            if(tx_len > 0)
            {
                Modbus_SendResponse(tx_buffer, tx_len);
                g_comm_status.total_frames++;
                g_comm_status.last_comm_time = HAL_GetTick();
            }
        }
        
        // 更新寄存器数据(模拟传感器数据变化)
        Modbus_UpdateRegisters();
        
        // 检查通信超时
        if(HAL_GetTick() - g_comm_status.last_comm_time > 5000)  // 5秒无通信
        {
            g_comm_status.comm_status = 0;  // 通信超时
        }
        else
        {
            g_comm_status.comm_status = 1;  // 通信正常
        }
        
        // 通信指示灯闪烁
        if(g_comm_status.comm_status)
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_0);  // LED亮
        }
        else
        {
            GPIO_ResetBits(GPIOB, GPIO_Pin_0); // LED灭
        }
        
        Delay_ms(10);
    }
}

3.3 MODBUS协议处理 (modbus_slave.c)

#include "modbus_slave.h"
#include "crc16.h"

RegisterMap g_reg_map;
CommStatus g_comm_status;
volatile uint8_t g_frame_received = 0;

// MODBUS初始化
void Modbus_Init(void)
{
    // 设置从站ID
    g_reg_map.holding_regs[0] = MODBUS_SLAVE_ID;
    
    // 初始化其他寄存器
    for(int i = 1; i < REG_HOLDING_NREGS; i++)
    {
        g_reg_map.holding_regs[i] = 0x0000;
    }
    
    for(int i = 0; i < REG_INPUT_NREGS; i++)
    {
        g_reg_map.input_regs[i] = 0x0000;
    }
    
    for(int i = 0; i < REG_COIL_NREGS; i++)
    {
        g_reg_map.coil_status[i] = 0x00;
    }
    
    for(int i = 0; i < REG_DISCRETE_NREGS; i++)
    {
        g_reg_map.discrete_inputs[i] = 0x00;
    }
}

// 处理MODBUS帧
void Modbus_ProcessFrame(uint8_t *rx_buf, uint16_t rx_len, uint8_t *tx_buf, uint16_t *tx_len)
{
    ModbusFrame frame;
    uint16_t crc_calc, crc_recv;
    
    *tx_len = 0;
    
    // 检查帧长度
    if(rx_len < 4)
    {
        g_comm_status.error_frames++;
        return;
    }
    
    // 解析帧头
    frame.slave_id = rx_buf[0];
    frame.function_code = rx_buf[1];
    
    // 检查从站地址
    if(frame.slave_id != MODBUS_SLAVE_ID && frame.slave_id != 0x00)  // 0x00为广播地址
    {
        return;  // 不是发给本设备的帧
    }
    
    // 检查CRC
    crc_calc = Modbus_CRC16(rx_buf, rx_len - 2);
    crc_recv = (rx_buf[rx_len-2] << 8) | rx_buf[rx_len-1];
    
    if(crc_calc != crc_recv)
    {
        g_comm_status.crc_errors++;
        return;
    }
    
    // 解析地址和数据
    frame.start_addr = (rx_buf[2] << 8) | rx_buf[3];
    frame.reg_count = (rx_buf[4] << 8) | rx_buf[5];
    
    // 根据功能码处理
    switch(frame.function_code)
    {
        case MB_FUNC_READ_HOLDING_REG:
            Modbus_ReadHoldingRegisters(&frame, tx_buf, tx_len);
            break;
            
        case MB_FUNC_READ_INPUT_REG:
            Modbus_ReadInputRegisters(&frame, tx_buf, tx_len);
            break;
            
        case MB_FUNC_WRITE_SINGLE_REG:
            Modbus_WriteSingleRegister(&frame, tx_buf, tx_len);
            break;
            
        case MB_FUNC_WRITE_MULTIPLE_REG:
            Modbus_WriteMultipleRegisters(&frame, rx_buf, tx_buf, tx_len);
            break;
            
        case MB_FUNC_READ_COILS:
            Modbus_ReadCoils(&frame, tx_buf, tx_len);
            break;
            
        case MB_FUNC_WRITE_SINGLE_COIL:
            Modbus_WriteSingleCoil(&frame, tx_buf, tx_len);
            break;
            
        default:
            Modbus_HandleException(&frame, tx_buf, tx_len);
            break;
    }
}

// 计算CRC16校验
uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t length)
{
    uint16_t crc = 0xFFFF;
    uint8_t i, j;
    
    for(i = 0; i < length; i++)
    {
        crc ^= buffer[i];
        for(j = 0; j < 8; j++)
        {
            if(crc & 0x0001)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    
    return crc;
}

// 发送MODBUS响应
void Modbus_SendResponse(uint8_t *tx_buf, uint16_t tx_len)
{
    uint16_t crc;
    
    // 计算CRC
    crc = Modbus_CRC16(tx_buf, tx_len);
    tx_buf[tx_len++] = crc & 0xFF;
    tx_buf[tx_len++] = (crc >> 8) & 0xFF;
    
    // 发送数据
    USART1_SendData(tx_buf, tx_len);
}

// 处理异常响应
void Modbus_HandleException(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len)
{
    tx_buf[0] = frame->slave_id;
    tx_buf[1] = frame->function_code | 0x80;  // 设置异常标志
    tx_buf[2] = frame->exception_code;
    *tx_len = 3;
}

// 读取保持寄存器
void Modbus_ReadHoldingRegisters(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len)
{
    uint16_t start_addr = frame->start_addr;
    uint16_t reg_count = frame->reg_count;
    uint16_t i;
    
    // 检查地址范围
    if(start_addr >= REG_HOLDING_NREGS || 
       start_addr + reg_count > REG_HOLDING_NREGS ||
       reg_count == 0 || reg_count > 125)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_ADDRESS;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 构建响应帧
    tx_buf[0] = frame->slave_id;
    tx_buf[1] = frame->function_code;
    tx_buf[2] = reg_count * 2;  // 字节数
    
    // 填充寄存器数据
    for(i = 0; i < reg_count; i++)
    {
        tx_buf[3 + i*2] = (g_reg_map.holding_regs[start_addr + i] >> 8) & 0xFF;
        tx_buf[4 + i*2] = g_reg_map.holding_regs[start_addr + i] & 0xFF;
    }
    
    *tx_len = 3 + reg_count * 2;
}

// 读取输入寄存器
void Modbus_ReadInputRegisters(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len)
{
    uint16_t start_addr = frame->start_addr;
    uint16_t reg_count = frame->reg_count;
    uint16_t i;
    
    // 检查地址范围
    if(start_addr >= REG_INPUT_NREGS || 
       start_addr + reg_count > REG_INPUT_NREGS ||
       reg_count == 0 || reg_count > 125)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_ADDRESS;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 构建响应帧
    tx_buf[0] = frame->slave_id;
    tx_buf[1] = frame->function_code;
    tx_buf[2] = reg_count * 2;  // 字节数
    
    // 填充寄存器数据
    for(i = 0; i < reg_count; i++)
    {
        tx_buf[3 + i*2] = (g_reg_map.input_regs[start_addr + i] >> 8) & 0xFF;
        tx_buf[4 + i*2] = g_reg_map.input_regs[start_addr + i] & 0xFF;
    }
    
    *tx_len = 3 + reg_count * 2;
}

// 写单个寄存器
void Modbus_WriteSingleRegister(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len)
{
    uint16_t start_addr = frame->start_addr;
    uint16_t reg_value;
    
    // 检查地址范围
    if(start_addr >= REG_HOLDING_NREGS)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_ADDRESS;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 获取写入值
    reg_value = (rx_buffer[4] << 8) | rx_buffer[5];
    
    // 检查写入值范围
    if(reg_value > 65535)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_VALUE;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 写入寄存器
    g_reg_map.holding_regs[start_addr] = reg_value;
    
    // 构建响应帧(回显请求)
    memcpy(tx_buf, rx_buffer, 6);
    *tx_len = 6;
}

// 写多个寄存器
void Modbus_WriteMultipleRegisters(ModbusFrame *frame, uint8_t *rx_buf, uint8_t *tx_buf, uint16_t *tx_len)
{
    uint16_t start_addr = frame->start_addr;
    uint16_t reg_count = frame->reg_count;
    uint8_t byte_count = rx_buf[6];
    uint16_t i;
    
    // 检查地址范围
    if(start_addr >= REG_HOLDING_NREGS || 
       start_addr + reg_count > REG_HOLDING_NREGS ||
       reg_count == 0 || reg_count > 123 ||
       byte_count != reg_count * 2)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_ADDRESS;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 写入寄存器
    for(i = 0; i < reg_count; i++)
    {
        g_reg_map.holding_regs[start_addr + i] = (rx_buf[7 + i*2] << 8) | rx_buf[8 + i*2];
    }
    
    // 构建响应帧
    tx_buf[0] = frame->slave_id;
    tx_buf[1] = frame->function_code;
    tx_buf[2] = (start_addr >> 8) & 0xFF;
    tx_buf[3] = start_addr & 0xFF;
    tx_buf[4] = (reg_count >> 8) & 0xFF;
    tx_buf[5] = reg_count & 0xFF;
    *tx_len = 6;
}

// 读取线圈状态
void Modbus_ReadCoils(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len)
{
    uint16_t start_addr = frame->start_addr;
    uint16_t coil_count = frame->reg_count;
    uint16_t byte_count = (coil_count + 7) / 8;
    uint16_t i, j;
    
    // 检查地址范围
    if(start_addr >= REG_COIL_NREGS || 
       start_addr + coil_count > REG_COIL_NREGS ||
       coil_count == 0 || coil_count > 2000)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_ADDRESS;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 构建响应帧
    tx_buf[0] = frame->slave_id;
    tx_buf[1] = frame->function_code;
    tx_buf[2] = byte_count;  // 字节数
    
    // 填充线圈数据
    memset(&tx_buf[3], 0, byte_count);
    for(i = 0; i < coil_count; i++)
    {
        if(g_reg_map.coil_status[start_addr + i])
        {
            tx_buf[3 + i/8] |= (1 << (i % 8));
        }
    }
    
    *tx_len = 3 + byte_count;
}

// 写单个线圈
void Modbus_WriteSingleCoil(ModbusFrame *frame, uint8_t *tx_buf, uint16_t *tx_len)
{
    uint16_t start_addr = frame->start_addr;
    uint16_t coil_value = (rx_buffer[4] << 8) | rx_buffer[5];
    
    // 检查地址范围
    if(start_addr >= REG_COIL_NREGS)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_ADDRESS;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 检查线圈值(只能是0x0000或0xFF00)
    if(coil_value != 0x0000 && coil_value != 0xFF00)
    {
        frame->exception_code = MB_EX_ILLEGAL_DATA_VALUE;
        Modbus_HandleException(frame, tx_buf, tx_len);
        return;
    }
    
    // 写入线圈
    g_reg_map.coil_status[start_addr] = (coil_value == 0xFF00) ? 1 : 0;
    
    // 构建响应帧(回显请求)
    memcpy(tx_buf, rx_buffer, 6);
    *tx_len = 6;
}

// 更新寄存器数据(模拟传感器数据)
void Modbus_UpdateRegisters(void)
{
    static uint32_t last_update = 0;
    uint32_t current_time = HAL_GetTick();
    
    // 每秒更新一次
    if(current_time - last_update >= 1000)
    {
        last_update = current_time;
        
        // 更新温度值(模拟变化)
        g_reg_map.holding_regs[1] += 1;
        if(g_reg_map.holding_regs[1] > 350) g_reg_map.holding_regs[1] = 250;
        
        // 更新运行时间
        g_reg_map.holding_regs[2] += 1;
        
        // 更新输入寄存器(模拟传感器数据)
        g_reg_map.input_regs[0] = g_reg_map.holding_regs[1];  // 温度
        g_reg_map.input_regs[1] = g_reg_map.holding_regs[6];  // 实际速度
        g_reg_map.input_regs[2] = g_reg_map.holding_regs[7];  // 报警阈值
    }
}

3.4 USART驱动 (usart.c)

#include "usart.h"
#include "modbus_slave.h"

volatile uint8_t g_frame_received = 0;
volatile uint8_t g_frame_timeout = 0;
uint8_t rx_buffer[256];
uint16_t rx_index = 0;

// USART1初始化
void USART1_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置USART1 TX (PA9) 和 RX (PA10)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置USART参数
    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_Even;  // MODBUS使用偶校验
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
    
    // 配置中断
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 使能接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    // 使能USART
    USART_Cmd(USART1, ENABLE);
}

// USART1发送数据
void USART1_SendData(uint8_t *data, uint16_t length)
{
    uint16_t i;
    
    // 使能RS485发送
    GPIO_SetBits(GPIOA, GPIO_Pin_1);  // DE/RE高电平,发送模式
    
    // 发送数据
    for(i = 0; i < length; i++)
    {
        USART_SendData(USART1, data[i]);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    }
    
    // 等待发送完成
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
    
    // 禁用RS485发送
    GPIO_ResetBits(GPIOA, GPIO_Pin_1);  // DE/RE低电平,接收模式
}

// USART1中断服务函数
void USART1_IRQHandler(void)
{
    uint8_t data;
    
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        // 读取接收到的数据
        data = USART_ReceiveData(USART1);
        
        // 存储到接收缓冲区
        if(rx_index < 256)
        {
            rx_buffer[rx_index++] = data;
        }
        
        // 重置超时计数器
        g_frame_timeout = MODBUS_FRAME_TIMEOUT;
    }
}

3.5 定时器驱动 (timer.c)

#include "timer.h"
#include "modbus_slave.h"

volatile uint8_t g_frame_timeout = 0;
extern volatile uint8_t g_frame_received;
extern uint16_t rx_index;

// TIM3初始化(用于3.5字符超时检测)
void TIM3_Init(uint16_t period_ms)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 使能TIM3时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    
    // 配置TIM3
    TIM_TimeBaseStructure.TIM_Period = period_ms * 10 - 1;  // 10kHz计数,period_ms毫秒
    TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1;        // 72MHz/7200 = 10kHz
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    
    // 配置中断
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 使能更新中断
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    
    // 启动TIM3
    TIM_Cmd(TIM3, ENABLE);
}

// TIM3中断服务函数
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
        
        // 检查是否超时
        if(g_frame_timeout > 0)
        {
            g_frame_timeout--;
            
            // 超时检测
            if(g_frame_timeout == 0 && rx_index > 0)
            {
                g_frame_received = 1;  // 设置帧接收完成标志
            }
        }
    }
}

四、测试与验证

4.1 Modbus Poll测试配置

连接设置:
- 连接: Serial Port
- 串口: COM1
- 波特率: 9600
- 数据位: 8
- 奇偶校验: Even
- 停止位: 1
- Mode: RTU

从站设置:
- Slave ID: 1
- Function: 03 Read Holding Registers
- Address: 0000
- Quantity: 10

4.2 预期测试结果

寄存器数据:
40001: 0001 (从站ID)
40002: 0250 (温度25.0℃)
40003: 1000 (运行时间1000小时)
40004: 0000 (无错误)
40005: 0001 (控制命令)
40006: 0050 (设定速度50%)
40007: 0048 (实际速度48%)
40008: 0300 (报警阈值30.0℃)

4.3 串口调试助手测试

# Python测试脚本
import serial
import struct
import time

def modbus_read_holding_registers(port, slave_id, start_addr, reg_count):
    """读取保持寄存器"""
    ser = serial.Serial(port, 9600, parity='E', timeout=1)
    
    # 构建请求帧
    request = struct.pack('>BBHH', slave_id, 0x03, start_addr, reg_count)
    crc = calculate_crc(request)
    request += struct.pack('<H', crc)
    
    # 发送请求
    ser.write(request)
    time.sleep(0.1)
    
    # 读取响应
    response = ser.read(5 + reg_count * 2)
    ser.close()
    
    return response

# 测试
response = modbus_read_holding_registers('COM1', 0x01, 0x0000, 0x0008)
print(f"Response: {response.hex()}")

参考代码 基于STM32F103的RS485 MODBUS从站例程 www.youwenfan.com/contentcnv/72249.html

五、编译与部署

5.1 Keil工程配置

Target: STM32F103C8
Device: STM32F103C8T6
C/C++:
  Include Paths: .\inc; .\User; .\Hardware
  Define: STM32F10X_MD, USE_STDPERIPH_DRIVER
Linker:
  Scatter File: STM32_FLASH.sct
Debug:
  Debugger: ST-Link Debugger

5.2 烧录步骤

# 使用ST-Link Utility
1. 连接ST-Link到JTAG接口
2. 打开STM32 ST-Link Utility
3. Target -> Connect
4. Target -> Program & Verify
5. 选择编译生成的.hex文件
6. Start

六、故障排除

问题 原因 解决方法
无法通信 波特率不匹配 检查双方波特率设置
数据错误 校验位错误 确认使用偶校验
帧不完整 超时时间太短 增加MODBUS_FRAME_TIMEOUT值
无响应 从站地址错误 确认从站ID为0x01
乱码 接线错误 检查A/B线是否接反
posted @ 2026-05-26 16:19  chen_yig  阅读(40)  评论(0)    收藏  举报