初始化PE要多久,【STM32F103ZET6开发板】第2-10讲:电源管理。

电源管理

实验目的

  • 掌握STM32电源管理实现降低功耗的有效方法及原理。
  • 掌握STM32三种低功耗模式进入和退出的方法、区别及相关程序设计。

实验内容

  • 编写程序观察进入待机模式后如何退出待机模式。编写程序观察进入停止模式后如何退出停止模式。编写程序观察进入睡眠模式后如何退出睡眠模式。

电源管理原理介绍

STM32时钟树介绍

时钟系统是处理器的核心,在学习STM32F103系列MCU过程中,时钟系统的熟练掌握会有助于对很多知识点的学习。

STM32F103系列MCU中,有五个时钟源,分别为HSI、HSE、LSI、LSE和PLL。

  • HSI是高速内部时钟,RC振荡器,频率为8MHz。HSE是高速外部时钟,接外部时钟源,频率范围为4MHz~16MHz。LSI是低速内部时钟,RC振荡器,频率为40KHz。LSE是低速外部时钟,接外部时钟源,频率为32.768kHz晶振。PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
  • 注:因PLL时钟源也是由HSI或HSE提供的,所以也有说法STM32F103系列MCU的时钟源是四个,即HSI、HSE、LSI和LSE。

STM32F103系列MCU时钟系统有个非常形象的称呼叫“时钟树”,这个 “时钟树”非常直观和清晰地诠释出各时钟之间的关系、各个外设的时钟来源以及需要注意的配置事项。下图即是STM32F103系列MCU时钟树的示意图。

初始化PE要多久,【STM32F103ZET6开发板】第2-10讲:电源管理。

图1:STM32时钟树示意图

  • 注:能为系统时钟SYSCLK提供时钟源的只有HSI、HSE和PLL。使用HSI内部8MHZ的RC振荡器可以省下外部晶振电路,但该振荡器精度有限,通常HSI精度比使用HSE差十倍以上。所以我们一般是选择使用外部晶振给PLL提供时钟源,再对PLL进行倍频后给系统时钟提供时钟源。

STM32低功耗模式

STM32F103系列MCU在系统或电源复位以后,微控制器处于运行状态。当CPU不需要继续运行时,可以利用多种低功耗模式来节省功耗,针对STM32F103系列MCU,有三种低功耗模式:待机模式、停止模式和睡眠模式。

另外,还可以通过降低系统时钟、关闭APB和AHB总线上未被使用的外设时钟等方法来降低功耗。比如:将GPIO设置为输出模式,其最大速度设置为2MHz要比最大速度设置50MHz要省电。

关于STM32F103系列MCU三种低功耗模式一览表如下:

表1:三种低功耗模式

初始化PE要多久,【STM32F103ZET6开发板】第2-10讲:电源管理。

  • 注:STM32L1系列MCU采用ST专有的超低泄漏制程,具有创新型自主动态电压调节功能和5种低功耗模式,为各种应用提供了无与伦比的平台灵活性。STM32L1系列MCU扩展了超低功耗的理念,并且不会牺牲性能。

STM32F103系列待机模式

STM32F103系列MCU的待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。

  • 如何进入待机模式?

需要进行下面3个步骤。

1、设置Cortex-M3系统控制寄存器中的SLEEPDEEP位。

2、设置电源控制寄存器(PWR_CR)中的PDDS位。

3、清除电源控制/状态寄存器(PWR_CSR)中的WUF位。

  • 注:用户在实际使用时只需调用库函数void PWR_EnterSTANDBYMode(void)即可进入待机模式。无需关注程序是如何操作相关寄存器控制进入待机模式的,这也是使用库函数相对于操作寄存器的一大优势。
  • 如何退出待机模式?

下列任何一种都可唤醒待机模式的MCU。

1、WKUP引脚的上升沿。

2、RTC闹钟事件的上升沿。

3、NRST引脚上外部复位。

4、IWDG复位。

  • 注:例程会演示使用WKUP引脚(PA0)的上升沿和NRST引脚上外部复位。
  • 在待机模式下,所有的I/O引脚处于高阻态,除了以下的引脚:

