低速串行协议进阶:UART中断+DMA与复杂帧处理


低速串行协议进阶:UART中断+DMA与复杂帧处理(STM32F103ZET6标准库)

——工业级抗干扰、FreeRTOS多任务、Modbus RTU实战

目录

  • 低速串行协议进阶:UART中断+DMA与复杂帧处理(STM32F103ZET6标准库)
    • 本章目标(STM32F103ZET6专属进阶)
    • 一、UART高级技术:从轮询到DMA+中断
      • 1.1 DMA传输:解放CPU(标准库实现)
        • 1.1.1 硬件连接(USART1+DMA1)
        • 1.1.2 标准库代码(DMA初始化)
        • 1.1.3 优势对比
      • 1.2 空闲中断:处理不定长帧
        • 1.2.1 原理
        • 1.2.2 标准库代码(中断配置)
      • 1.3 多缓冲管理:环形缓冲区设计
    • 二、工业级抗干扰设计:RS485差分+隔离
      • 2.1 STM32配置(收发控制)
    • 三、串口调试工具实战:示波器+逻辑分析仪
      • 3.1 示波器测量(RS485波形)
      • 3.2 逻辑分析仪抓包(Saleae)
    • 四、FreeRTOS多任务集成:UART数据解耦
      • 4.1 任务架构
      • 4.2 标准库+FreeRTOS代码
    • 五、Modbus RTU从机实战:三要素完整应用
      • 5.1 协议规格(工业级)
      • 5.2 标准库实现(从机响应)
    • 六、时序精度优化:±0.5%波特率控制
      • 6.1 波特率计算(STM32F103ZET6)
      • 6.2 晶振校准(硬件补偿)
    • 七、总结与调试技巧
      • 7.1 本章重点
      • 7.2 调试技巧
      • 7.3 常见问题Q&A
    • 课后作业(工业级UART实战)

本章目标(STM32F103ZET6专属进阶)

  1. 掌握UART高级技术:DMA传输、空闲中断、多缓冲管理
  2. 实现工业级抗干扰设计(RS485差分+隔离电路)
  3. 精通串口调试工具(示波器+逻辑分析仪)的实战应用
  4. 结合FreeRTOS实现多任务UART通信(Modbus RTU从机)
  5. 优化时序精度(±0.5%波特率误差控制)

一、UART高级技术:从轮询到DMA+中断

1.1 DMA传输:解放CPU(标准库实现)

1.1.1 硬件连接(USART1+DMA1)
STM32引脚 外设 DMA通道 说明
PA9(TX) UART_TX DMA1_CH4 发送通道
PA10(RX) UART_RX DMA1_CH5 接收通道
1.1.2 标准库代码(DMA初始化)
void UART_DMA_Init(void) {  
  DMA_InitTypeDef DMA_InitStructure;  
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  

  // 配置接收DMA(USART1_RX)  
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;  
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buf;  
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  
  DMA_InitStructure.DMA_BufferSize = RX_BUF_SIZE;  
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 环形缓冲区  
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;  
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);  
  DMA_Cmd(DMA1_Channel5, ENABLE);  

  // 使能UART接收DMA  
  USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);  
}  
1.1.3 优势对比
模式 CPU占用 最大带宽 适用场景
轮询 100% 低(≤100KB/s) 简单调试
中断 <1% 中(≤500KB/s) 中等数据量
DMA 0% 高(≤2MB/s) 高速连续数据(传感器阵列)

1.2 空闲中断:处理不定长帧

1.2.1 原理
  • UART接收完一帧后进入空闲状态,触发中断(USART_IT_IDLE
  • 通过DMA+空闲中断实现无阻塞接收
1.2.2 标准库代码(中断配置)
void UART_Idle_Init(void) {  
  USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能空闲中断  
  NVIC_InitTypeDef NVIC_InitStructure;  
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  
  NVIC_InitStructure.NVIC_IRQChannelPriority = 1;  
  NVIC_Init(&NVIC_InitStructure);  
}  

// 中断服务函数  
void USART1_IRQHandler(void) {  
  if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) {  
    USART_ClearITPendingBit(USART1, USART_IT_IDLE); // 清除中断标志  
    uint16_t len = RX_BUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);  
    if (len > 0) {  
      xQueueSend(uart_queue, &rx_buf[RX_BUF_SIZE - len], 0); // 发送到FreeRTOS队列  
    }  
  }  
}  

