[STM32F0] 在STM32F030F4P6这类资源有限的MCU上使用DMA进行串口通信

[复制链接]
168|10
七毛钱 发表于 2025-9-2 15:09 | 显示全部楼层 |阅读模式
在STM32F030F4P6这类资源有限的MCU上使用DMA进行串口通信,确实能显著提升效率,尤其适合处理高速或大数据量的场景(如传感器数据采集、无线模块通信等)。以下是关键实现步骤、优化技巧及常见问题解决方案:

一、DMA+UART的核心优势
零CPU占用:DMA自动完成数据搬运,CPU可处理其他任务(如控制算法、协议解析)。
低延迟:无需轮询等待,数据到达后立即触发DMA传输。
高吞吐量:适合连续数据流(如音频、视频),避免轮询丢失数据的风险。
二、硬件配置要点
1. 引脚复用设置
STM32F030F4P6的UART1默认引脚:
TX: PA9 (USART1_TX)
RX: PA10 (USART1_RX)
在CubeMX中需启用对应引脚的复用功能(Alternate Function为USART1_TX/RX)。
2. DMA通道分配
USART1_TX:通常使用DMA1_Channel2(需查阅参考手册确认)。
USART1_RX:通常使用 DMA1_Channel3。
注意:STM32F030的DMA资源有限,需避免与其他外设(如SPI、I2C)冲突。
三、软件实现步骤(以HAL库为例)
1. 初始化UART+DMA
c
// 定义DMA缓冲区(大小根据需求调整)
#define BUF_SIZE 256
uint8_t rx_buf[BUF_SIZE];
uint8_t tx_buf[BUF_SIZE];

// UART初始化(CubeMX生成代码后补充DMA配置)
void UART_DMA_Init(void) {
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart1);

    // 配置DMA接收(循环模式)
    hdma_usart1_rx.Instance = DMA1_Channel3;
    hdma_usart1_rx.Init.Request = DMA_REQUEST_2; // USART1_RX请求
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; // 循环模式,自动重载
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_usart1_rx);
    __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);

    // 启动DMA接收
    HAL_UART_Receive_DMA(&huart1, rx_buf, BUF_SIZE);
}
2. 数据发送(非阻塞)
c
// 通过DMA发送数据(不阻塞CPU)
void UART_DMA_Send(uint8_t *data, uint16_t size) {
    HAL_UART_Transmit_DMA(&huart1, data, size);
}

// 检查发送是否完成(可选)
uint8_t UART_DMA_TxComplete(void) {
    return __HAL_DMA_GET_FLAG(&hdma_usart1_tx, DMA_FLAG_TCIF1_3); // 根据实际通道调整标志位
}
3. 数据处理(中断回调)
c
// DMA接收完成一半/全部时触发(需在CubeMX中启用DMA中断)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        // 处理rx_buf中的数据(例如解析协议)
        Process_UART_Data(rx_buf, BUF_SIZE);
        
        // 若需重新启动接收(非循环模式时)
        // HAL_UART_Receive_DMA(&huart1, rx_buf, BUF_SIZE);
    }
}

// 错误处理回调
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        // 重启DMA接收(如发生溢出错误)
        HAL_UART_DeInit(&huart1);
        UART_DMA_Init();
    }
}
四、优化技巧
双缓冲机制
使用两个接收缓冲区(如rx_buf1和rx_buf2),DMA在后台填充一个缓冲区时,CPU处理另一个缓冲区的数据,避免数据覆盖。
示例:
c
volatile uint8_t active_buf = 0;
uint8_t rx_buf1[BUF_SIZE], rx_buf2[BUF_SIZE];

// 启动DMA交替接收
HAL_UART_Receive_DMA(&huart1, rx_buf1, BUF_SIZE);
HAL_UART_Receive_DMA(&huart1, rx_buf2, BUF_SIZE); // 需通过硬件或软件切换通道
降低中断频率
在循环模式下,DMA持续填充缓冲区,仅在缓冲区半满/全满时触发中断,减少CPU中断负载。
DMA优先级调整
对实时性要求高的通信(如无线模块),将UART DMA优先级设为DMA_PRIORITY_VERY_HIGH。
五、常见问题解决
1. 数据丢失或乱码
原因:DMA缓冲区过小或CPU处理速度跟不上数据速率。
解决:增大缓冲区,或改用双缓冲;检查波特率、采样率设置是否匹配。
2. DMA传输卡死
原因:未正确处理传输完成标志,或缓冲区溢出。
解决:在回调函数中清除标志位,或启用DMA错误中断(HAL_DMA_RegisterCallback)。
3. 与其它外设冲突
原因:DMA通道被占用(如SPI和UART共用DMA1_Channel2)。
解决:重新分配通道,或通过HAL_DMAEx_MultiBufferStart实现多外设共享。
六、资源占用对比
方式        CPU占用        实时性        适合场景
轮询        高        低        低速、低数据量
中断        中        中        中等速率(如9600bps)
DMA        极低        高        高速、大数据量(如115200bps+)

七、参考代码库
ST官方示例:STM32CubeF0固件包中的DMA_USART示例。
开源项目:如LWIP_STM32(网络通信中大量使用DMA+UART)。
通过合理配置DMA,STM32F030F4P6可轻松实现高效串口通信,释放CPU资源用于更复杂的任务。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

381

主题

2687

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部