手持设备电池盒可靠性测试报告编写规范
2026/9/23 15:46:26
串口通信的关键是两端参数匹配,否则会出现乱码或通信失败,核心参数如下:
| 参数 | 说明 | 常用值 |
|---|---|---|
| 波特率 | 每秒传输的比特数,代表通信速率 | 9600、115200(最常用)、38400、57600 |
| 数据位 | 每个字符的有效数据位数 | 8位(主流)、7位 |
| 停止位 | 每个字符结束的标志位 | 1位(主流)、1.5位、2位 |
| 校验位 | 用于检错,可选奇/偶/无校验 | 无校验(主流)、奇校验、偶校验 |
| 流控 | 控制数据传输节奏(可选) | 无流控(主流)、硬件流控(RTS/CTS)、软件流控(XON/XOFF) |
串口通信的硬件连接极其简单,核心是“交叉连接”(TX接RX,RX接TX),共地保证电平参考一致:
| 设备A(如STM32) | 设备B(如USB转串口模块/上位机) |
|---|---|
| TX(发送引脚) | RX(接收引脚) |
| RX(接收引脚) | TX(发送引脚) |
| GND(地) | GND(地) |
注意:
以STM32F103为例(寄存器/库函数/HAL库均可,本文以HAL库为例),讲解三种收发模式的实现逻辑,覆盖从基础到进阶的开发场景。
核心逻辑:通过不断检查寄存器状态,判断是否有数据接收/发送完成,适合简单、低频率的通信场景,缺点是占用CPU资源。
#include"stm32f1xx_hal.h"UART_HandleTypeDef huart1;// 串口1初始化(波特率115200,8N1:8位数据位、无校验、1位停止位)voidMX_USART1_UART_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;if(HAL_UART_Init(&huart1)!=HAL_OK){Error_Handler();}}// 底层硬件初始化(引脚、时钟)voidHAL_UART_MspInit(UART_HandleTypeDef*uartHandle){GPIO_InitTypeDef GPIO_InitStruct={0};if(uartHandle->Instance==USART1){__HAL_RCC_USART1_CLK_ENABLE();// 使能USART1时钟__HAL_RCC_GPIOA_CLK_ENABLE();// 使能GPIOA时钟// PA9(TX):推挽复用输出GPIO_InitStruct.Pin=GPIO_PIN_9;GPIO_InitStruct.Mode=GPIO_MODE_AF_PP;GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);// PA10(RX):浮空输入GPIO_InitStruct.Pin=GPIO_PIN_10;GPIO_InitStruct.Mode=GPIO_MODE_INPUT;GPIO_InitStruct.Pull=GPIO_NOPULL;HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);}}// 发送字符串(轮询模式)voidUART1_SendString(uint8_t*str){while(*str!='\0'){// 等待发送寄存器为空while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TXE)==RESET);// 发送单个字节HAL_UART_Transmit(&huart1,str,1,100);str++;}}// 主函数调用intmain(void){HAL_Init();SystemClock_Config();MX_USART1_UART_Init();while(1){UART1_SendString((uint8_t*)"Hello UART!\r\n");HAL_Delay(1000);// 每秒发送一次}}uint8_trecv_buf[100];uint8_trecv_len=0;// 接收单个字节(轮询模式)uint8_tUART1_RecvByte(void){uint8_tdata=0;// 等待接收寄存器非空while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)==RESET);HAL_UART_Receive(&huart1,&data,1,100);returndata;}// 主函数中接收while(1){recv_buf[recv_len]=UART1_RecvByte();if(recv_buf[recv_len]=='\n')// 以换行符作为结束标志{recv_buf[recv_len+1]='\0';UART1_SendString((uint8_t*)"收到数据:");UART1_SendString(recv_buf);recv_len=0;// 重置接收长度}else{recv_len++;}}核心逻辑:无需CPU轮询,当串口有数据接收/发送完成时,触发中断,CPU仅在中断时处理数据,大幅降低CPU占用率,适合实时性要求高的场景。
在HAL_UART_MspInit中添加中断配置:
voidHAL_UART_MspInit(UART_HandleTypeDef*uartHandle){// 原有引脚、时钟配置...// 开启USART1中断HAL_NVIC_SetPriority(USART1_IRQn,0,0);// 设置优先级HAL_NVIC_EnableIRQ(USART1_IRQn);// 使能中断}uint8_tuart1_recv_buf[1024];// 接收缓冲区uint16_tuart1_recv_len=0;// 接收长度uint8_tuart1_recv_flag=0;// 接收完成标志// 启动接收中断(开启RXNE和IDLE中断)voidUART1_Start_Recv_IT(void){__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);// 清除IDLE标志__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);// 开启接收非空中断__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);// 开启IDLE中断(无数据接收时触发)HAL_UART_Receive_IT(&huart1,&uart1_recv_buf[uart1_recv_len],1);}// USART1中断服务函数voidUSART1_IRQHandler(void){uint32_ttemp;if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)!=RESET)// 接收非空{HAL_UART_IRQHandler(&huart1);// 调用HAL库中断处理函数uart1_recv_len++;// 接收长度+1// 继续开启接收中断HAL_UART_Receive_IT(&huart1,&uart1_recv_buf[uart1_recv_len],1);}if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=RESET)// IDLE中断(接收完成){__HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除IDLE标志temp=huart1.Instance->SR;// 读SR寄存器(清标志)temp=huart1.Instance->DR;// 读DR寄存器uart1_recv_flag=1;// 置位接收完成标志}}// 主函数处理接收数据intmain(void){HAL_Init();SystemClock_Config();MX_USART1_UART_Init();UART1_Start_Recv_IT();// 启动接收中断while(1){if(uart1_recv_flag==1){// 发送接收到的数据HAL_UART_Transmit(&huart1,uart1_recv_buf,uart1_recv_len,1000);// 重置标志和长度uart1_recv_flag=0;uart1_recv_len=0;}}}uint8_tuart1_send_buf[1024];uint16_tuart1_send_len=0;uint16_tuart1_send_index=0;// 启动发送中断voidUART1_Send_IT(uint8_t*buf,uint16_tlen){uart1_send_len=len;uart1_send_index=0;memcpy(uart1_send_buf,buf,len);__HAL_UART_ENABLE_IT(&huart1,UART_IT_TXE);// 开启发送空中断}// 中断服务函数中处理发送voidUSART1_IRQHandler(void){// 原有接收中断处理...if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TXE)!=RESET)// 发送寄存器空{if(uart1_send_index<uart1_send_len){huart1.Instance->DR=uart1_send_buf[uart1_send_index];// 发送单个字节uart1_send_index++;}else{__HAL_UART_DISABLE_IT(&huart1,UART_IT_TXE);// 关闭发送中断}}}// 主函数调用UART1_Send_IT((uint8_t*)"Interrupt Send!\r\n",16);核心逻辑:直接内存访问(DMA),数据收发无需CPU参与,由DMA控制器直接在内存和串口寄存器间传输数据,CPU仅需初始化DMA和处理传输完成事件,适合大数据量、高波特率的通信场景。
DMA_HandleTypeDef hdma_usart1_rx;DMA_HandleTypeDef hdma_usart1_tx;// 串口1 DMA初始化voidMX_USART1_DMA_Init(void){// RX DMA配置(DMA1通道5)hdma_usart1_rx.Instance=DMA1_Channel5;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_MEDIUM;if(HAL_DMA_Init(&hdma_usart1_rx)!=HAL_OK){Error_Handler();}__HAL_LINKDMA(&huart1,hdmarx,hdma_usart1_rx);// 关联串口和DMA// TX DMA配置(DMA1通道4)hdma_usart1_tx.Instance=DMA1_Channel4;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_MEDIUM;if(HAL_DMA_Init(&hdma_usart1_tx)!=HAL_OK){Error_Handler();}__HAL_LINKDMA(&huart1,hdmatx,hdma_usart1_tx);}// 在HAL_UART_MspInit中开启DMA时钟voidHAL_UART_MspInit(UART_HandleTypeDef*uartHandle){// 原有引脚、中断配置...__HAL_RCC_DMA1_CLK_ENABLE();// 使能DMA1时钟// DMA中断配置(可选,用于传输完成回调)HAL_NVIC_SetPriority(DMA1_Channel5_IRQn,1,0);HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);HAL_NVIC_SetPriority(DMA1_Channel4_IRQn,1,0);HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);}#defineUART1_DMA_BUF_LEN1024uint8_tuart1_dma_recv_buf[UART1_DMA_BUF_LEN];uint16_tuart1_dma_recv_last=0;// 启动DMA接收voidUART1_Start_DMA_Recv(void){HAL_UART_Receive_DMA(&huart1,uart1_dma_recv_buf,UART1_DMA_BUF_LEN);}// 主函数中处理DMA接收数据intmain(void){HAL_Init();SystemClock_Config();MX_USART1_UART_Init();MX_USART1_DMA_Init();UART1_Start_DMA_Recv();while(1){// 计算新接收的数据长度(循环模式下,通过当前指针和上一次指针的差值)uint16_tcurrent_len=UART1_DMA_BUF_LEN-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);uint16_tnew_len=current_len-uart1_dma_recv_last;if(new_len>0){// 发送接收到的数据(DMA发送)HAL_UART_Transmit_DMA(&huart1,&uart1_dma_recv_buf[uart1_dma_recv_last],new_len);uart1_dma_recv_last=current_len;// 更新上一次指针}}}// DMA发送字符串voidUART1_Send_DMA(uint8_t*buf,uint16_tlen){// 等待上一次DMA发送完成while(HAL_DMA_GetState(&hdma_usart1_tx)!=HAL_DMA_STATE_READY);HAL_UART_Transmit_DMA(&huart1,buf,len);}// 主函数调用uint8_tsend_data[]="DMA Send: Hello World! This is a long data test...\r\n";UART1_Send_DMA(send_data,sizeof(send_data));// 接收完成回调voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart){if(huart->Instance==USART1){// 循环模式下,无需重新启动,DMA会自动继续接收// 可在此处处理接收完成逻辑}}// 发送完成回调voidHAL_UART_TxCpltCallback(UART_HandleTypeDef*huart){if(huart->Instance==USART1){// 发送完成,可置位标志}}| 工具 | 用途 | 操作要点 |
|---|---|---|
| USB转串口模块(CH340/PL2303) | 单片机与上位机通信的桥梁 | 安装对应驱动,确认COM口编号 |
| 串口助手(SSCOM/串口调试助手) | 上位机收发数据、验证通信 | 配置波特率/数据位/停止位/校验位与单片机一致 |
| 示波器/逻辑分析仪 | 分析TX/RX电平、波特率、帧格式 | 抓取起始位/数据位/停止位,排查电平异常 |
| 万用表 | 测量TX/RX引脚电平、检查接线 | 空闲时TX/RX应为高电平(3.3V/5V) |
| 问题现象 | 原因分析 | 解决方案 |
|---|---|---|
| 串口助手接收乱码 | 1. 波特率不匹配;2. 晶振频率错误;3. 电平不匹配;4. 数据位/停止位错误 | 1. 核对波特率;2. 检查单片机晶振配置(如STM32系统时钟);3. 用电平转换模块;4. 确认8N1等参数一致 |
| 无法接收数据 | 1. RX/TX接反;2. 接收中断未开启;3. 缓冲区溢出;4. 引脚配置错误 | 1. 调换RX/TX接线;2. 检查中断使能代码;3. 增大缓冲区或开启DMA;4. RX引脚配置为浮空输入 |
| 中断不触发 | 1. 中断优先级配置错误;2. 中断标志未清除;3. NVIC未使能 | 1. 调整中断优先级;2. 中断服务函数中清除标志;3. 调用HAL_NVIC_EnableIRQ() |
| DMA传输失败 | 1. DMA通道配置错误;2. 内存/外设地址错误;3. DMA模式错误 | 1. 核对DMA通道(如USART1_RX对应DMA1通道5);2. 确认Periph→Memory方向;3. 接收用循环模式,发送用正常模式 |
| 数据丢失/不完整 | 1. 缓冲区太小;2. 中断响应不及时;3. 波特率过高(硬件不支持) | 1. 增大缓冲区;2. 优化中断优先级;3. 降低波特率(如从115200降至9600) |
// 51单片机串口初始化(9600波特率,11.0592MHz晶振)voidUART_Init(void){TMOD|=0x20;// 定时器1工作在模式2(8位自动重装)TH1=0xFD;// 9600波特率初值TL1=0xFD;TR1=1;// 启动定时器1SCON=0x50;// 串口工作在模式1,REN=1(允许接收)EA=1;// 开启总中断ES=1;// 开启串口中断}// 51串口中断服务函数voidUART_ISR(void)interrupt4{if(RI)// 接收中断{RI=0;// 清除接收标志P1=SBUF;// 接收到的数据输出到P1口SBUF=SBUF;// 回显数据while(!TI);// 等待发送完成TI=0;// 清除发送标志}}// Arduino串口示例(9600波特率)voidsetup(){Serial.begin(9600);// 初始化串口,波特率9600}voidloop(){if(Serial.available()>0)// 有数据接收{chardata=Serial.read();// 读取单个字节Serial.print("收到:");Serial.println(data);// 回显数据}Serial.println("Arduino UART Test");delay(1000);}