测试STM32单片机低功耗模式时,遇到一个问题。当单片机从停止模式被唤醒后,LED指示灯闪烁变慢,同时串口通信出现乱码。
程序如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46//将串口接收口做为中断唤醒 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。
于是代码修改为从停机模式唤醒后,重新初始化一次时钟。
1
2
3
4
5
6
7
8
9void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line10) != RESET) { EXTI_ClearITPendingBit(EXTI_Line10); SystemInit(); //重新设置时钟 } }
这样从停止模式恢复后,LED指示灯正常。但是又发现新的问题,串口通信出现了乱码。
主程序中隔3s发送一次数据,用来提示程序运行情况。当从停机模式唤醒后,会出现一次串口乱码情况。会不会是停止模式唤醒是只重新设置了系统时钟,没有重新设置串口引起的。那么在中断中在增加一个串口初始化看看。
1
2
3
4
5
6
7
8
9
10void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line10) != RESET) { EXTI_ClearITPendingBit(EXTI_Line10); SystemInit(); //重新设置时钟 uart_init(9600); //重新初始化串口 } }
增加串口初始化后,依然会出现乱码。那是什么原因造成的?会不会是在进入停止模式后,串口的寄存器某个位发生了改变? 在初始化串口前,先复位一次串口试试。
1
2
3
4
5
6
7
8
9
10
11
12void 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。
可以看出,串口的状态寄存器和数据寄存器发生了改变。所以在初始化串口之前要重新复位状态寄存器,和数据寄存器。那么在停机模式唤醒时不用复位串口,只需复位串口状态寄存器和数据寄存器就行。于是代码修改如下:
1
2
3
4
5
6
7
8
9
10
11
12void 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口时钟。
测试结果如下:
通过串口数据可以看出,串口未出现复位。同时和刚才直接复位串口相比,接收的数据中多了一行 退出停机模式提示。说明刚才直接复位串口的方法,会丢失一组数据。
主程序代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66//进入停止模式 任意外部中断唤醒 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从停止模式唤醒后程序执行缓慢及串口乱码解决方法内容请搜索靠谱客的其他文章。
发表评论 取消回复