1、复位引脚(始终有效)。

2、当被设置为防侵入或校准输出时的TAMPER引脚。

3、被使能的唤醒引脚。

STM32F103系列停止模式

STM32F103系列MCU的停止模式是在Cortex-M3的深睡眠模式基础上结合了外设的时钟控制机制,在停止模式下电压调节器可运行在正常或低功耗模式。此时在1.8V供电区域的的所有时钟都被停止,PLL、HSI和HSE RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。

  • 如何进入停止模式?

在以下条件下执行WFI(等待中断)或WFE(等待事件)指令:

1、设置Cortex-M3系统控制寄存器中的SLEEPDEEP位。

2、清除电源控制寄存器(PWR_CR)中的PDDS位。

3、通过设置PWR_CR中LPDS位选择电压调节器的模式。

  • 注:用户在实际使用时只需调用库函数void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)即可进入停止模式。
  • 如何退出停止模式?

1、如果执行WFI进入停止模式:

设置任一外部中断线为中断模式(在NVIC中必须使能相应的外部中断向量)。

2、如果执行WFE进入停止模式:

设置任一外部中断线为事件模式。

  • 注:例程会演示外部中断线为中断模式退出停止模式。
  • 在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。

STM32F103系列睡眠模式

STM32F103系列MCU的睡眠模式是三种低功耗模式中最耗电的。STM32F103系列MCU的睡眠模式下,电压调节器是打开的,只有CPU时钟是关闭的,其他时钟都是打开的。

  • 如何进入睡眠模式?

按照下列任何一种操作均可进入睡眠模式:

1、执行WFI指令进入睡眠状态。

2、执行WFE指令进入睡眠状态。

  • 注:用户在实际使用时只需调用__WFI()或__WFE()即可进入睡眠模式。
  • 如何退出睡眠模式?

1、执行WFI进入睡眠模式后需任一中断去唤醒。

2、执行WFE进入睡眠模式后需唤醒事件去唤醒。

  • 注:例程会演示外部中断去退出睡眠模式。
  • 在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态。

软件设计

PWR寄存器汇集

STM32F103提供了3个用于操作PWR的寄存器,如下表所示:

表1:PWR相关寄存器

序号

寄存器名

读/写

功能描述

1

PWR_CR

读/写

电源控制寄存器。

2

PWR_CSR

读/写

电源控制/状态寄存器。

每一种寄存器详细的描述在这里不做具体的介绍,大家可以参考目录:“第1部分:开发板硬件资料”“2 - 芯片资料”中“STM32英文参考手册_V15”或“STM32中文参考手册_V10”对应的PWR章节的寄存器部分认真研读。

PWR库函数汇集

ST官方提供的最终库函数版本是V3.5版本,该版本库函数提供了9个与PWR操作有关的库函数,如下表所示:

表2:PWR相关库函数汇集

序号

函数名

功能描述

1

PWR_DeInit

将外设PWR寄存器重设为缺省值。

2

PWR_BackupAccessCmd

使能或者失能RTC和后备寄存器访问。

3

PWR_PVDCmd

使能或者失能可编程电压探测器(PVD)。

4

PWR_PVDLevelConfig

设置PVD的探测电压阈值。

5

PWR_WakeUpPinCmd

使能或者失能唤醒管脚功能。

6

PWR_EnterSTOPMode

进入停止(STOP)模式。

7

PWR_EnterSTANDBYMode

进入待命(STANDBY)模式。

8

PWR_GetFlagStatus

检查指定PWR标志位设置与否。

9

PWR_ClearFlag

清除PWR的待处理标志位。

其他库函数详细的描述在这里不做具体的介绍,大家可以参考目录:“第1部分:开发板硬件资料”--->“2 - 芯片资料”中“STM32固件库使用手册的中文翻译版”对应的PWR章节的库函数部分认真研读。

STANDBY待机模式实验

  • 注:本节的实验源码是在“实验2-1-3:流水灯”的基础上修改。本节对应的实验源码是:“实验2-10-1:STANDBY待机模式”。

