STM32F407 串口收发数据程序

一、硬件配置

串口 引脚 功能
USART1 PA9 (TX) 发送数据
USART1 PA10 (RX) 接收数据
USART2 PA2 (TX) 发送数据
USART2 PA3 (RX) 接收数据
USART3 PB10 (TX) 发送数据
USART3 PB11 (RX) 接收数据

二、完整代码实现

2.1 串口初始化代码

/**
 * @file usart.c
 * @brief STM32F407 串口驱动程序
 */

#include "usart.h"
#include "string.h"
#include "stdio.h"

// 串口接收缓冲区
#define USART1_RX_BUFFER_SIZE 256
#define USART2_RX_BUFFER_SIZE 256
#define USART3_RX_BUFFER_SIZE 256

static uint8_t usart1_rx_buffer[USART1_RX_BUFFER_SIZE];
static uint8_t usart2_rx_buffer[USART2_RX_BUFFER_SIZE];
static uint8_t usart3_rx_buffer[USART3_RX_BUFFER_SIZE];

static uint16_t usart1_rx_index = 0;
static uint16_t usart2_rx_index = 0;
static uint16_t usart3_rx_index = 0;

// 串口句柄
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
UART_HandleTypeDef huart3;

/**
 * @brief USART1 初始化
 * @param baudrate 波特率
 */
void USART1_Init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 1. 使能时钟
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // 2. 配置GPIO引脚
    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 3. 配置串口参数
    huart1.Instance = USART1;
    huart1.Init.BaudRate = baudrate;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    
    if (HAL_UART_Init(&huart1) != HAL_OK) {
        Error_Handler();
    }
    
    // 4. 开启接收中断
    HAL_UART_Receive_IT(&huart1, &usart1_rx_buffer[usart1_rx_index], 1);
}

/**
 * @brief USART2 初始化
 * @param baudrate 波特率
 */
void USART2_Init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    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_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    huart2.Instance = USART2;
    huart2.Init.BaudRate = baudrate;
    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();
    }
    
    HAL_UART_Receive_IT(&huart2, &usart2_rx_buffer[usart2_rx_index], 1);
}

/**
 * @brief USART3 初始化
 * @param baudrate 波特率
 */
void USART3_Init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    __HAL_RCC_USART3_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    huart3.Instance = USART3;
    huart3.Init.BaudRate = baudrate;
    huart3.Init.WordLength = UART_WORDLENGTH_8B;
    huart3.Init.StopBits = UART_STOPBITS_1;
    huart3.Init.Parity = UART_PARITY_NONE;
    huart3.Init.Mode = UART_MODE_TX_RX;
    huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart3.Init.OverSampling = UART_OVERSAMPLING_16;
    
    if (HAL_UART_Init(&huart3) != HAL_OK) {
        Error_Handler();
    }
    
    HAL_UART_Receive_IT(&huart3, &usart3_rx_buffer[usart3_rx_index], 1);
}

2.2 串口发送函数

/**
 * @brief 串口发送单个字节
 * @param huart 串口句柄
 * @param ch 要发送的字节
 */
void USART_SendByte(UART_HandleTypeDef *huart, uint8_t ch) {
    HAL_UART_Transmit(huart, &ch, 1, HAL_MAX_DELAY);
}

/**
 * @brief 串口发送字符串
 * @param huart 串口句柄
 * @param str 要发送的字符串
 */
void USART_SendString(UART_HandleTypeDef *huart, char *str) {
    HAL_UART_Transmit(huart, (uint8_t *)str, strlen(str), HAL_MAX_DELAY);
}

/**
 * @brief 串口发送数组
 * @param huart 串口句柄
 * @param data 数据数组
 * @param len 数组长度
 */
void USART_SendArray(UART_HandleTypeDef *huart, uint8_t *data, uint16_t len) {
    HAL_UART_Transmit(huart, data, len, HAL_MAX_DELAY);
}

/**
 * @brief 串口发送整数
 * @param huart 串口句柄
 * @param num 要发送的整数
 */
