在嵌入式系统中,串口通信是一种常见的外设通信方式。STM32G431 作为一款性能强大的 MCU,提供了丰富的外设和 DMA(直接存储器访问)功能,使得串口通信更加高效。本文将通过实例,展示如何利用 STM32G431 的 UART 和 DMA,实现高效的数据收发。
一、项目需求分析在本项目中,我们需要实现以下功能:
- 使用 UART 接口进行数据的发送和接收;
- 利用 DMA 提高数据传输的效率,避免过多的 CPU 干预;
- 通过中断处理数据接收完成的事件。
二、硬件与软件环境- 硬件:STM32G431 Nucleo 开发板
- 软件:STM32CubeIDE
- 串口工具:任意串口调试工具(如 Tera Term 或 CoolTerm)
三、代码实现下面的代码实现了 UART 与 DMA 的初始化,以及串口数据的发送和接收逻辑。
#include "main.h"
#include "stm32g4xx_hal.h"
// 定义 DMA 缓冲区大小
#define RX_BUFFER_SIZE 64
#define TX_BUFFER_SIZE 64
UART_HandleTypeDef huart2; // UART2 句柄
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;
// DMA 缓冲区
uint8_t rxBuffer[RX_BUFFER_SIZE];
uint8_t txBuffer[TX_BUFFER_SIZE] = "Hello from STM32G431!\r\n";
// 接收完成标志
volatile uint8_t rxComplete = 0;
// 函数声明
void SystemClock_Config(void);
void UART2_Init(void);
void DMA_Init(void);
// 主函数
int main(void) {
HAL_Init();
SystemClock_Config();
UART2_Init();
DMA_Init();
// 启动 UART 的 DMA 接收
HAL_UART_Receive_DMA(&huart2, rxBuffer, RX_BUFFER_SIZE);
while (1) {
if (rxComplete) {
// 处理接收到的数据
HAL_UART_Transmit_DMA(&huart2, rxBuffer, RX_BUFFER_SIZE);
rxComplete = 0;
}
}
}
// UART2 初始化
void UART2_Init(void) {
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK) {
Error_Handler();
}
}
// DMA 初始化
void DMA_Init(void) {
__HAL_RCC_DMA1_CLK_ENABLE();
// 配置 USART2 RX DMA
hdma_usart2_rx.Instance = DMA1_Channel6;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) {
Error_Handler();
}
__HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx);
// 配置 USART2 TX DMA
hdma_usart2_tx.Instance = DMA1_Channel7;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) {
Error_Handler();
}
__HAL_LINKDMA(&huart2, hdmatx, hdma_usart2_tx);
}
// 接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) {
rxComplete = 1;
}
}
// 系统时钟配置
void SystemClock_Config(void) {
// 配置代码略,为简化展示
}
// 错误处理函数
void Error_Handler(void) {
while (1) {
// 错误处理逻辑
}
}
四、关键技术点解析- DMA 的应用
- 使用 DMA 减轻了 CPU 的数据搬运任务,使 MCU 能够执行其他操作。
- RX 使用循环模式(DMA_CIRCULAR),实现数据连续接收。
- 中断机制
- 通过 HAL 库的 HAL_UART_RxCpltCallback,我们可以方便地处理接收完成事件。
- 性能提升
- UART 与 DMA 的结合大幅度提升了数据传输效率,适用于高速数据通信场景。
五、测试与优化将开发板连接到 PC 并打开串口工具,可以看到每次输入的数据都会通过 DMA 回传,实现数据回环的功能。如果需要进一步优化,可以结合 FreeRTOS 管理任务或优化 DMA 缓冲区的使用。
|