1.3 多缓冲管理:环形缓冲区设计

#define RX_BUF_SIZE 256  
uint8_t rx_buf[RX_BUF_SIZE]; // 环形缓冲区  
__IO uint16_t rx_write = 0, rx_read = 0; // 读写指针  

// 写入数据(DMA中断)  
void rx_buf_write(uint8_t data) {  
  rx_buf[rx_write++] = data;  
  rx_write %= RX_BUF_SIZE;  
  if (rx_write == rx_read) rx_read++; // 溢出处理  
}  

// 读取数据(应用层)  
uint16_t rx_buf_read(uint8_t* buf, uint16_t len) {  
  uint16_t i, count = 0;  
  while (rx_read != rx_write && count < len) {  
    buf[count++] = rx_buf[rx_read++];  
    rx_read %= RX_BUF_SIZE;  
  }  
  return count;  
}  

二、工业级抗干扰设计:RS485差分+隔离

2.1 STM32配置(收发控制)

#define RS485_RE_DE PB1 // 收发控制引脚  
void RS485_Init(void) {  
  GPIO_InitTypeDef GPIO_InitStructure;  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;  
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  GPIO_Init(GPIOB, &GPIO_InitStructure);  
  GPIO_SetBits(GPIOB, GPIO_Pin_1); // 默认接收模式(RE/DE低电平)  
}  

// 发送模式(DE=1, RE=1)  
#define RS485_TX_EN() GPIO_SetBits(GPIOB, GPIO_Pin_1)  
// 接收模式(DE=0, RE=0)  
#define RS485_RX_EN() GPIO_ResetBits(GPIOB, GPIO_Pin_1)  

三、串口调试工具实战:示波器+逻辑分析仪

3.1 示波器测量(RS485波形)

  • 正常波形
    • A/B线差分信号(200mV~5V)
    • 波特率验证(115200bps:8.68μs/bit)
  • 异常处理
    • 信号畸变:检查终端电阻(120Ω)是否匹配
    • 共模电压超标:增加隔离电容(100nF)

3.2 逻辑分析仪抓包(Saleae)

  • Modbus RTU帧解析
    [从机地址:01] [功能码:03] [起始地址:0000] [数据长度:0002] [CRC:840A]  
    
  • 工具命令
    # 自动识别Modbus协议(Saleae Lua脚本)  
    function decode_modbus(sample)  
      if sample[0] == 0x01 and sample[1] == 0x03 then  
        return "Read Holding Registers: " .. sample[2]..":"..sample[3]  
      end  
    end  
    

四、FreeRTOS多任务集成:UART数据解耦

4.1 任务架构

+-------------------+          +-----------------+  
| UART接收任务      |          | Modbus解析任务  |  
| (空闲中断+DMA)  ────Queue───> (协议解析+响应)|  
+-------------------+          +-----------------+  

4.2 标准库+FreeRTOS代码

// 创建队列  
QueueHandle_t uart_queue;  
#define UART_QUEUE_SIZE 16  
#define UART_ITEM_SIZE 256  

void uart_task(void* pvParameters) {  
  uint8_t rx_frame[UART_ITEM_SIZE];  
  while (1) {  
    if (xQueueReceive(uart_queue, &rx_frame, portMAX_DELAY)) {  
      xQueueSend(modbus_queue, &rx_frame, 0); // 转发到Modbus队列  
    }  
  }  
}  

void modbus_task(void* pvParameters) {  
  uint8_t frame[UART_ITEM_SIZE];  
  while (1) {  
    if (xQueueReceive(modbus_queue, &frame, portMAX_DELAY)) {  
      Modbus_ProcessFrame(frame); // 解析Modbus帧  
    }  
  }  
}  

// 初始化(main.c)  
int main(void) {  
  UART_DMA_Init();  
  RS485_Init();  
  uart_queue = xQueueCreate(UART_QUEUE_SIZE, UART_ITEM_SIZE);  
  xTaskCreate(uart_task, "UART Task", 256, NULL, 2, NULL);  
  xTaskCreate(modbus_task, "Modbus Task", 512, NULL, 1, NULL);  
  vTaskStartScheduler();  
}  

五、Modbus RTU从机实战:三要素完整应用

5.1 协议规格(工业级)

