STM32CubeMX串口通信实战:从发送到接收再到重定向(附完整代码)
STM32CubeMX串口通信实战从发送到接收再到重定向附完整代码引言在嵌入式开发领域串口通信就像开发者的瑞士军刀——简单、可靠且无处不在。无论是调试信息输出、设备间数据交换还是与上位机通信UART通用异步收发传输器都是最常用的接口之一。对于STM32开发者来说STM32CubeMX工具极大地简化了外设配置过程让开发者能够更专注于业务逻辑的实现。本文将带你从零开始通过STM32CubeMX完成串口通信的全流程实战。不同于简单的功能演示我们会深入探讨三个核心场景数据发送、中断接收以及printf重定向。每个环节都配有经过验证的代码片段你可以直接将这些代码应用到自己的项目中。1. 环境准备与基础配置1.1 硬件选型与连接在开始之前确保你已准备好以下硬件任意型号STM32开发板如STM32F103C8T6最小系统板USB转TTL模块如CH340G杜邦线若干连接注意事项开发板的UART TX引脚连接USB转TTL的RX开发板的UART RX引脚连接USB转TTL的TX确保共地GND连接1.2 STM32CubeMX工程创建打开STM32CubeMX点击New Project选择你的MCU型号或开发板型号在Pinout Configuration界面找到USART1或其他可用串口将模式设置为Asynchronous配置基本参数波特率1152008位数据位无校验1位停止位// 生成的串口初始化代码片段HAL库 huart1.Instance USART1; huart1.Init.BaudRate 115200; huart1.Init.WordLength UART_WORDLENGTH_8B; huart1.Init.StopBits UART_STOPBITS_1; huart1.Init.Parity UART_PARITY_NONE;提示建议勾选Generate IRQ handler选项为后续中断接收做准备。2. 串口数据发送实战2.1 基础发送函数解析HAL库提供了简洁的发送接口其函数原型如下HAL_StatusTypeDef HAL_UART_Transmit( UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout );参数说明参数类型说明huartUART_HandleTypeDef*串口句柄指针pDatauint8_t*发送数据缓冲区Sizeuint16_t发送数据长度Timeoutuint32_t超时时间毫秒2.2 定时发送实现在main.c文件中添加发送缓冲区并实现周期发送/* Private variables ---------------------------------------------------------*/ uint8_t tx_msg[] STM32 UART Demo\r\n; /* USER CODE BEGIN 2 */ // 首次发送 HAL_UART_Transmit(huart1, tx_msg, sizeof(tx_msg)-1, HAL_MAX_DELAY); /* USER CODE END 2 */ /* Infinite loop */ while (1) { HAL_UART_Transmit(huart1, tx_msg, sizeof(tx_msg)-1, HAL_MAX_DELAY); HAL_Delay(1000); // 1秒间隔 }注意字符串末尾的\r\n是换行符确保在串口终端中正确显示。3. 中断接收与回显3.1 中断接收配置相比轮询方式中断接收能显著降低CPU占用。配置步骤如下在CubeMX中使能USART全局中断在main函数初始化阶段启动中断接收/* Private variables ---------------------------------------------------------*/ uint8_t rx_buffer; // 单字节接收缓冲区 /* USER CODE BEGIN 2 */ // 启动中断接收 HAL_UART_Receive_IT(huart1, rx_buffer, 1); /* USER CODE END 2 */3.2 中断回调实现在stm32f1xx_it.c文件中添加中断回调函数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { // 回显接收到的字符 HAL_UART_Transmit(huart1, rx_buffer, 1, 100); // 重新使能接收中断 HAL_UART_Receive_IT(huart1, rx_buffer, 1); } }关键点说明每次中断接收完成后需要重新使能接收回调函数中避免耗时操作可通过判断huart-Instance区分不同串口4. printf重定向技巧4.1 重定向原理通过重写fputc和fgetc函数可以让标准库的printf和scanf直接操作串口。在usart.c文件中添加#include stdio.h #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, HAL_MAX_DELAY); return ch; }4.2 应用示例重定向后可以像在PC上一样使用printfint value 42; printf(System started!\r\n); printf(Current value: %d\r\n, value);常见问题排查确保包含了stdio.h头文件如果使用MicroLIB需要在IDE中勾选Use MicroLIB选项浮点数打印需要额外配置5. 高级应用与优化5.1 环形缓冲区实现对于高频数据接收建议使用环形缓冲区#define BUF_SIZE 256 typedef struct { uint8_t buffer[BUF_SIZE]; uint16_t head; uint16_t tail; } ring_buffer_t; ring_buffer_t uart_rx_buf {0}; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { uart_rx_buf.buffer[uart_rx_buf.head] rx_buffer; uart_rx_buf.head (uart_rx_buf.head 1) % BUF_SIZE; HAL_UART_Receive_IT(huart1, rx_buffer, 1); } }5.2 DMA传输优化对于大数据量传输DMA模式能极大提升效率在CubeMX中配置USART DMA选项使用DMA发送函数HAL_UART_Transmit_DMA(huart1, tx_data, data_len);DMA优势零CPU开销的数据传输支持大块数据传输可与中断配合实现高效通信6. 实战调试技巧6.1 常见问题排查表现象可能原因解决方案无数据输出线序接反检查TX/RX交叉连接乱码波特率不匹配核对双方波特率设置数据丢失缓冲区溢出增大缓冲区或提高处理速度无法进入中断中断未使能检查NVIC配置6.2 性能优化建议对于实时性要求高的场景优先使用中断DMA组合避免在中断服务函数中进行复杂处理关键代码段考虑关闭中断保护使用硬件流控制RTS/CTS防止数据丢失7. 完整代码示例以下是一个整合了所有功能的示例代码框架/* main.c */ #include main.h #include string.h #include stdio.h UART_HandleTypeDef huart1; uint8_t rx_data; ring_buffer_t uart_rx_buf; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); HAL_UART_Receive_IT(huart1, rx_data, 1); printf(System Ready\r\n); while (1) { process_uart_data(); HAL_Delay(10); } } void process_uart_data(void) { if(uart_rx_buf.head ! uart_rx_buf.tail) { uint8_t data uart_rx_buf.buffer[uart_rx_buf.tail]; uart_rx_buf.tail (uart_rx_buf.tail 1) % BUF_SIZE; // 处理接收到的数据 printf(Received: %c\r\n, data); } }在实际项目中我发现合理组织代码结构比追求单一功能的极致性能更重要。比如将串口处理封装成独立模块通过清晰定义的接口与主程序交互这样既方便调试也利于后期维护。