工程需要用到的库文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表4:实验需要用到的c文件

序号

文件名

后缀

功能描述

1

stm32f10x_rcc

.c

复位与时钟控制器。

2

stm32f10x_gpio

.c

通用输入输出。

3

stm32f10x_exti

.c

外部中断事件控制器。

4

stm32f10x_pwr

.c

电源管理。

5

misc

.c

中断向量控制器。

按下图所示将需要的c文件添加到工程。

初始化PE要多久,【STM32F103ZET6开发板】第2-10讲:电源管理。

图5:在新建工程中添加所需库函数c文件

头文件引用和路径设置

  • 需要引用的头文件

因为在“main.c”文件中使用了标准库和我们自己建的软件延时函数,所以需要引用下面的头文件。

  1. #include "stm32f10x.h"
  2. //delay这里报错的原因是:delay函数用汇编实现的,导致了MDK误报。
  3. #include "delay.h"
  • 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表5:头文件包含路径

序号

路径

描述

1

..\\Lib\\F10x_FWLIB\\inc

标准库头文件路径。

2

..\\User

stm32f10x_conf.h头文件在该路径,所以要包含。

3

..\\User\\bsp

自建的板卡相关的驱动文件路径。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

初始化PE要多久,【STM32F103ZET6开发板】第2-10讲:电源管理。

图6:添加头文件包含路径

编写代码

首先是对4个用户按键对应GPIO的外部中断功能进行配置并设置中断优先级,再分别给每一个中断服务函数设计不同的任务。按键S1的外部中断2对应的中断任务是使能WKUP唤醒引脚并调用函数进入待机模式,按键S2的外部中断3对应的中断任务是控制指示灯D2闪两次,按键S3的外部中断4对应的中断任务是控制指示灯D3闪两次,按键S4的外部中断5对应的中断任务是控制指示灯D4闪两次,具体代码如下。

代码清单:外部中断2中断服务函数

  1. /******************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. *****************************************************************************/
  6. void EXTI2_IRQHandler(void)
  7. {
  8. PWR_WakeUpPinCmd(ENABLE); //使能WKUP引脚的唤醒功能(PA0引脚)
  9. PWR_EnterSTANDBYMode(); //进入待机模式
  10. EXTI_ClearITPendingBit(EXTI_Line2); //清除EXTI中断线路挂起位
  11. }

代码清单:外部中断3中断服务函数

  1. /*****************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. ****************************************************************************/
  6. void EXTI3_IRQHandler(void)
  7. {
  8. led_on(LED_2); //点亮指示灯D2
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_2); //熄灭指示灯D2
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_2); //点亮指示灯D2
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_2); //熄灭指示灯D2
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line3); //清除EXTI中断线路挂起位
  17. }

代码清单:外部中断4中断服务函数

  1. /*****************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. ***************************************************************************/
  6. void EXTI4_IRQHandler(void)
  7. {
  8. led_on(LED_3); //点亮指示灯D3
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_3); //熄灭指示灯D3
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_3); //点亮指示灯D3
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_3); //熄灭指示灯D3
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI中断线路挂起位
  17. }

代码清单:外部中断5中断服务函数

  1. /***************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. ***************************************************************************/
  6. void EXTI9_5_IRQHandler(void)
  7. {
  8. led_on(LED_4); //点亮指示灯D4
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_4); //熄灭指示灯D4
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_4); //点亮指示灯D4
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_4); //熄灭指示灯D4
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI中断线路挂起位
  17. }

然后主函数中会对各外部中断进行初始化,打开电源管理的时钟,在主循环中是控制用户指示灯D1闪烁。

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE2为外部中断2
  6. EXTI_PE2_Init();
  7. //初始化PE3为外部中断3
  8. EXTI_PE3_Init();
  9. //初始化PE4为外部中断4
  10. EXTI_PE4_Init();
  11. //初始化PE5为外部中断5
  12. EXTI_PE5_Init();
  13. //配置嵌套向量中断控制器
  14. NVIC_Configuration();
  15. //使能电源管理单元的时钟
  16. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR , ENABLE);
  17. //主循环
  18. while(1)
  19. {
  20. led_on(LED_1); //点亮用户指示灯D1
  21. sw_delay_ms(200) ; //延时200ms
  22. led_off(LED_1); //熄灭用户指示灯D1
  23. sw_delay_ms(200) ; //延时200ms
  24. }
  25. }

