我是靠谱客的博主 害羞西牛,最近开发中收集的这篇文章主要介绍STM32从停止模式唤醒后程序执行缓慢及串口乱码解决方法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

测试STM32单片机低功耗模式时,遇到一个问题。当单片机从停止模式被唤醒后,LED指示灯闪烁变慢,同时串口通信出现乱码。
程序如下:


//将串口接收口做为中断唤醒
void EXIT_UART_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource10);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    EXTI_InitStructure.EXTI_Line = EXTI_Line10;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

}
void EXTI15_10_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line10) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line10);
	}
}
//进入停止模式   任意外部中断唤醒  WKUP不能唤醒  
void enter_stop_mode(void)
{
    EXIT_UART_Init();											//RX引脚配置为外部中断
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 		//开电源管理时钟
    PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);		//进入停机模式
}

进入停止模式时,首先将串口接收引脚PA10设置为外部中断,然后配置电源时钟,下来进入停止模式。在主程序中通过一个按键控制程序进入停止模式,然后给串口发送任意数据可以唤醒停止模式。
当程序从停机模式被唤醒后,发现LED闪烁频率变慢。
正常情况下LED指示灯波形为:
在这里插入图片描述
LED高低电平时长为51ms。
当从停机模式唤醒后,LED波形为:
在这里插入图片描述
从停机模式被唤醒后,LED高低电平时长变为450ms左右,和正常情况下为1:9的关系。为什么会这样呢?看到STM32中文参考手册里面有这样一句话:
在这里插入图片描述
退出停止模式时,HSI 被选为系统时钟。
在这里插入图片描述
HSI时钟为8MHz,而开发板的时钟是由外部晶振8MHz倍频到72MHz,刚好相差了9倍。所以从停机模式被唤醒后,LED的翻转速度为正常的1/9。
于是代码修改为从停机模式唤醒后,重新初始化一次时钟。

void EXTI15_10_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line10) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line10);
		SystemInit();						//重新设置时钟
	}
}

这样从停止模式恢复后,LED指示灯正常。但是又发现新的问题,串口通信出现了乱码。
在这里插入图片描述
主程序中隔3s发送一次数据,用来提示程序运行情况。当从停机模式唤醒后,会出现一次串口乱码情况。会不会是停止模式唤醒是只重新设置了系统时钟,没有重新设置串口引起的。那么在中断中在增加一个串口初始化看看。

void EXTI15_10_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line10) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line10);
		SystemInit();						//重新设置时钟
		uart_init(9600);					//重新初始化串口  
	}
}

增加串口初始化后,依然会出现乱码。那是什么原因造成的?会不会是在进入停止模式后,串口的寄存器某个位发生了改变? 在初始化串口前,先复位一次串口试试。

void EXTI15_10_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line10) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line10);
		SystemInit();						            //重新设置时钟
		USART_DeInit(USART1);				//复位串口
		uart_init(9600);					           //重新初始化串口  
	}
}

先将串口复位后,在初始化串口,未出现乱码情况。
在这里插入图片描述
为了验证这个现象,在调试模式看看串口寄存器的值是不是发生了变化,正常情况下串口寄存器值:
在这里插入图片描述
还未初始化串口时,串口1寄存器的值 SR寄存器为0xc0,其他寄存器全部为0。
在这里插入图片描述
串口初始化结束后,BRR寄存器值变为0x1D4C,CR1寄存器值变为0x202C。
然后全速运行一会程序,进入停止模式,再次唤醒后观察串口1寄存器的值。
在这里插入图片描述

当从停止模式唤醒后,SR寄存器值变为了0xFA,DR寄存器的值变成了0x55。
可以看出,串口的状态寄存器和数据寄存器发生了改变。所以在初始化串口之前要重新复位状态寄存器,和数据寄存器。那么在停机模式唤醒时不用复位串口,只需复位串口状态寄存器和数据寄存器就行。于是代码修改如下:

void EXTI15_10_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line10) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line10);
		SystemInit();						//重新设置时钟
		USART1->SR = 0x00C0;                //复位状态寄存器
		USART1->DR = 0x00;                  //复位数据寄存器	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
	}
}

中断中唤醒时,先重置系统时钟,然后复位串口状态寄存器和数据寄存器,然后重新初始化串口和GPIOA口时钟。
测试结果如下:
在这里插入图片描述
通过串口数据可以看出,串口未出现复位。同时和刚才直接复位串口相比,接收的数据中多了一行 退出停机模式提示。说明刚才直接复位串口的方法,会丢失一组数据。
主程序代码如下:

//进入停止模式   任意外部中断唤醒  WKUP不能唤醒  停止模式唤醒后系统 默认为 HSI 需要重新设置时钟
void enter_stop_mode(void)
{
    EXIT_UART_Init();											//RX引脚配置为外部中断
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 		//开电源管理时钟
    PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);		//进入停机模式
}
int main(void)
{
    u8 i = 0, j = 0;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    delay_init();       										//延时函数初始化
    LED_Init();         										//初始化与LED连接的硬件接口
    KEY_Init();
    uart_init(9600);

    LED = 1;
    delay_ms(500);
    printf("low power test! rnrn");
    while(1)
    {
        i =  KEY_Scan(1);
        switch(i)
        {
        case 0:
            break;
        case 1:
            printf("进入停机模式rnrn");
            enter_stop_mode();									//唤醒后 接着下一行语句执行
            printf("退出停机模式rnrn");              		//唤醒后执行当前语句
            break;
        case 2:
            printf("进入待机模式rnrn");				
            Sys_Enter_Standby();						 		//唤醒后从程序开始位置执行
            printf("退出待机模式rnrn");				 		//执行不到这块
            break;
        case 3:
            printf("进入睡眠模式 中断唤醒 rnrn");
            sleep_mode_wfi();									//唤醒后接着下一条语句执行
            printf("退出睡眠模式 中断唤醒 rnrn");     		//唤醒后执行当前语句
            break;
        case 4:
            printf("进入睡眠模式 事件唤醒 rnrn");
            sleep_mode_wfe();									//唤醒后接着下一条语句执行
            printf("退出睡眠模式 事件唤醒 rnrn");				//唤醒后执行当前语句
            break;
        }

        j++;
       
		if(j>199)
		{
			  j = 0;
			printf("low power test running!rn");
		}

	   if(j % 5==0)
        {       
            LED = !LED;
        }
        delay_ms(10);

    }
}

总结:在使用低功耗停止模式时,从停止模式唤醒时,需要重新配置系统时钟,外设重新配置时最好先复位,然后再配置。

最后

以上就是害羞西牛为你收集整理的STM32从停止模式唤醒后程序执行缓慢及串口乱码解决方法的全部内容,希望文章能够帮你解决STM32从停止模式唤醒后程序执行缓慢及串口乱码解决方法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部