stm32f103rct6中使用串口1 DMA通信程序含异常处理
使用keil5写一个关于stm32f103rct6中使用串口1通信程序,通信使用DMA收发,使用HAL库,实现一个功能为收到数据后马上回传同样的数据。
以下是针对STM32F103RCT6增强错误处理的完整DMA-UART回环通信实现,包含详细的错误分类处理和恢复机制:
#include "stm32f1xx_hal.h"
#include <string.h>#define RX_BUFFER_SIZE 256
#define ERROR_FLASH_DELAY 100// 定义错误类型标志位
typedef enum {COM_ERROR_NONE = 0x00,COM_ERROR_UART_ORE = 0x01, // 溢出错误COM_ERROR_UART_NE = 0x02, // 噪声错误COM_ERROR_UART_FE = 0x04, // 帧错误COM_ERROR_UART_PE = 0x08, // 奇偶校验错误COM_ERROR_DMA_TE = 0x10, // DMA传输错误COM_ERROR_DMA_FE = 0x20 // DMA FIFO错误
} COM_ErrorTypeDef;volatile uint32_t com_errors = COM_ERROR_NONE;
uint8_t rx_buffer[RX_BUFFER_SIZE];
volatile uint16_t received_len = 0;
volatile uint8_t is_transmitting = 0;UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
void COM_ErrorHandler(uint32_t error_code);int main(void) {HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();// 启用UART错误中断__HAL_UART_ENABLE_IT(&huart1, UART_IT_ERR);// 启动初始接收HAL_UART_Receive_DMA(&huart1, rx_buffer, RX_BUFFER_SIZE);__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);while (1) {// 错误状态监测if(com_errors != COM_ERROR_NONE) {COM_ErrorHandler(com_errors);com_errors = COM_ERROR_NONE; // 清除错误寄存器}// 系统指示灯(正常运行时快速闪烁)HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);HAL_Delay(100);}
}/* 增强型UART中断处理 */
void USART1_IRQHandler(void) {uint32_t isr_flags = READ_REG(huart1.Instance->SR);// 处理IDLE中断if((isr_flags & USART_SR_IDLE) && (__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_IDLE))) {__HAL_UART_CLEAR_IDLEFLAG(&huart1);if(!is_transmitting) {HAL_UART_DMAStop(&huart1);received_len = RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx);// 启动发送前检查DMA状态if(hdma_usart1_tx.State == HAL_DMA_STATE_READY) {is_transmitting = 1;HAL_UART_Transmit_DMA(&huart1, rx_buffer, received_len);}}}// 错误中断处理if((isr_flags & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) && (__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_ERR))) {// 记录具体错误类型if(isr_flags & USART_SR_ORE) com_errors |= COM_ERROR_UART_ORE;if(isr_flags & USART_SR_NE) com_errors |= COM_ERROR_UART_NE;if(isr_flags & USART_SR_FE) com_errors |= COM_ERROR_UART_FE;if(isr_flags & USART_SR_PE) com_errors |= COM_ERROR_UART_PE;// 必须读取SR和DR寄存器来清除ORE标志volatile uint32_t temp = huart1.Instance->SR;temp = huart1.Instance->DR;(void)temp;// 立即停止DMA防止错误扩散HAL_UART_DMAStop(&huart1);huart1.RxState = HAL_UART_STATE_READY;huart1.TxState = HAL_UART_STATE_READY;}HAL_UART_IRQHandler(&huart1);
}/* 发送完成回调 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {if(huart->Instance == USART1) {is_transmitting = 0;// 重新启动接收前检查DMA状态if(hdma_usart1_rx.State == HAL_DMA_STATE_READY) {memset(rx_buffer, 0, RX_BUFFER_SIZE);HAL_UART_Receive_DMA(huart, rx_buffer, RX_BUFFER_SIZE);}}
}/* 增强型DMA错误回调 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if(huart->Instance == USART1) {// 获取详细DMA错误状态if(__HAL_DMA_GET_FLAG(hdma_usart1_rx.Instance, DMA_FLAG_TE1)) com_errors |= COM_ERROR_DMA_TE;if(__HAL_DMA_GET_FLAG(hdma_usart1_rx.Instance, DMA_FLAG_FE1)) com_errors |= COM_ERROR_DMA_FE;// 清除DMA错误标志__HAL_DMA_CLEAR_FLAG(hdma_usart1_rx.Instance, DMA_FLAG_HT1 | DMA_FLAG_TC1 | DMA_FLAG_TE1 | DMA_FLAG_FE1);__HAL_DMA_CLEAR_FLAG(hdma_usart1_tx.Instance, DMA_FLAG_HT1 | DMA_FLAG_TC1 | DMA_FLAG_TE1 | DMA_FLAG_FE1);// 强制重置DMAHAL_DMA_DeInit(&hdma_usart1_rx);HAL_DMA_DeInit(&hdma_usart1_tx);MX_DMA_Init();}
}/* 错误处理函数 */
void COM_ErrorHandler(uint32_t error_code) {// 错误指示灯模式(不同错误不同闪烁方式)while(1) {for(uint8_t i=0; i<(error_code & 0xF); i++) {HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);HAL_Delay(ERROR_FLASH_DELAY);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);HAL_Delay(ERROR_FLASH_DELAY);}HAL_Delay(ERROR_FLASH_DELAY * 4);// 尝试自动恢复if(error_code & (COM_ERROR_UART_ORE | COM_ERROR_UART_NE | COM_ERROR_UART_FE | COM_ERROR_UART_PE)) {HAL_UART_DeInit(&huart1);MX_USART1_UART_Init();HAL_UART_Receive_DMA(&huart1, rx_buffer, RX_BUFFER_SIZE);return; // 退出错误循环}}
}/* 其他初始化函数保持原有结构,需确保以下增强配置:*/
static void MX_DMA_Init(void) {// 原有配置基础上增加DMA错误中断使能hdma_usart1_rx.Instance = DMA1_Channel5;// ...hdma_usart1_rx.Init.Mode = DMA_NORMAL;hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE; // 启用FIFOhdma_usart1_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;hdma_usart1_rx.Init.MemBurst = DMA_MBURST_INC4;hdma_usart1_rx.Init.PeriphBurst = DMA_PBURST_INC4;HAL_DMA_Init(&hdma_usart1_rx);// 使能DMA错误中断__HAL_DMA_ENABLE_IT(&hdma_usart1_rx, DMA_IT_TE | DMA_IT_FE);// TX DMA类似配置hdma_usart1_tx.Instance = DMA1_Channel4;// ...__HAL_DMA_ENABLE_IT(&hdma_usart1_tx, DMA_IT_TE | DMA_IT_FE);
}
关键增强点说明:
-
错误分类检测机制
typedef enum {COM_ERROR_NONE = 0x00,COM_ERROR_UART_ORE = 0x01, // 溢出错误COM_ERROR_UART_NE = 0x02, // 噪声错误COM_ERROR_UART_FE = 0x04, // 帧错误COM_ERROR_UART_PE = 0x08, // 奇偶校验错误COM_ERROR_DMA_TE = 0x10, // DMA传输错误COM_ERROR_DMA_FE = 0x20 // DMA FIFO错误 } COM_ErrorTypeDef;
使用位域标志区分不同错误类型,支持多错误同时检测
-
UART错误中断处理
// 在USART1_IRQHandler中: if((isr_flags & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) && (__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_ERR))) {// 详细错误记录// 强制清除ORE标志的特殊操作// 立即停止DMA }
精确捕获所有UART物理层错误类型
-
DMA错误处理增强
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {// 检查DMA传输错误标志位// 清除所有DMA相关标志// 完全重新初始化DMA }
处理DMA传输错误和FIFO错误,并进行硬件级复位
-
状态机保护机制
volatile uint8_t is_transmitting = 0; // 发送状态标志 if(!is_transmitting) { // 仅在非发送状态处理新数据// 启动发送前检查DMA状态if(hdma_usart1_tx.State == HAL_DMA_STATE_READY) {is_transmitting = 1;// 启动发送} }
防止在发送过程中处理新数据导致状态混乱
-
自动恢复机制
void COM_ErrorHandler(uint32_t error_code) {// 根据错误类型尝试自动恢复if(error_code & UART_ERRORS_MASK) {HAL_UART_DeInit(&huart1);MX_USART1_UART_Init();HAL_UART_Receive_DMA(...);return; // 尝试退出错误循环} }
针对可恢复错误自动重启外设,提升系统鲁棒性
-
DMA FIFO配置优化
hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE; hdma_usart1_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_usart1_rx.Init.MemBurst = DMA_MBURST_INC4;
通过FIFO配置提升大数据量传输的稳定性
错误处理流程图
graph TDA[错误发生] --> B{错误类型判断}B -->|UART物理层错误| C[记录错误类型]B -->|DMA传输错误| D[记录DMA错误标志]C --> E[清除UART错误标志]D --> F[清除DMA错误标志]E --> G[停止所有DMA传输]F --> GG --> H[重置外设状态]H --> I[进入错误处理循环]I --> J{可恢复错误?}J -->|是| K[重启外设初始化]K --> L[恢复正常运行]J -->|否| M[保持错误状态指示]
该实现方案特点:
- 采用分层错误处理机制:物理层错误、协议层错误、DMA传输错误分开处理
- 错误状态寄存器支持多错误同时记录
- 关键操作前加入外设状态检查(如检查DMA状态是否为READY)
- 自动恢复机制最大限度减少人工干预需求
- 通过FIFO配置提升抗干扰能力
- 详细的错误指示系统(LED闪烁模式反映不同错误类型)
建议配合硬件设计加入以下保护措施:
- 在USART线路加入TVS二极管防止过压
- 使用磁珠隔离模拟地和数字地
- 确保时钟源稳定性(建议使用外部晶振)
- PCB布局时保持USART走线远离高频信号线