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

[复制链接]
 楼主| Clyde011 发表于 2024-12-10 07:54 | 显示全部楼层 |阅读模式
在嵌入式系统中,串口通信是一种常见的外设通信方式。STM32G431 作为一款性能强大的 MCU,提供了丰富的外设和 DMA(直接存储器访问)功能,使得串口通信更加高效。本文将通过实例,展示如何利用 STM32G431 的 UART 和 DMA,实现高效的数据收发。
一、项目需求分析在本项目中,我们需要实现以下功能:
  • 使用 UART 接口进行数据的发送和接收;
  • 利用 DMA 提高数据传输的效率,避免过多的 CPU 干预;
  • 通过中断处理数据接收完成的事件。
二、硬件与软件环境
  • 硬件:STM32G431 Nucleo 开发板
  • 软件:STM32CubeIDE
  • 串口工具:任意串口调试工具(如 Tera Term 或 CoolTerm)
三、代码实现下面的代码实现了 UART 与 DMA 的初始化,以及串口数据的发送和接收逻辑。
  1. #include "main.h"
  2. #include "stm32g4xx_hal.h"

  3. // 定义 DMA 缓冲区大小
  4. #define RX_BUFFER_SIZE 64
  5. #define TX_BUFFER_SIZE 64

  6. UART_HandleTypeDef huart2;  // UART2 句柄
  7. DMA_HandleTypeDef hdma_usart2_rx;
  8. DMA_HandleTypeDef hdma_usart2_tx;

  9. // DMA 缓冲区
  10. uint8_t rxBuffer[RX_BUFFER_SIZE];
  11. uint8_t txBuffer[TX_BUFFER_SIZE] = "Hello from STM32G431!\r\n";

  12. // 接收完成标志
  13. volatile uint8_t rxComplete = 0;

  14. // 函数声明
  15. void SystemClock_Config(void);
  16. void UART2_Init(void);
  17. void DMA_Init(void);

  18. // 主函数
  19. int main(void) {
  20.     HAL_Init();
  21.     SystemClock_Config();
  22.     UART2_Init();
  23.     DMA_Init();

  24.     // 启动 UART 的 DMA 接收
  25.     HAL_UART_Receive_DMA(&huart2, rxBuffer, RX_BUFFER_SIZE);

  26.     while (1) {
  27.         if (rxComplete) {
  28.             // 处理接收到的数据
  29.             HAL_UART_Transmit_DMA(&huart2, rxBuffer, RX_BUFFER_SIZE);
  30.             rxComplete = 0;
  31.         }
  32.     }
  33. }

  34. // UART2 初始化
  35. void UART2_Init(void) {
  36.     __HAL_RCC_USART2_CLK_ENABLE();
  37.     __HAL_RCC_GPIOA_CLK_ENABLE();

  38.     GPIO_InitTypeDef GPIO_InitStruct = {0};
  39.     GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
  40.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  41.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  42.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  43.     GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
  44.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  45.     huart2.Instance = USART2;
  46.     huart2.Init.BaudRate = 115200;
  47.     huart2.Init.WordLength = UART_WORDLENGTH_8B;
  48.     huart2.Init.StopBits = UART_STOPBITS_1;
  49.     huart2.Init.Parity = UART_PARITY_NONE;
  50.     huart2.Init.Mode = UART_MODE_TX_RX;
  51.     huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  52.     huart2.Init.OverSampling = UART_OVERSAMPLING_16;

  53.     if (HAL_UART_Init(&huart2) != HAL_OK) {
  54.         Error_Handler();
  55.     }
  56. }

  57. // DMA 初始化
  58. void DMA_Init(void) {
  59.     __HAL_RCC_DMA1_CLK_ENABLE();

  60.     // 配置 USART2 RX DMA
  61.     hdma_usart2_rx.Instance = DMA1_Channel6;
  62.     hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  63.     hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
  64.     hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
  65.     hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  66.     hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  67.     hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
  68.     hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;

  69.     if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) {
  70.         Error_Handler();
  71.     }
  72.     __HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx);

  73.     // 配置 USART2 TX DMA
  74.     hdma_usart2_tx.Instance = DMA1_Channel7;
  75.     hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  76.     hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  77.     hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
  78.     hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  79.     hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  80.     hdma_usart2_tx.Init.Mode = DMA_NORMAL;
  81.     hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW;

  82.     if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) {
  83.         Error_Handler();
  84.     }
  85.     __HAL_LINKDMA(&huart2, hdmatx, hdma_usart2_tx);
  86. }

  87. // 接收完成回调函数
  88. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  89.     if (huart->Instance == USART2) {
  90.         rxComplete = 1;
  91.     }
  92. }

  93. // 系统时钟配置
  94. void SystemClock_Config(void) {
  95.     // 配置代码略,为简化展示
  96. }

  97. // 错误处理函数
  98. void Error_Handler(void) {
  99.     while (1) {
  100.         // 错误处理逻辑
  101.     }
  102. }
四、关键技术点解析
  • 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 实现的,有没有办法直接用寄存器实现?
童雨竹 发表于 2024-12-10 07:55 | 显示全部楼层
这段代码可以扩展到双 UART 吗?我有个项目需要用到两个串口。
万图 发表于 2024-12-10 07:56 | 显示全部楼层
感觉 STM32G4 性能不错,这个系列的其他型号值得尝试吗?
Wordsworth 发表于 2024-12-10 07:56 | 显示全部楼层
代码能跑在 Keil 上吗?我习惯用 Keil。
Pulitzer 发表于 2024-12-10 07:56 | 显示全部楼层
想问下 DMA 的优先级设置会不会影响系统其他部分的性能?
Bblythe 发表于 2024-12-10 07:56 | 显示全部楼层
感觉 HAL 库有点冗余,能不能用 LL 库改写一下?
Uriah 发表于 2024-12-10 07:57 | 显示全部楼层
这段代码支持带校验的串口通信吗,比如加个 CRC 校验?
AloneKaven 发表于 2024-12-10 11:50 | 显示全部楼层
周半梅 发表于 2024-12-10 07:55
请问如果用别的 STM32 型号,这段代码改动大不大?

hal库的改动应该不大
您需要登录后才可以回帖 登录 | 注册

本版积分规则

139

主题

5538

帖子

0

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