void USART_SendInt(UART_HandleTypeDef *huart, int32_t num) {
    char str[20];
    sprintf(str, "%ld", num);
    USART_SendString(huart, str);
}

/**
 * @brief 串口发送浮点数
 * @param huart 串口句柄
 * @param f 要发送的浮点数
 * @param decimals 小数位数
 */
void USART_SendFloat(UART_HandleTypeDef *huart, float f, uint8_t decimals) {
    char str[20];
    sprintf(str, "%.*f", decimals, f);
    USART_SendString(huart, str);
}

/**
 * @brief 格式化输出(类似printf)
 * @param huart 串口句柄
 * @param format 格式化字符串
 * @param ... 可变参数
 */
void USART_Printf(UART_HandleTypeDef *huart, const char *format, ...) {
    char buffer[256];
    va_list args;
    va_start(args, format);
    vsprintf(buffer, format, args);
    va_end(args);
    USART_SendString(huart, buffer);
}

2.3 串口接收中断处理

/**
 * @brief 串口接收完成回调函数
 * @param huart 串口句柄
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        // 处理USART1接收到的数据
        uint8_t received_byte = usart1_rx_buffer[usart1_rx_index];
        
        // 这里可以添加数据处理逻辑
        // 例如:将接收到的数据回显
        USART_SendByte(&huart1, received_byte);
        
        // 检查是否接收到完整的一帧数据(例如以换行符结束)
        if (received_byte == '\n' || usart1_rx_index >= USART1_RX_BUFFER_SIZE - 1) {
            // 处理完整的数据帧
            USART_SendString(&huart1, "\r\nReceived: ");
            USART_SendArray(&huart1, usart1_rx_buffer, usart1_rx_index);
            USART_SendString(&huart1, "\r\n");
            
            // 清空缓冲区
            memset(usart1_rx_buffer, 0, USART1_RX_BUFFER_SIZE);
            usart1_rx_index = 0;
        } else {
            usart1_rx_index++;
        }
        
        // 重新开启接收中断
        HAL_UART_Receive_IT(&huart1, &usart1_rx_buffer[usart1_rx_index], 1);
    }
    else if (huart->Instance == USART2) {
        // 处理USART2接收到的数据
        uint8_t received_byte = usart2_rx_buffer[usart2_rx_index];
        
        // 回显数据
        USART_SendByte(&huart2, received_byte);
        
        if (received_byte == '\n' || usart2_rx_index >= USART2_RX_BUFFER_SIZE - 1) {
            USART_SendString(&huart2, "\r\nUSART2 Received!\r\n");
            memset(usart2_rx_buffer, 0, USART2_RX_BUFFER_SIZE);
            usart2_rx_index = 0;
        } else {
            usart2_rx_index++;
        }
        
        HAL_UART_Receive_IT(&huart2, &usart2_rx_buffer[usart2_rx_index], 1);
    }
    else if (huart->Instance == USART3) {
        // 处理USART3接收到的数据
        uint8_t received_byte = usart3_rx_buffer[usart3_rx_index];
        
        USART_SendByte(&huart3, received_byte);
        
        if (received_byte == '\n' || usart3_rx_index >= USART3_RX_BUFFER_SIZE - 1) {
            USART_SendString(&huart3, "\r\nUSART3 Received!\r\n");
            memset(usart3_rx_buffer, 0, USART3_RX_BUFFER_SIZE);
            usart3_rx_index = 0;
        } else {
            usart3_rx_index++;
        }
        
        HAL_UART_Receive_IT(&huart3, &usart3_rx_buffer[usart3_rx_index], 1);
    }
}

2.4 串口DMA接收(高性能版本)

/**
 * @file usart_dma.c
 * @brief 串口DMA接收实现
 */

#define USART1_DMA_RX_BUFFER_SIZE 512
#define USART1_DMA_TX_BUFFER_SIZE 512

static uint8_t usart1_dma_rx_buffer[USART1_DMA_RX_BUFFER_SIZE];
static uint8_t usart1_dma_tx_buffer[USART1_DMA_TX_BUFFER_SIZE];

DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/**
 * @brief USART1 DMA初始化
 */
void USART1_DMA_Init(void) {
    __HAL_RCC_DMA2_CLK_ENABLE();
    
    // 配置DMA接收
    hdma_usart1_rx.Instance = DMA2_Stream2;
    hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;  // 循环模式
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) {
        Error_Handler();
    }
    
    __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);
    
    // 配置DMA发送
    hdma_usart1_tx.Instance = DMA2_Stream7;
    hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;
    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;
    hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK) {
        Error_Handler();
    }
    
    __HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);
    
    // 启动DMA接收
    HAL_UART_Receive_DMA(&huart1, usart1_dma_rx_buffer, USART1_DMA_RX_BUFFER_SIZE);
}

/**
 * @brief DMA接收完成回调函数
 */
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        // 半传输完成,处理前半部分数据
        // 可以在这里处理数据
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        // 传输完成,处理后半部分数据
        // 可以在这里处理数据
    }
}

/**
 * @brief 使用DMA发送数据
 */
void USART1_DMA_Send(uint8_t *data, uint16_t len) {
    HAL_UART_Transmit_DMA(&huart1, data, len);
}

2.5 主程序示例

/**
 * @file main.c
 * @brief STM32F407 串口收发测试主程序
 */

#include "stm32f4xx_hal.h"
#include "usart.h"
#include "delay.h"

int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();
    
    // 延时初始化
    Delay_Init();
    
    // 初始化串口
    USART1_Init(115200);
    USART2_Init(9600);
    USART3_Init(57600);
    
    // 初始化DMA接收(可选)
    // USART1_DMA_Init();
    
    printf("STM32F407 USART Test Program\r\n");
    printf("System Clock: %lu Hz\r\n", HAL_RCC_GetSysClockFreq());
    printf("Starting communication test...\r\n");
    
    // 测试发送
    USART_SendString(&huart1, "Hello from USART1!\r\n");
    USART_SendString(&huart2, "Hello from USART2!\r\n");
    USART_SendString(&huart3, "Hello from USART3!\r\n");
    
    // 格式化输出测试
    USART_Printf(&huart1, "Integer: %d, Float: %.2f\r\n", 12345, 3.14159f);
    
    uint8_t counter = 0;
    
    while (1) {
        // 每秒发送一次数据
        USART_Printf(&huart1, "Counter: %d, Time: %lu ms\r\n", 
                     counter++, HAL_GetTick());
        
        // 发送数组测试
        uint8_t test_array[] = {0xAA, 0x55, 0x01, 0x02, 0x03};
        USART_SendArray(&huart2, test_array, sizeof(test_array));
        
        // 发送浮点数测试
        float temperature = 25.6f + (counter % 10) * 0.1f;
        USART_Printf(&huart3, "Temperature: %.1f C\r\n", temperature);
        
        HAL_Delay(1000);  // 延时1秒
    }
}

2.6 重定向printf到串口

/**
 * @file syscalls.c
 * @brief 重定向printf到串口
 */

#include "usart.h"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

// 重定向_write函数
int _write(int fd, char *ptr, int len) {
    if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
        // 发送到USART1
        USART_SendArray(&huart1, (uint8_t *)ptr, len);
        return len;
    }
    errno = EBADF;
    return -1;
}

// 重定向_read函数
int _read(int fd, char *ptr, int len) {
    if (fd == STDIN_FILENO) {
        // 从USART1读取(这里简化实现)
        // 实际应该使用接收缓冲区
        return 0;
    }
    errno = EBADF;
    return -1;
}

三、高级功能扩展

3.1 串口命令解析器

/**
 * @file cmd_parser.c
 * @brief 串口命令解析器
 */

typedef struct {
    char command[20];
    void (*handler)(void);
} Command_t;

// 命令处理函数
void Cmd_Help(void) {
    USART_Printf(&huart1, "Available commands:\r\n");
    USART_Printf(&huart1, "  help     - Show this help\r\n");
    USART_Printf(&huart1, "  led_on   - Turn on LED\r\n");
    USART_Printf(&huart1, "  led_off  - Turn off LED\r\n");
    USART_Printf(&huart1, "  reset    - Reset system\r\n");
}

