环境配置

项目使用STM32CubeMX初始化框架

IDE: JetBrains Clion 2025.1.1

编译环境使用arm-none-eabi-gcc

使用嘉立创天空星STM32F407VGT6开发板进行测试

提要

使用DMA+RingBuffer形式实现USART写入,减少主控占用,提高串口数据处理效率。

实现

usart.c文件内添加

/* USER CODE BEGIN 0 */

#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#define UART_TX_BUFFER_SIZE 1024

static uint8_t uart_tx_buffer[UART_TX_BUFFER_SIZE];
static volatile uint16_t uart_tx_head = 0;
static volatile uint16_t uart_tx_tail = 0;
static volatile uint8_t uart_tx_dma_busy = 0;

static void uart_buffer_write(const uint8_t *data, uint16_t len)
{
  for (uint16_t i = 0; i < len; i++) {
    uint16_t next = (uart_tx_head + 1) % UART_TX_BUFFER_SIZE;
    if (next == uart_tx_tail) {
      // 缓冲区满,丢弃
      break;
    }
    uart_tx_buffer[uart_tx_head] = data[i];
    uart_tx_head = next;
  }

  if (!uart_tx_dma_busy) {
    uint16_t count = (uart_tx_head >= uart_tx_tail)
        ? uart_tx_head - uart_tx_tail
        : UART_TX_BUFFER_SIZE - uart_tx_tail;

    if (count > 0) {
      uart_tx_dma_busy = 1;
      HAL_UART_Transmit_DMA(&huart1, &uart_tx_buffer[uart_tx_tail], count);
    }
  }
}

void uart_tx_dma_callback(UART_HandleTypeDef *huart)
{
  if (huart->Instance != USART1) return;

  uint16_t count = (uart_tx_head >= uart_tx_tail)
      ? uart_tx_head - uart_tx_tail
      : UART_TX_BUFFER_SIZE - uart_tx_tail;

  uart_tx_tail = (uart_tx_tail + count) % UART_TX_BUFFER_SIZE;

  if (uart_tx_tail != uart_tx_head) {
    count = (uart_tx_head > uart_tx_tail)
        ? uart_tx_head - uart_tx_tail
        : UART_TX_BUFFER_SIZE - uart_tx_tail;

    HAL_UART_Transmit_DMA(&huart1, &uart_tx_buffer[uart_tx_tail], count);
  } else {
    uart_tx_dma_busy = 0;
  }
}

/* USER CODE END 0 */

/* USER CODE BEGIN 1 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  extern void uart_tx_dma_callback(UART_HandleTypeDef *huart); // 声明 usart.c 中写的回调函数
  uart_tx_dma_callback(huart); // 调用定义的环形缓冲区管理函数
}

void uart_printf(const char *fmt, ...)
{
  char tmp[128];  // 临时格式化缓冲区
  va_list args;
  va_start(args, fmt);
  int len = vsnprintf(tmp, sizeof(tmp), fmt, args);
  va_end(args);
  if (len > 0)
  {
    uart_buffer_write((uint8_t *)tmp, len);  // 一次性写入全部格式化字符串
  }
}
/* USER CODE END 1 */

usart.h文件内添加

/* USER CODE BEGIN Prototypes */
void uart_tx_dma_callback(UART_HandleTypeDef *huart);
extern void uart_printf(const char *fmt, ...);
/* USER CODE END Prototypes */

CMakeList.txt文件内添加

target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
        -Wl,-u,_printf_float
        -Wl,-u,_scanf_float //启用串口浮点支持
)