第30讲:实战——USART串口收发原型快速搭建+调试
CSDN专栏:
- 嵌入式程序开发实战
- 嵌入式双范式AI编程
- 嵌入式开发必掌握
- 嵌入式求职面试技术资料
第30讲:实战——USART串口收发原型快速搭建+调试
一、USART基础
USART(通用同步/异步收发器)是嵌入式最常用的通信接口,用于与PC、其他MCU、模块通信。
1.1 USART配置要点
基本参数:
- 波特率:9600、19200、38400、57600、115200等
- 数据位:8位(最常用)
- 停止位:1位(最常用)
- 校验位:无(最常用)
- 流控:无(最常用)
引脚配置:
USART1:PA9(TX)、PA10(RX)
USART2:PA2(TX)、PA3(RX)
USART3:PB10(TX)、PB11(RX)
1.2 通信模式
轮询模式:
特点:阻塞等待
适用:简单应用、调试
中断模式:
特点:非阻塞、事件驱动
适用:实际应用
DMA模式:
特点:高效、CPU占用少
适用:高速、大数据量
二、串口发送实战
2.1 基础发送
需求:
串口发送"Hello World"Prompt:
生成STM32F103 USART发送代码: - USART1:PA9(TX)、PA10(RX) - 波特率:115200 - 功能:发送"Hello World" - 使用HAL库AI生成:
#include"stm32f1xx_hal.h"UART_HandleTypeDef huart1;voidUSART1_Init(void){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;huart1.Init.Mode=UART_MODE_TX_RX;huart1.Init.HwFlowCtl=UART_HWCONTROL_NONE;huart1.Init.OverSampling=UART_OVERSAMPLING_16;HAL_UART_Init(&huart1);}intmain(void){HAL_Init();SystemClock_Config();USART1_Init();char*msg="Hello 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_tdata=0x55;uint16_tvalue=1234;floattemperature=25.6f;// 十进制printf("Value: %d\r\n",value);// 十六进制printf("Hex: 0x%02X\r\n",data);// 浮点数printf("Temperature: %.2f\r\n",temperature);// 二进制printf("Binary: ");for(inti=7;i>=0;i--){printf("%d",(data>>i)&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中断接收代码: - USART1:PA9(TX)、PA10(RX) - 波特率:115200 - 功能:中断接收并回显 - 使用HAL库AI生成:
uint8_trxData;voidUSART1_Init(void){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;huart1.Init.Mode=UART_MODE_TX_RX;huart1.Init.HwFlowCtl=UART_HWCONTROL_NONE;huart1.Init.OverSampling=UART_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->Instance==USART1){// 回显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.head+1)%BUFFER_SIZE;if(nextHead!=rxBuffer.tail){// 缓冲区未满rxBuffer.buffer[rxBuffer.head]=data;rxBuffer.head=nextHead;}}uint8_tRingBuffer_Read(uint8_t*data){if(rxBuffer.head==rxBuffer.tail){// 缓冲区空return0;}*data=rxBuffer.buffer[rxBuffer.tail];rxBuffer.tail=(rxBuffer.tail+1)%BUFFER_SIZE;return1;}uint16_tRingBuffer_Available(void){return(rxBuffer.head-rxBuffer.tail+BUFFER_SIZE)%BUFFER_SIZE;}// 中断回调voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart->Instance==USART1){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_LEVEL>=1)printf("[ERROR] "fmt"\r\n",##__VA_ARGS__)#defineLOG_WARN(fmt,...)if(LOG_LEVEL>=2)printf("[WARN] "fmt"\r\n",##__VA_ARGS__)#defineLOG_INFO(fmt,...)if(LOG_LEVEL>=3)printf("[INFO] "fmt"\r\n",##__VA_ARGS__)#defineLOG_DEBUG(fmt,...)if(LOG_LEVEL>=4)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_ti=0;i<len;i++){printf("%02X ",data[i]);}printf("\r\n");}ASCII输出:
voidPrint_ASCII(uint8_t*data,uint16_tlen){printf("ASCII: ");for(uint16_ti=0;i<len;i++){if(data[i]>=32&&data[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_ti=0;i<len;i++){printf("%02X ",data[i]);if((i+1)%16==0){// 每16字节换行printf(" ");for(uint16_tj=i-15;j<=i;j++){if(data[j]>=32&&data[j]<=126){printf("%c",data[j]);}else{printf(".");}}printf("\r\n");}}// 处理剩余字节if(len%16!=0){for(uint16_ti=len%16;i<16;i++){printf(" ");}printf(" ");for(uint16_tj=len-(len%16);j<len;j++){if(data[j]>=32&&data[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_tactualBaud=SystemCoreClock/(USART1->BRR&0xFFFF);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常用于驱动屏幕,如何快速试错点亮屏幕?下一讲将详细讲解。