void Cmd_LED_On(void) {
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
    USART_Printf(&huart1, "LED turned ON\r\n");
}

void Cmd_LED_Off(void) {
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
    USART_Printf(&huart1, "LED turned OFF\r\n");
}

void Cmd_Reset(void) {
    USART_Printf(&huart1, "System reset...\r\n");
    NVIC_SystemReset();
}

// 命令表
Command_t command_table[] = {
    {"help", Cmd_Help},
    {"led_on", Cmd_LED_On},
    {"led_off", Cmd_LED_Off},
    {"reset", Cmd_Reset},
};

#define COMMAND_COUNT (sizeof(command_table) / sizeof(Command_t))

/**
 * @brief 解析并执行命令
 */
void Parse_Command(char *cmd) {
    // 去除换行符
    char *newline = strchr(cmd, '\r');
    if (newline) *newline = '\0';
    newline = strchr(cmd, '\n');
    if (newline) *newline = '\0';
    
    // 查找匹配的命令
    for (int i = 0; i < COMMAND_COUNT; i++) {
        if (strcmp(cmd, command_table[i].command) == 0) {
            command_table[i].handler();
            return;
        }
    }
    
    USART_Printf(&huart1, "Unknown command: %s\r\n", cmd);
    USART_Printf(&huart1, "Type 'help' for available commands\r\n");
}

3.2 串口环形缓冲区(高性能)

/**
 * @file ring_buffer.c
 * @brief 串口环形缓冲区
 */

typedef struct {
    uint8_t buffer[256];
    uint16_t head;
    uint16_t tail;
    uint16_t count;
} RingBuffer_t;

static RingBuffer_t usart1_rx_ringbuf;

/**
 * @brief 初始化环形缓冲区
 */
void RingBuffer_Init(RingBuffer_t *rb) {
    rb->head = 0;
    rb->tail = 0;
    rb->count = 0;
}

/**
 * @brief 写入一个字节到环形缓冲区
 */
void RingBuffer_Write(RingBuffer_t *rb, uint8_t data) {
    if (rb->count < 256) {
        rb->buffer[rb->head] = data;
        rb->head = (rb->head + 1) % 256;
        rb->count++;
    }
}

/**
 * @brief 从环形缓冲区读取一个字节
 */
uint8_t RingBuffer_Read(RingBuffer_t *rb) {
    uint8_t data = 0;
    if (rb->count > 0) {
        data = rb->buffer[rb->tail];
        rb->tail = (rb->tail + 1) % 256;
        rb->count--;
    }
    return data;
}

/**
 * @brief 检查缓冲区是否为空
 */
uint8_t RingBuffer_IsEmpty(RingBuffer_t *rb) {
    return (rb->count == 0);
}

// 在串口中断中使用
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        uint8_t received_byte = usart1_rx_buffer[0];
        RingBuffer_Write(&usart1_rx_ringbuf, received_byte);
        HAL_UART_Receive_IT(&huart1, usart1_rx_buffer, 1);
    }
}

参考代码 STM32F407串口进行收发数据 www.youwenfan.com/contentcnv/60392.html

四、使用注意事项

4.1 常见问题解决

问题 原因 解决方案
串口无输出 波特率不匹配 检查双方波特率设置
接收数据乱码 时钟配置错误 检查系统时钟配置
接收中断不触发 未开启中断 确认HAL_UART_Receive_IT()被调用
数据丢失 接收速度过快 使用DMA接收或增大缓冲区

4.2 性能优化建议

  1. 使用DMA接收:对于高速数据,必须使用DMA
  2. 使用环形缓冲区:避免数据覆盖
  3. 避免在中断中处理复杂逻辑:只做数据接收
  4. 使用空闲中断:检测一帧数据结束
  5. 合理设置优先级:串口中断优先级要适当
posted @ 2026-05-18 17:47  w199899899  阅读(46)  评论(0)    收藏  举报