第30讲:实战——USART串口收发原型快速搭建+调试

发布时间:2026/7/23 7:38:57
第30讲:实战——USART串口收发原型快速搭建+调试 CSDN专栏嵌入式程序开发实战嵌入式双范式AI编程嵌入式开发必掌握嵌入式求职面试技术资料第30讲实战——USART串口收发原型快速搭建调试一、USART基础USART通用同步/异步收发器是嵌入式最常用的通信接口用于与PC、其他MCU、模块通信。1.1 USART配置要点基本参数波特率9600、19200、38400、57600、115200等数据位8位最常用停止位1位最常用校验位无最常用流控无最常用引脚配置USART1PA9(TX)、PA10(RX)USART2PA2(TX)、PA3(RX)USART3PB10(TX)、PB11(RX)1.2 通信模式轮询模式特点阻塞等待适用简单应用、调试中断模式特点非阻塞、事件驱动适用实际应用DMA模式特点高效、CPU占用少适用高速、大数据量二、串口发送实战2.1 基础发送需求串口发送Hello WorldPrompt生成STM32F103 USART发送代码 - USART1PA9(TX)、PA10(RX) - 波特率115200 - 功能发送Hello World - 使用HAL库AI生成#includestm32f1xx_hal.hUART_HandleTypeDef huart1;voidUSART1_Init(void){huart1.InstanceUSART1;huart1.Init.BaudRate115200;huart1.Init.WordLengthUART_WORDLENGTH_8B;huart1.Init.StopBitsUART_STOPBITS_1;huart1.Init.ParityUART_PARITY_NONE;huart1.Init.ModeUART_MODE_TX_RX;huart1.Init.HwFlowCtlUART_HWCONTROL_NONE;huart1.Init.OverSamplingUART_OVERSAMPLING_16;HAL_UART_Init(huart1);}intmain(void){HAL_Init();SystemClock_Config();USART1_Init();char*msgHello World\r\n;HAL_UART_Transmit(huart1,(uint8_t*)msg,strlen(msg),100);while(1);}2.2 printf重定向需求使用printf输出到串口代码// 重定向fputcintfputc(intch,FILE*f){HAL_UART_Transmit(huart1,(uint8_t*)ch,1,100);returnch;}// 使用intmain(void){HAL_Init();SystemClock_Config();USART1_Init();printf(System Start\r\n);printf(Version: 1.0\r\n);while(1){printf(Tick: %d\r\n,HAL_GetTick());HAL_Delay(1000);}}Keil配置Options → Target → Use MicroLIB勾选2.3 格式化输出需求输出各种格式的数据代码voidPrint_Data(void){uint8_tdata0x55;uint16_tvalue1234;floattemperature25.6f;// 十进制printf(Value: %d\r\n,value);// 十六进制printf(Hex: 0x%02X\r\n,data);// 浮点数printf(Temperature: %.2f\r\n,temperature);// 二进制printf(Binary: );for(inti7;i0;i--){printf(%d,(datai)1);}printf(\r\n);}三、串口接收实战3.1 轮询接收需求接收数据并回显代码intmain(void){HAL_Init();SystemClock_Config();USART1_Init();uint8_trxData;while(1){// 接收数据if(HAL_UART_Receive(huart1,rxData,1,100)HAL_OK){// 回显HAL_UART_Transmit(huart1,rxData,1,100);}}}3.2 中断接收需求使用中断接收数据Prompt生成STM32F103 USART中断接收代码 - USART1PA9(TX)、PA10(RX) - 波特率115200 - 功能中断接收并回显 - 使用HAL库AI生成uint8_trxData;voidUSART1_Init(void){huart1.InstanceUSART1;huart1.Init.BaudRate115200;huart1.Init.WordLengthUART_WORDLENGTH_8B;huart1.Init.StopBitsUART_STOPBITS_1;huart1.Init.ParityUART_PARITY_NONE;huart1.Init.ModeUART_MODE_TX_RX;huart1.Init.HwFlowCtlUART_HWCONTROL_NONE;huart1.Init.OverSamplingUART_OVERSAMPLING_16;HAL_UART_Init(huart1);// 启动接收中断HAL_UART_Receive_IT(huart1,rxData,1);}voidUSART1_IRQHandler(void){HAL_UART_IRQHandler(huart1);}voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart-InstanceUSART1){// 回显HAL_UART_Transmit(huart1,rxData,1,100);// 继续接收HAL_UART_Receive_IT(huart1,rxData,1);}}intmain(void){HAL_Init();SystemClock_Config();USART1_Init();while(1){// 主循环可以做其他事情}}3.3 环形缓冲区接收需求使用环形缓冲区接收数据避免数据丢失代码#defineBUFFER_SIZE256typedefstruct{uint8_tbuffer[BUFFER_SIZE];uint16_thead;uint16_ttail;}RingBuffer_t;RingBuffer_t rxBuffer{0};voidRingBuffer_Write(uint8_tdata){uint16_tnextHead(rxBuffer.head1)%BUFFER_SIZE;if(nextHead!rxBuffer.tail){// 缓冲区未满rxBuffer.buffer[rxBuffer.head]data;rxBuffer.headnextHead;}}uint8_tRingBuffer_Read(uint8_t*data){if(rxBuffer.headrxBuffer.tail){// 缓冲区空return0;}*datarxBuffer.buffer[rxBuffer.tail];rxBuffer.tail(rxBuffer.tail1)%BUFFER_SIZE;return1;}uint16_tRingBuffer_Available(void){return(rxBuffer.head-rxBuffer.tailBUFFER_SIZE)%BUFFER_SIZE;}// 中断回调voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart-InstanceUSART1){RingBuffer_Write(rxData);HAL_UART_Receive_IT(huart1,rxData,1);}}// 主循环处理voidProcess_UartData(void){uint8_tdata;while(RingBuffer_Read(data)){// 处理数据printf(Received: 0x%02X\r\n,data);}}四、串口调试技巧4.1 串口工具常用工具- 串口调试助手SSCOM、XCOM、串口精灵 - 终端工具PuTTY、Tera Term、SecureCRT - 分析工具逻辑分析仪、协议分析仪配置波特率115200 数据位8 停止位1 校验位无 流控无4.2 调试输出分级日志#defineLOG_LEVEL3// 0:无 1:错误 2:警告 3:信息 4:调试#defineLOG_ERROR(fmt,...)if(LOG_LEVEL1)printf([ERROR] fmt\r\n,##__VA_ARGS__)#defineLOG_WARN(fmt,...)if(LOG_LEVEL2)printf([WARN] fmt\r\n,##__VA_ARGS__)#defineLOG_INFO(fmt,...)if(LOG_LEVEL3)printf([INFO] fmt\r\n,##__VA_ARGS__)#defineLOG_DEBUG(fmt,...)if(LOG_LEVEL4)printf([DEBUG] fmt\r\n,##__VA_ARGS__)// 使用LOG_ERROR(UART initialization failed);LOG_WARN(Buffer nearly full: %d,usage);LOG_INFO(System started);LOG_DEBUG(Received byte: 0x%02X,data);4.3 数据分析十六进制输出voidPrint_Hex(uint8_t*data,uint16_tlen){printf(Hex: );for(uint16_ti0;ilen;i){printf(%02X ,data[i]);}printf(\r\n);}ASCII输出voidPrint_ASCII(uint8_t*data,uint16_tlen){printf(ASCII: );for(uint16_ti0;ilen;i){if(data[i]32data[i]126){// 可打印字符printf(%c,data[i]);}else{printf(.);}}printf(\r\n);}混合输出voidPrint_Data_Mixed(uint8_t*data,uint16_tlen){printf(Data[%d]:\r\n,len);for(uint16_ti0;ilen;i){printf(%02X ,data[i]);if((i1)%160){// 每16字节换行printf( );for(uint16_tji-15;ji;j){if(data[j]32data[j]126){printf(%c,data[j]);}else{printf(.);}}printf(\r\n);}}// 处理剩余字节if(len%16!0){for(uint16_tilen%16;i16;i){printf( );}printf( );for(uint16_tjlen-(len%16);jlen;j){if(data[j]32data[j]126){printf(%c,data[j]);}else{printf(.);}}printf(\r\n);}}五、常见问题与解决5.1 乱码问题现象串口输出乱码原因1. 波特率不匹配 2. 系统时钟配置错误 3. 数据格式不匹配解决// 验证波特率voidVerify_Baudrate(void){printf(SystemCoreClock: %d\r\n,SystemCoreClock);printf(UART BRR: 0x%04X\r\n,USART1-BRR);uint32_tactualBaudSystemCoreClock/(USART1-BRR0xFFFF);printf(Actual baudrate: %d\r\n,actualBaud);}5.2 数据丢失现象接收数据丢失原因1. 接收缓冲区溢出 2. 处理速度慢 3. 中断被阻塞解决// 使用环形缓冲区// 提高处理速度// 优化中断优先级5.3 发送卡死现象发送函数卡死原因1. 超时时间过短 2. UART未初始化 3. 引脚配置错误解决// 增加超时时间HAL_UART_Transmit(huart1,data,len,1000);// 1秒超时// 检查初始化if(huart1.Instance!USART1){printf(UART not initialized\r\n);}六、本讲核心要点6.1 记住这三句话串口配置波特率、数据位、停止位、校验位最常用8N1接收方式轮询简单、中断常用、DMA高效调试技巧printf重定向、分级日志、数据分析6.2 实践建议对于新手学习串口配置实践中断接收使用调试工具对于有经验工程师建立串口驱动模板实现环形缓冲区优化调试输出6.3 下讲预告第31讲将深入讲解实战SPI屏驱动快速试错、点亮屏幕SPI常用于驱动屏幕如何快速试错点亮屏幕下一讲将详细讲解。