stm32单片机串口dma中断收发
时间: 2025-07-15 22:37:41 浏览: 6
### STM32 串口 DMA 和中断收发实现教程
#### 配置概述
STM32 的串口可以通过 DMA 或者中断的方式进行高效的数据收发操作。DMA 方式允许硬件自动处理数据传输,减少 CPU 干预;而中断方式则更适合实时性和灵活性较高的场景。
---
#### 使用 DMA 进行串口收发的实现方法
##### 初始化配置
为了使用 DMA 控制串口通信,需要先完成以下初始化工作:
1. **使能时钟**
- 启用串口外设和对应的 DMA 外设时钟。
2. **GPIO 配置**
- 将 GPIO 引脚配置为复用功能(AF),并连接到指定的 UART/USART。
3. **UART/USART 配置**
- 设置波特率、数据位数、停止位以及校验模式等参数。
4. **DMA 配置**
- 根据需求选择合适的 DMA 流和通道,并设置方向(`MemoryToPeripheral` 或 `PeripheralToMemory`)、优先级以及其他属性。
5. **启用 DMA 请求**
- 对于发送操作,需开启 `USART_DMAReq_Tx`;对于接收操作,需开启 `USART_DMAReq_Rx`。
以下是基于引用中的描述所整理的一个典型实现代码示例[^1]:
```c
#include "stm32f1xx_hal.h"
// 定义全局变量用于存储接收到的数据缓冲区
#define RX_BUFFER_SIZE 64
uint8_t rx_buffer[RX_BUFFER_SIZE];
volatile uint8_t dma_rx_complete_flag = 0;
void UART_DMA_Init(void) {
// 1. 使能时钟
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE(); // 假设 USART1 映射到 PA9(TX), PA10(RX)
__HAL_RCC_DMA1_CLK_ENABLE();
// 2. 配置 GPIO
GPIO_InitTypeDef gpio_init;
gpio_init.Pin = GPIO_PIN_9 | GPIO_PIN_10; // TX/RX Pin
gpio_init.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
gpio_init.Pull = GPIO_PULLUP; // 上拉电阻
HAL_GPIO_Init(GPIOA, &gpio_init);
// 3. 配置 USART
USART_InitTypeDef usart_init;
usart_init.BaudRate = 115200;
usart_init.WordLength = UART_WORDLENGTH_8B;
usart_init.StopBits = UART_STOPBITS_1;
usart_init.Parity = UART_PARITY_NONE;
usart_init.HwFlowCtl = UART_HWCONTROL_NONE;
usart_init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart1);
// 4. 配置 DMA
DMA_HandleTypeDef hdma_usart1_tx;
hdma_usart1_tx.Instance = DMA1_Stream6; // 发送使用的 Stream
hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4; // Channel 4 对应 USART1
hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_tx.Init.Mode = DMA_NORMAL;
hdma_usart1_tx.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_usart1_tx);
// 接收部分同样需要类似的配置...
// 5. 绑定 DMA 到 USART
__HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);
// 6. 开启 DMA 请求
__HAL_UART_ENABLE_DMA(&huart1, UART_DMAREQ_TX); // 发送
}
// 数据发送函数
void UART_Send_DMA(const uint8_t* data, uint16_t size) {
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)data, size);
}
```
---
#### 使用中断方式进行串口收发的实现方法
如果希望采用中断驱动的方式来管理串口收发过程,则可以按照如下步骤执行:
1. **使能中断源**
- 在 NVIC 中注册串口中断向量号,并调整其优先级。
2. **编写中断服务程序**
- 当发生特定事件(如接收完成或错误状态)时触发回调逻辑。
下面是一个简单的中断驱动型接收示例[^2]:
```c
extern uint8_t received_data;
void USART1_IRQHandler(void) {
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) { // 检测是否有新数据到达
received_data = huart1.Instance->DR; // 获取接收到的数据
__HAL_UART_CLEAR_IT(&huart1, UART_IT_RXNE); // 清除中断标志
}
}
void UART_EnableInterrupts(void) {
HAL_NVIC_SetPriority(USART1_IRQn, 1, 0); // 设置优先级
HAL_NVIC_EnableIRQ(USART1_IRQn); // 使能中断
}
```
---
#### 结合 DMA 和中断的优势分析
- 如果目标是最小化 CPU 负载并且能够一次性批量传输大量数据,推荐使用纯 DMA 方法。
- 若应用程序涉及复杂的协议解析或者动态长度的消息帧检测,则可考虑结合两者——利用 DMA 执行实际的数据搬运任务,同时依靠中断来通知主机何时启动下一阶段的操作。
---
阅读全文
相关推荐


