实验步骤

  1. 解压“…\\第3部分:标准库教程和实验源码\\ 1 - 基础实验程序\\”目录下的压缩文件“实验2-10-1:STANDBY待机模式”,将解压后得到的文件夹拷贝到合适的目录,如“D\\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\\STANDBY\\projec”目录下的工程“STANDBY.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“STANDBY.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,可以观察到D1指示灯不停闪烁。
  7. 按下开发板按键S1,可观察到指示灯D1熄灭。
  8. 按下开发板按键S2,可观察到指示灯D1、D2均不会闪烁。
  9. 按下开发板按键S3,可观察到指示灯D1、D3均不会闪烁。
  10. 按下开发板按键S4,可观察到指示灯D1、D4均不会闪烁。
  11. 按下开发板按键S6,可观察到指示灯D1恢复闪烁。
  12. 再按下开发板按键S1,可观察到指示灯D1再次熄灭。
  13. 按下开发板按键S5,可观察到指示灯D1恢复闪烁。
  • 实验现象分析: 正常没有按键按下时,程序一直执行while循环中的控制指示灯D1间隔200ms闪烁一次的程序。按下按键S1,会进入外部中断2的中断服务程序,在外部中断2的中断服务程序中使能了WKUP引脚的唤醒功能(PA0引脚),又同时控制单片机进入待机模式。所以进入待机模式的单片机不会再执行while循环中的语句,指示灯D1不再闪烁。按下按键S2、S3、S4均不能唤醒处于待机模式的单片机,也无法执行外部中断3、外部中断4和外部中断5的中断服务函数的程序,故有上述实验现象。按下按键S6,有开发板原理图可知是将WKUP唤醒引脚PA0给了一个上升沿,该上升沿可达到唤醒待机模式的单片机的效果,所以重新执行while循环中的语句,指示灯D1闪烁。按键S5是复位按键,连接的是单片机复位引脚,按下按键S5,单片机复位程序重头开始执行,所以会重新执行while循环中的语句,指示灯D1闪烁。

STOP停止模式实验

  • 注:本节的实验源码是在“实验2-10-1:STANDBY待机模式”的基础上修改。本节对应的实验源码是:“实验2-10-2:STOP停止模式”。

工程需要用到的库文件

本实验需要用到的c文件以及添加头文件包含路径的方法与介绍“实验2-10-1:STANDBY待机模式”完全一样,在此不再赘述。

编写代码

首先是对4个用户按键对应GPIO的外部中断功能进行配置并设置中断优先级,再分别给每一个中断服务函数设计不同的任务。按键S1的外部中断2对应的中断任务是重新配置系统时钟,按键S2的外部中断3对应的中断任务是控制指示灯D2闪两次,按键S3的外部中断4对应的中断任务是重新配置系统时钟和控制指示灯D3闪两次,按键S4的外部中断5对应的中断任务是重新配置系统时钟和控制指示灯D4闪两次,具体代码如下。

代码清单:外部中断2中断服务函数

  1. /******************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/void EXTI2_IRQHandler(void)
  6. {
  7. SystemInit(); //重新配置系统时钟
  8. EXTI_ClearITPendingBit(EXTI_Line2); //清除EXTI中断线路挂起位
  9. }

代码清单:外部中断3中断服务函数

  1. /******************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. *****************************************************************************/
  6. void EXTI3_IRQHandler(void)
  7. {
  8. led_on(LED_2); //点亮指示灯D2
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_2); //熄灭指示灯D2
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_2); //点亮指示灯D2
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_2); //熄灭指示灯D2
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line3); //清除EXTI中断线路挂起位
  17. }

