STM32F103串口角度代码解析Modbus


发布时间:

2025-08-13

STM32F103C8T6 串口中断+DMA获取传感器数据方案

基于STM32F103C8T6通过串口中断+DMA获取传感器数据的实现方案

方案严格遵循手册中Modbus协议规范和数据格式要求

一、硬件配置说明

  1. 串口选择:使用USART1(PA9发送、PA10接收),波特率115200(默认),8位数据位,无校验位,1位停止位(8N1)。
  2. RS485接口:通过MAX485芯片实现电平转换,控制引脚(如PA8)用于切换收发模式。
  3. DMA配置:使用DMA1通道5(对应USART1接收),循环模式,数据宽度8位。

二、软件核心代码

1. 数据结构定义

C语言代码:
#include "stm32f10x.h"

// 传感器数据结构体
typedef struct {
    float roll;       // 横滚角 (°)
    float pitch;      // 俯仰角 (°)
    float yaw;        // 方位角 (°)
    float x_plane;    // X轴面角 (°)
    float y_plane;    // Y轴面角 (°)
    float z_plane;    // Z轴面角 (°)
} SensorData;

SensorData sensor_data = {0};
uint8_t rx_buf[16] = {0};  // DMA接收缓冲区(Modbus响应最大长度)
uint8_t tx_buf[8] = {0};   // 发送缓冲区(Modbus命令)

2. 初始化配置

C语言代码:
void USART1_DMA_Init(void) {
    // 1. 串口初始化
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN;
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 引脚配置:PA9推挽输出(TX),PA10浮空输入(RX)
    GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9 | 
                    GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
    GPIOA->CRH |= GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1;  // TX: 50MHz推挽
    GPIOA->CRH |= GPIO_CRH_CNF10_0;                    // RX: 浮空输入

    // 波特率配置:115200(PCLK2=72MHz,USARTDIV=72000000/(16*115200)=39.0625)
    USART1->BRR = 0x2710;  // 0x2710 = 39.0625 * 16
    USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;  // 使能收发
    USART1->CR3 |= USART_CR3_DMAR;  // 使能DMA接收

    // 2. DMA配置(接收)
    DMA1_Channel5->CPAR = (uint32_t)&USART1->DR;  // 外设地址(串口数据寄存器)
    DMA1_Channel5->CMAR = (uint32_t)rx_buf;       // 内存地址(接收缓冲区)
    DMA1_Channel5->CNDTR = 16;                    // 接收最大长度
    DMA1_Channel5->CCR |= DMA_CCR5_MINC | DMA_CCR5_CIRC | DMA_CCR5_EN;  // 内存递增+循环模式

    // 3. 中断配置(接收完成后处理)
    USART1->CR1 |= USART_CR1_IDLEIE;  // 使能空闲中断
    NVIC_EnableIRQ(USART1_IRQn);
}

3. Modbus命令发送(读取输入寄存器)

C语言代码:
// 发送Modbus命令:读取0000H-0008H寄存器(共9个,包含所需6个角度)
void SendReadCommand(void) {
    // 命令格式:从站地址(05) + 功能码(04) + 起始地址(0000) + 寄存器数量(0009) + CRC校验
    tx_buf[0] = 0x05;         // 从站地址(默认5)
    tx_buf[1] = 0x04;         // 功能码(读取输入寄存器)
    tx_buf[2] = 0x00;         // 起始地址高字节(0000H)
    tx_buf[3] = 0x00;         // 起始地址低字节
    tx_buf[4] = 0x00;         // 数量高字节(9个寄存器)
    tx_buf[5] = 0x09;         // 数量低字节
    uint16_t crc = CRC16(tx_buf, 6);  // 计算CRC校验
    tx_buf[6] = crc & 0xFF;   // CRC低字节
    tx_buf[7] = crc >> 8;     // CRC高字节

    // 切换为发送模式(控制MAX485方向引脚)
    GPIOA->BSRR = GPIO_BSRR_BS8;  // PA8置高(发送)
    for (int i = 0; i < 8; i++) {
        while (!(USART1->SR & USART_SR_TXE));  // 等待发送缓冲区空
        USART1->DR = tx_buf[i];
    }
    while (!(USART1->SR & USART_SR_TC));       // 等待发送完成
    GPIOA->BSRR = GPIO_BSRR_BR8;  // PA8置低(接收)
}

// CRC16校验计算(按手册算法实现)
uint16_t CRC16(uint8_t *data, uint8_t len) {
    uint16_t crc = 0xFFFF;
    for (uint8_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x01) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

4. 中断处理与数据解析

C语言代码:
// 串口空闲中断(接收完成后触发)
void USART1_IRQHandler(void) {
    if (USART1->SR & USART_SR_IDLE) {
        USART1->DR;  // 清除空闲中断标志
        DMA1_Channel5->CCR &= ~DMA_CCR5_EN;  // 关闭DMA
        
        // 解析响应帧(格式:地址+功能码+字节数+数据+CRC)
        if (rx_buf[0] == 0x05 && rx_buf[1] == 0x04 && rx_buf[2] == 0x12) {
            // 提取Roll(0000H)、Pitch(0001H)、Yaw(0002H)
            int16_t roll_raw = (rx_buf[3] << 8) | rx_buf[4];
            int16_t pitch_raw = (rx_buf[5] << 8) | rx_buf[6];
            int16_t yaw_raw = (rx_buf[7] << 8) | rx_buf[8];
            
            // 提取X轴面角(0006H)、Y轴面角(0007H)、Z轴面角(0008H)
            int16_t x_plane_raw = (rx_buf[15] << 8) | rx_buf[16];
            int16_t y_plane_raw = (rx_buf[17] << 8) | rx_buf[18];
            int16_t z_plane_raw = (rx_buf[19] << 8) | rx_buf[20];
            
            // 转换为实际角度(乘以0.01缩放系数)
            sensor_data.roll = roll_raw * 0.01f;
            sensor_data.pitch = pitch_raw * 0.01f;
            sensor_data.yaw = yaw_raw * 0.01f;
            sensor_data.x_plane = x_plane_raw * 0.01f;
            sensor_data.y_plane = y_plane_raw * 0.01f;
            sensor_data.z_plane = z_plane_raw * 0.01f;
        }

        // 重置DMA
        DMA1_Channel5->CNDTR = 16;
        DMA1_Channel5->CCR |= DMA_CCR5_EN;
    }
}

三、关键说明

寄存器地址映射:

  • Roll(0000H)、Pitch(0001H)、Yaw(0002H)
  • X轴面角(0006H)、Y轴面角(0007H)、Z轴面角(0008H)

数据格式:

  • 16位有符号整数(Int16),高字节在前(大端模式)
  • 实际角度 = 原始值 × 0.01°(缩放系数)

通讯时序:

  • 发送命令后需等待传感器响应(约10-50ms)
  • 响应帧长度为:1(地址)+1(功能码)+1(字节数)+18(数据,9×2字节)+2(CRC)=23字节

四、使用流程

  1. 初始化串口DMA和GPIO
  2. 调用SendReadCommand()发送读取命令
  3. 传感器响应后,中断自动解析数据到sensor_data结构体
  4. 主程序中直接使用解析后的角度值

此方案严格遵循手册中的Modbus协议和数据格式要求,可稳定获取所需角度数据,适用于STM32F103C8T6平台。