要素 内容
语法 RTU帧:从机地址(1B)+功能码(1B)+数据(NB)+CRC(2B)
语义 功能码:0x03(读寄存器)、0x06(写单个寄存器)
时序 字符间超时≤1.5字符时间,帧间超时≤3.5字符时间(115200bps:13μs/字符)

5.2 标准库实现(从机响应)

void Modbus_ProcessFrame(uint8_t* frame) {  
  uint8_t slave_addr = frame[0];  
  if (slave_addr != MY_SLAVE_ADDR) return; // 过滤非本机地址  

  uint8_t func_code = frame[1];  
  switch (func_code) {  
    case 0x03: // 读保持寄存器  
      {  
        uint16_t start_addr = (frame[2] << 8) | frame[3];  
        uint16_t quantity = (frame[4] << 8) | frame[5];  
        uint8_t* data = Modbus_ReadRegisters(start_addr, quantity);  
        uint8_t response[8 + quantity*2];  
        response[0] = slave_addr;  
        response[1] = func_code;  
        response[2] = quantity * 2;  
        memcpy(&response[3], data, quantity*2);  
        uint16_t crc = CRC16_Calculate(response, 3 + quantity*2);  
        response[3 + quantity*2] = crc >> 8;  
        response[4 + quantity*2] = crc & 0xFF;  
        RS485_TX_EN();  
        UART_DMA_Send(response, sizeof(response)); // DMA发送  
        RS485_RX_EN();  
      }  
      break;  
    // 其他功能码...  
  }  
}  

六、时序精度优化:±0.5%波特率控制

6.1 波特率计算(STM32F103ZET6)

// 公式:BRR = f_PCLK2 / (16 * BaudRate)  
#define BAUDRATE 115200  
uint32_t BRR = SystemCoreClock / (16 * BAUDRATE); // 72000000 / 1843200 = 39.0625  
USART_InitStructure.USART_BaudRate = BAUDRATE;  
USART_Init(USART1, &USART_InitStructure);  

// 实际波特率误差:  
// (72000000 / (16*39.0625)) - 115200 = 0 → 0%误差  

6.2 晶振校准(硬件补偿)

  • 使用外部高精度晶振(8MHz±20ppm)
  • 软件补偿:通过USART_SetPrescaler()动态调整

七、总结与调试技巧

7.1 本章重点

  • 高级UART:DMA环形缓冲+空闲中断(无阻塞接收)
  • 工业设计:RS485隔离电路(抗15kV ESD)+差分传输
  • 工具实战:示波器测波形→逻辑分析仪抓包→协议解析
  • FreeRTOS:任务解耦(接收→解析→响应流水线)

7.2 调试技巧

  1. DMA故障:检查DMA_ISR寄存器(TCIF传输完成标志)
  2. 隔离电路:测量隔离两端电压(确保5kV隔离阻抗>100MΩ)
  3. 时序问题:使用USART_GetFlagStatus(USART_FLAG_RXNE)调试接收超时

7.3 常见问题Q&A

Q:DMA传输时CPU能否休眠?
A:可以!DMA独立于CPU,配合__WFI()实现低功耗(功耗<1mA)

Q:Modbus帧间超时如何实现?
A:使用定时器测量最后一个字符接收时间,超时后触发帧解析

课后作业(工业级UART实战)

  1. DMA优化:实现双缓冲DMA(接收缓冲区1/2交替,避免中断锁)
  2. 隔离设计:添加电源隔离(DCP010505)和信号隔离(ADuM1201)
  3. FreeRTOS扩展:添加AT命令解析任务(处理串口指令+AT协议)

工业级通信心法
“抗干扰是工业通信的生命线,DMA是高速传输的引擎,FreeRTOS是复杂逻辑的调度器。三者结合,让STM32在恶劣环境中实现稳定、高效的通信。”


本章适配STM32F103ZET6特色
专属配置:DMA1通道5(USART1_RX)、FreeRTOS任务优先级(抢占优先级1/2)
标准库深度USART_DMACmd()DMA_Mode_Circular的工业级应用
行业标准:严格遵循Modbus RTU规范(TIA/EIA-485-A)

技术指标

  • 最大通信距离:1200米(485总线,120Ω终端电阻)
  • 抗干扰能力:±4kV浪涌(差模),±15kV ESD(人体模式)
  • 数据吞吐量:230.4KB/s(115200bps,8位数据)

你可能感兴趣的:(嵌入式通信协议自学实战专栏,#,基础通信协议,单片机,stm32,嵌入式软件,c语言)