代码清单:外部中断4中断服务函数

  1. /****************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. ****************************************************************************/
  6. void EXTI4_IRQHandler(void)
  7. {
  8. SystemInit(); //重新配置系统时钟
  9. led_on(LED_3); //点亮指示灯D3
  10. sw_delay_ms(200); //延时200ms
  11. led_off(LED_3); //熄灭指示灯D3
  12. sw_delay_ms(200); //延时200ms
  13. led_on(LED_3); //点亮指示灯D3
  14. sw_delay_ms(200); //延时200ms
  15. led_off(LED_3); //熄灭指示灯D3
  16. sw_delay_ms(200); //延时200ms
  17. EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI中断线路挂起位
  18. }

代码清单:外部中断5中断服务函数

  1. /***************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************/
  6. void EXTI9_5_IRQHandler(void)
  7. {
  8. SystemInit(); //重新配置系统时钟
  9. led_on(LED_4); //点亮指示灯D4
  10. sw_delay_ms(200); //延时200ms
  11. led_off(LED_4); //熄灭指示灯D4
  12. sw_delay_ms(200); //延时200ms
  13. led_on(LED_4); //点亮指示灯D4
  14. sw_delay_ms(200); //延时200ms
  15. led_off(LED_4); //熄灭指示灯D4
  16. sw_delay_ms(200); //延时200ms
  17. EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI中断线路挂起位
  18. }

然后主函数中会对各外部中断进行初始化,使能PA0引脚的WKUP唤醒功能,并进入停止模式。在主循环中是控制用户指示灯D1闪烁。

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE2为外部中断2
  6. EXTI_PE2_Init();
  7. //初始化PE3为外部中断3
  8. EXTI_PE3_Init();
  9. //初始化PE4为外部中断4
  10. EXTI_PE4_Init();
  11. //初始化PE5为外部中断5
  12. EXTI_PE5_Init();
  13. //配置嵌套向量中断控制器
  14. NVIC_Configuration();
  15. //使能WKUP引脚的唤醒功能(PA0引脚)
  16. PWR_WakeUpPinCmd(ENABLE);
  17. //进入停止模式,设置电压调节器为低功耗模式,等待中断唤醒
  18. PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);
  19. //主循环
  20. while(1)
  21. {
  22. led_on(LED_1); //点亮用户指示灯D1
  23. sw_delay_ms(200) ; //延时200ms
  24. led_off(LED_1); //熄灭用户指示灯D1
  25. sw_delay_ms(200) ; //延时200ms
  26. }
  27. }

