打印
[STM32G4]

探索STM32G431:基于UART和DMA实现高效串口通信

[复制链接]
204|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Clyde011|  楼主 | 2024-12-10 07:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在嵌入式系统中,串口通信是一种常见的外设通信方式。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 缓冲区的使用。

使用特权

评论回复
沙发
公羊子丹| | 2024-12-10 07:54 | 只看该作者
这个代码看起来好高级,有没有更简单的版本?

使用特权

评论回复
板凳
周半梅| | 2024-12-10 07:55 | 只看该作者
请问如果用别的 STM32 型号,这段代码改动大不大?

使用特权

评论回复
地板
帛灿灿| | 2024-12-10 07:55 | 只看该作者
我看你的中断是用 HAL 实现的,有没有办法直接用寄存器实现?

使用特权

评论回复
5
童雨竹| | 2024-12-10 07:55 | 只看该作者
这段代码可以扩展到双 UART 吗?我有个项目需要用到两个串口。

使用特权

评论回复
6
万图| | 2024-12-10 07:56 | 只看该作者
感觉 STM32G4 性能不错,这个系列的其他型号值得尝试吗?

使用特权

评论回复
7
Wordsworth| | 2024-12-10 07:56 | 只看该作者
代码能跑在 Keil 上吗?我习惯用 Keil。

使用特权

评论回复
8
Pulitzer| | 2024-12-10 07:56 | 只看该作者
想问下 DMA 的优先级设置会不会影响系统其他部分的性能?

使用特权

评论回复
9
Bblythe| | 2024-12-10 07:56 | 只看该作者
感觉 HAL 库有点冗余,能不能用 LL 库改写一下?

使用特权

评论回复
10
Uriah| | 2024-12-10 07:57 | 只看该作者
这段代码支持带校验的串口通信吗,比如加个 CRC 校验?

使用特权

评论回复
11
AloneKaven| | 2024-12-10 11:50 | 只看该作者
周半梅 发表于 2024-12-10 07:55
请问如果用别的 STM32 型号,这段代码改动大不大?

hal库的改动应该不大

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

3418

帖子

0

粉丝