【STM32】串口数据乱码 +待机模式

一、问题背景

  在进行STM32待机模式的实验时,使用USART发送数据时,最后一字节的数据出现乱码。

1.1 问题现象

  MCU串口发送数据Use USART%d to print\r\nRead Time from RTC:%d\r\n。PC串口收到错误数据,具体现象见图1.1。

Typora-Logo
图a)文本显示
Typora-Logo
图b)HEX显示
图 1.1

二、相关配置

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

posted on 2024-05-29 15:33  upupupCoder  阅读(15)  评论(0编辑  收藏  举报

导航