STM32串口第一字节消失或者误码的原因和解决方案

本文探讨了STM32复位后串口打印首个字节出现误码或消失的问题,分析了其背后的原因,并提供了具体的解决办法。此外,还介绍了如何在Keil中正确配置串口以支持printf函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题外话:最近在一个复杂系统中遇到这个类似问题,设备的不同模块各种控制板有近10块,设备启动之后同时上电导致串口初始化发送各种乱码。对于复杂的系统,我的建议是不要只想着串口初始化的细节,因为它往往伴随着设备初始化顺序的问题,推荐的实践是将不同的板子按初始化优先级进行顺序上电初始化。高优先级的板子上电之后,设置IO、清空串口缓存等各种初始化操作完成之后再使能下级板上电初始化,低优先级板子上电也先初始化,然后重复上面的操作使能更低一级优先级板子上电初始化,直至所有板子上电初始化完成。

话不多说,开始正文,现象:stm32复位之后串口打印的第一个字节误码或者消失(这是两个问题,下面分别分析)。

误码

原因:误码多是由于端口初始化有问题。使用ST官方v3.5的标准库时,对串口输出端口进行了重复初始化。

如下代码:

    /* PA[15:0] 设置为推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 串口TX端口PA9 设置为复用推挽输出 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

 可以看到串口TX端口所在的PA9引脚被进行了两次初始化,这种情况会导致第一字节误码。

解决方案:上面的程序明显是为了偷懒用了GPIO_Pin_All,如果挨个写就没问题了。或者直接操作寄存器也是可以的。

第一字节消失(16年资料,不包更新,谁知道会不会修复)

原因:看数据手册
 


 
 


 一般我们的串口查询方式的发送代码如下,包括ST官方例程里的printf串口打印的实现也是这样的代码。

{
    USART_SendData(USART1, dat);
    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {}
}

 结合上面的图和程序很容易发现问题。

‘TXE’和‘TC’ 标志位复位值都是‘1’。
 那么当我们复位之后通过USART_SendData函数向USART_DR寄存器写入第一个字节,然后通过while查询TC标志的时候,因为TC初始值是‘1’,所以直接就跳出了while,哪怕此时串口并没有开始发送!第一字节就是这样被跳过去了。。

解决方案:调用USART_SendData函数之前,先将‘TC’标志位清‘0’就OK,代码如下

{
    USART_ClearFlag(USART1, USART_FLAG_TC);
    USART_SendData(USART1, dat);
    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {}
}

 -----------------------------关于串口printf---------------------------------------------------------

2024年了,主流的嵌入式IDE基本都支持stdio函数,51都可以用printf,即使不用串口,如果格式化输入输出的需求很频繁,建议直接 #include "stdio" 然后sprintf和sscanf也是非常方便的。

在keil中使用串口printf,ST官方有例程,需要在main.c中添加如下代码,16年的资料,不包更新

#include <stdio.h>

#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    USART_ClearFlag(USART1, USART_FLAG_TC);
    USART_SendData(USART1, (uint8_t) ch);

    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {}

    return ch;
}

 注意:还需要在设置中添加MicroLib才能够正常使用printf函数!!!如下图
 

 
### STM32串口接收前几个字节出现乱码解决方案STM32微控制器通过串口接收到的数据中前几个字节出现乱码时,这通常是由初始化设置当或硬件连接问题引起的。以下是详细的分析解决办法: #### 初始化配置检查 确保UART接口的参数配置正确至关重要。波特率、数据位、停止位以及校验方式都应与上位机一致[^1]。 ```c // UART Initialization Function static void MX_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(); } } ``` #### 中断处理优化 对于连续接收多字符的情况,采用中断驱动的方式能够有效提高效率并减少误码的可能性。注意回调函数中的指针管理,防止数组越界访问造成异常行为[^2]。 ```c char rxdata[30]; uint8_t rxdat; unsigned char rx_pointer; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ if(huart->Instance==USART1){ rxdata[rx_pointer++] = rxdat; // 防止缓冲区溢出 if(rx_pointer >= sizeof(rxdata)){ rx_pointer = 0; // 或者其他重置逻辑 } HAL_UART_Receive_IT(&huart1, &rxdat, 1); } } ``` #### 接收稳定性增强措施 为了进一步提升接收稳定性准确性,建议采取如下策略: - **增加延时**:在发送命令之后适当加入短时间延迟,等待传输完成再继续操作; - **清除缓存**:每次准备读取新消息之前清空旧有的未处理数据; - **同步机制**:设计特定协议头来实现帧同步,从而忽略掉任何意外产生的错误起始位置。 #### 测试验证流程 针对具体现象进行针对性测试有助于快速定位问题所在。例如尝试单独输出简单字符检验基本功能正常与否;利用调试工具观察实际波形特征排除物理层干扰因素等[^4]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值