实验步骤

  1. 解压“…\\第3部分:标准库教程和实验源码\\ 1 - 基础实验程序\\”目录下的压缩文件“实验2-10-2:STOP停止模式”,将解压后得到的文件夹拷贝到合适的目录,如“D\\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\\STOP\\projec”目录下的工程“STOP.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“STOP.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,可以观察到指示灯D1、D2、D3、D4熄灭。
  7. 按下开发板按键S1,可观察到指示灯D1会间隔200ms一直闪烁。
  8. 按下开发板按键S2,可观察到指示灯D2会间隔200ms闪烁两次,之后指示灯D1再间隔200ms一直闪烁。
  9. 按下开发板按键S3,可观察到指示灯D3会间隔200ms闪烁两次,之后指示灯D1再间隔200ms一直闪烁。
  10. 按下开发板按键S4,可观察到指示灯D4会间隔200ms闪烁两次,之后指示灯D1再间隔200ms一直闪烁。
  11. 按下开发板按键S5,可观察到没有指示灯闪烁的情况。
  12. 按下开发板按键S6,可观察到依然没有指示灯闪烁的情况。
  13. 按下开发板按键S2,可观察到指示灯D2会间隔约1.2s闪烁两次,之后指示灯D1再间隔约1.2s一直闪烁。
  14. 按下开发板按键S1,可观察到指示灯D1会间隔200ms一直闪烁。
  15. 按下开发板按键S2,可观察到指示灯D2会间隔200ms闪烁两次,之后指示灯D1再间隔200ms一直闪烁。
  16. 按下开发板按键S3,可观察到指示灯D3会间隔200ms闪烁两次,之后指示灯D1再间隔200ms一直闪烁。
  17. 按下开发板按键S4,可观察到指示灯D4会间隔200ms闪烁两次,之后指示灯D1再间隔200ms一直闪烁。
  • 实验现象分析: 初始化程序后,在没有进入while循环前,已经控制单片机进入停止模式,所以不会有指示灯闪烁的情况(指示灯全灭)。按下按键S1,外部中断2会唤醒停止模式下的单片机,并执行外部中断2中断服务函数中的语句,而外部中断2中断服务函数中首先会将系统时钟重新配置(因为停止模式唤醒后系统时钟被改变),所以程序执行while循环中的控制指示灯D1闪烁的程序,闪烁间隔是200ms。按下按键S2、S3、S4则是正常的触发外部中断,程序会首先执行外部中断3、外部中断4和外部中断5中的服务函数的程序,即控制对应用户指示灯闪烁,然后才会执行while循环中的控制指示灯D1闪烁的程序。按键S5是复位按键,连接的是单片机复位引脚,按下按键S5,单片机复位程序重头开始执行,所以在执行while前,单片机就进入停止模式。按下按键S6,WKUP唤醒引脚上升沿不能唤醒停止模式下的单片机,所以依然没有指示灯闪烁的现象。按下按键S2,外部中断3会唤醒停止模式下的单片机,并执行外部中断3中断服务函数中的语句,而外部中断3中断服务函数中没有重新配置系统时钟的语句(而停止模式唤醒后系统时钟被改变),所以此时的200ms的延时函数不再是200ms,而是约1.2s,所以指示灯D2和指示灯D1闪烁的时间间隔被延长。再按下按键S1,会执行外部中断2中断服务函数中的语句,而外部中断2中断服务函数中首先会将系统时钟重新配置,所以此时指示灯闪烁的间隔恢复成200ms。(因为按键3和按键4对应的中断服务函数中都会重新配置系统时钟,所以操作按键S3或按键4与按键1的作用是一样的)

SLEEP睡眠模式实验

  • 注:本节的实验源码是在“实验2-10-1:STANDBY待机模式”的基础上修改。本节对应的实验源码是:“实验2-10-3:SLEEP睡眠模式”。

工程需要用到的库文件

本实验需要用到的c文件以及添加头文件包含路径的方法与介绍“实验2-10-1:STANDBY待机模式”完全一样,在此不再赘述。

编写代码

首先是对4个用户按键对应GPIO的外部中断功能进行配置并设置中断优先级,再分别给每一个中断服务函数设计不同的任务。按键S1的外部中断2对应的中断任务是使能WKUP唤醒引脚,按键S2的外部中断3对应的中断任务是控制指示灯D2闪两次,按键S3的外部中断4对应的中断任务是控制指示灯D3闪两次,按键S4的外部中断5对应的中断任务是控制指示灯D4闪两次,具体代码如下。

代码清单:外部中断2中断服务函数

  1. /*************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. *************************************************************************/
  6. void EXTI2_IRQHandler(void)
  7. {
  8. PWR_WakeUpPinCmd (ENABLE); //使能WKUP引脚的唤醒功能(PA0引脚)
  9. EXTI_ClearITPendingBit(EXTI_Line2); //清除EXTI中断线路挂起位
  10. }

代码清单:外部中断3中断服务函数

  1. /**************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. *************************************************************************/
  6. void EXTI3_IRQHandler(void)
  7. {
  8. led_on(LED_2); //点亮指示灯D2
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_2); //熄灭指示灯D2
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_2); //点亮指示灯D2
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_2); //熄灭指示灯D2
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line3); //清除EXTI中断线路挂起位
  17. }

代码清单:外部中断4中断服务函数

  1. /*****************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. ****************************************************************************/
  6. void EXTI4_IRQHandler(void)
  7. {
  8. led_on(LED_3); //点亮指示灯D3
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_3); //熄灭指示灯D3
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_3); //点亮指示灯D3
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_3); //熄灭指示灯D3
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI中断线路挂起位
  17. }

