【STM32】串口数据乱码 +待机模式
一、问题背景
在进行STM32待机模式的实验时,使用USART发送数据时,最后一字节的数据出现乱码。
1.1 问题现象
MCU串口发送数据Use USART%d to print\r\n
和Read Time from RTC:%d\r\n
。PC串口收到错误数据,具体现象见图1.1。
![]() 图a)文本显示 |
![]() 图b)HEX显示 |
二、相关配置
2.1 软件配置
2.1.1 主函数
点击查看代码
int main(void)
{
u32 AlarmValue=0;
OLED_Init();
SERIAL_Init();
MyRTC_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_WakeUpPinCmd(ENABLE);
Delay_ms(10);
SERIAL_Printf("Use USART%d to print\r\n",1);
AlarmValue=RTC_GetCounter()+10;
RTC_SetAlarm(AlarmValue);
while(1){
SERIAL_Printf("Read Time from RTC:%d\r\n",MyRTC_ReadTime());
// while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);
PWR_EnterSTANDBYMode();
}
return 0;
}
2.1.2 串口初始化函数
点击查看代码
void SERIAL_Init(void)
{
USART_InitTypeDef USART_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_InitStruct.USART_BaudRate = 1500000;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStruct);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
USART_Cmd(USART1, ENABLE);
}
2.1.3 串口发送函数
点击查看代码
static void SERIAL_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
}
void SERIAL_SendString(char *String)
{
uint16_t i;
uint16_t Length;
TxStringFlag = RESET;
Length = strlen(String);
for(i=0;i<Length;i++){
SERIAL_SendByte(String[i]);
}
TxStringFlag = SET;
}
int SERIAL_Printf(const char * format, ...)
{
char sprint_buf[BUFFER_MAXSIZE] = {0};
u16 n = 0;
va_list args;
va_start(args,format);
n = vsprintf(sprint_buf, format, args);
va_end(args);
SERIAL_SendString(sprint_buf);
return n;
}
2.2 硬件配置
2.2.1 硬件与接线
STM32F103C8T6的USART1口==>CH340 USB转串口模块=>PC的USB口

图 2.1
三、调试思路及过程
3.1 调试思路
在数据发送流程中,一步一步读取数据的值,看看在哪里改变了值。
3.2 调试过程
由于在待机模式下是无法进行Debug
调试的(见图 3.1),所以只能通过OLED显示屏来打印变量值。

图 3.1
在许久的调试中发现,数据的值未有异常,USART_SendData()
发送数据后,也加入了while
循环来等待USART_FLAG_TXE
被硬件置位。(见以下代码)
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
好奇怪啊?while
循环跳出,说明USART1->SR
寄存器的TXE
被硬件置为SET
,此时发送数据寄存器为空。那不就表示数据都发送出去了吗?可为啥PC端收到的数据是错误的呢?
去掉PWR_EnterSTANDBYMode();
看看?(见以下代码)
SERIAL_Printf("Read Time from RTC:%d\r\n",MyRTC_ReadTime());
while(1){
}

图 3.2
从图3.2可以看到,数据恢复正常了,看来进入待机模式对USART1
发送数据有影响。
那么,如果延迟一段时间,再进入待机模式呢,数据会有什么变化?于是进行如下改动
Delay_ms(10);
PWR_EnterSTANDBYMode();
测试后发现,数据恢复正常了。
这么看来应该是,在数据发送的途中,MCU进入了待机模式。此时USART
外设时钟停止,导致数据无法正常发送出去。
为了确保数据正常发送出去,可以加个延迟函数,之后再进入待机模式。
加入延迟函数确实可以简单解决这个问题,但这个方法过于“笨拙”。无法做到数据确实发送出去后,立刻进入睡眠。有没有什么方法,可以做些改进呢?之前通过是通过USART_FLAG_TXE
标志来判断数据是否发送完成,实际测试下来,它并不是用来表示数据发送完成的有效标志。我们看一下其他的标志位,(代码如下)
USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
USART_FLAG_LBD: LIN Break detection flag
USART_FLAG_TXE: Transmit data register empty flag
USART_FLAG_TC: Transmission Complete flag
USART_FLAG_RXNE: Receive data register not empty flag
USART_FLAG_IDLE: Idle Line detection flag
USART_FLAG_ORE: OverRun Error flag
USART_FLAG_NE: Noise Error flag
USART_FLAG_FE: Framing Error flag
USART_FLAG_PE: Parity Error flag
注意到USART_FLAG_TC: Transmission Complete flag
,原来它才是表示发送数据完成的标志。
这个错误是由于我对于标志位没有正确理解导致的,现在把它加入到代码中,看看实验结果是否有改善。(代码如下)
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);//等待发送数据完成
PWR_EnterSTANDBYMode(); //进入待机模式

图 3.3
PC端数据接收正常,无乱码现象。
四、解决方法
4.1 延迟函数
进入待机模式前,加入延迟函数。
Delay_ms(10); //延迟的时间酌情设定
PWR_EnterSTANDBYMode();//进入待机模式
4.2 循环判断USART_FLAG_TC
标志位
进入待机模式前,加入while循环,来检测USART_FLAG_TC
标志。
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);//等待发送数据完成
PWR_EnterSTANDBYMode(); //进入待机模式
五、总结与反思
- 待机模式下无法进行
Debug
调试,会报No target
错误 USART_FLAG_TXE
被置位,仅表示发送数据寄存器为空,并不表示发送数据完成USART_FLAG_TC
被置位,表示发送数据真正完成- 调用
PWR_EnterSTANDBYMode();
前,请确保所有工作都已完成,相关数据都存储完成 - 熟练翻读查阅芯片参考手册,理解外设相关寄存器的位的作用
- 理解外设的状态标志的宏定义
六、资料参考
6.1 标志位描述
@param USART_FLAG: specifies the flag to check.
This parameter can be one of the following values:
@arg USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
@arg USART_FLAG_LBD: LIN Break detection flag
@arg USART_FLAG_TXE: Transmit data register empty flag
@arg USART_FLAG_TC: Transmission Complete flag
@arg USART_FLAG_RXNE: Receive data register not empty flag
@arg USART_FLAG_IDLE: Idle Line detection flag
@arg USART_FLAG_ORE: OverRun Error flag
@arg USART_FLAG_NE: Noise Error flag
@arg USART_FLAG_FE: Framing Error flag
@arg USART_FLAG_PE: Parity Error flag
6.2 参考手册描述

图 6.1
本文来自博客园,作者:upupupCoder,转载请注明原文链接:https://www.cnblogs.com/upupupCoder/p/17994217
posted on 2024-05-29 15:33 upupupCoder 阅读(98) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App