代码清单:外部中断5中断服务函数

  1. /**************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. *************************************************************************/
  6. void EXTI9_5_IRQHandler(void)
  7. {
  8. led_on(LED_4); //点亮指示灯D4
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_4); //熄灭指示灯D4
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_4); //点亮指示灯D4
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_4); //熄灭指示灯D4
  15. sw_delay_ms(200); //延时200ms
  16. EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI中断线路挂起位
  17. }

然后主函数中会对各外部中断进行初始化,在主循环中是控制用户指示灯D1闪烁1次,后进入睡眠模式。

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE2为外部中断2
  6. EXTI_PE2_Init();
  7. //初始化PE3为外部中断3
  8. EXTI_PE3_Init();
  9. //初始化PE4为外部中断4
  10. EXTI_PE4_Init();
  11. //初始化PE5为外部中断5
  12. EXTI_PE5_Init();
  13. //配置嵌套向量中断控制器
  14. NVIC_Configuration();
  15. //主循环
  16. while(1)
  17. {
  18. led_on(LED_1); //点亮用户指示灯D1
  19. sw_delay_ms(200) ; //延时200ms
  20. led_off(LED_1); //熄灭用户指示灯D1
  21. sw_delay_ms(200) ; //延时200ms
  22. __WFI(); //进入睡眠模式,等待中断唤醒
  23. }
  24. }

实验步骤

  1. 解压“…\\第3部分:标准库教程和实验源码\\ 1 - 基础实验程序\\”目录下的压缩文件“实验2-10-3:SLEEP睡眠模式”,将解压后得到的文件夹拷贝到合适的目录,如“D\\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\\SLEEP\\projec”目录下的工程“SLEEP.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“SLEEP.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,可观察到指示灯D1闪烁一次后熄灭,指示灯D2、D3、D4均是熄灭状态。
  7. 按下开发板按键S1,可观察到指示灯D1会再闪烁一次之后熄灭。
  8. 按下开发板按键S2,可观察到指示灯D2会闪烁两次,之后指示灯D1再闪烁一次。
  9. 按下开发板按键S3,可观察到指示灯D3会闪烁两次,之后指示灯D1再闪烁一次。
  10. 按下开发板按键S4,可观察到指示灯D4会闪烁两次,之后指示灯D1再闪烁一次。
  11. 按下开发板按键S6,可观察到指示灯D1不会闪烁。
  12. 按下开发板按键S5,可观察到指示灯D1会再闪烁一次。
  • 实验现象分析: 正常没有按键按下时,程序执行while循环中的控制指示灯D1闪烁一次的程序后执行进入睡眠模式的语句,使单片机进入睡眠状态,D1不再闪烁。按下按键S1,外部中断2会唤醒睡眠模式下的单片机,所以程序执行while循环中的控制指示灯D1闪烁一次的程序,之后单片机再次进入睡眠状态。按下按键S2、S3、S4均能唤醒处于睡眠模式的单片机,程序会首先执行外部中断3、外部中断4和外部中断5中的中断服务函数的程序,即控制对应用户指示灯闪烁,然后才会执行while循环中的控制指示灯D1闪烁一次的程序,之后单片机再次进入睡眠状态。按下按键S6,WKUP唤醒引脚上升沿可以唤醒待机模式下的单片机,但不能唤醒睡眠模式下的单片机,所以指示灯D1不会闪烁。按键S5是复位按键,连接的是单片机复位引脚,按下按键S5,单片机复位程序重头开始执行,所以会重新执行while循环中的语句,指示灯D1闪烁一次,之后单片机再次进入睡眠状态。

本文来自网络投稿,来自作者酷小二。不代表酷牛教程立场,如若转载,请注明出处:https://www.xukn.com/1342330.html

(0)
酷小二酷小二
上一篇 2023-10-10 23:57
下一篇 2023-10-10 23:57

发表回复

登录后才能评论