学习目标

  • 了解什么是电池管理单元PMU
  • 了解ARM32中的电源域
  • 了解几种省电模式

    学习内容

    PMU

    PMU全称Power Management Unit,电源管理单元。

    电源域

    113.png
    总共有三大电源域,包括VDD / VDDA域,1.2V域和备份域。

    VDD/VDDA域

    VDD/VDDA域如下图:
    114.png
    提供PMU 常规电源供应以下模块的供电:

  • 看门狗

  • 主频晶振
  • 内部晶振
  • ADC和DAC
  • LDO电源转换
  • 上电复位
  • 锁相环

    备份域

    备份域如下图:
    115.png
    备份域提供以下供电:

  • 外部低频时钟晶振

  • RTC
  • 上电复位
  • 电源转换

    1.2V域

    1.2V域如下图所示:
    116.png
    这个作用域主要提供:

  • AHB高速总线的供电

  • APB外设总线的供电
  • 内存
  • Cortex-M4的供电

    省电模式

    总共有三个省电模式:

  • 睡眠模式

  • 深度睡眠模式
  • 待机模式

    睡眠模式

    睡眠模式时,会关闭 1.2V域中的 Cortex-M4的供电。
    118.png

    深度睡眠模式

    进入深度模式时,会关闭 1.2V域中的所有供电;同时关闭VDD/VDDA域中的HXTAL``IRC16M``PLLs
    119.png

    待机模式

    进入待机模式时,会关闭1.2V域中的所有供电;同时关闭VDD/VDDA域中的LDO、IRC16M、HXTAL、PLLs
    121.png

    几种模式总结

    126.png

    案例需求


    009.png

  • 让LED1 每间隔一段时间闪烁(500ms)

  • 通过串口切换 省电模式

174.png175.png

  • 为KEY2配置外部中断按键,按下时LED2自动切换开关

176.png

  • 为PA0配置外部中断按键,按下时LED3自动切换开关

    模式初始化

    ```c static void sleep_mode() { // 电池管理单元时钟 rcu_periph_clock_enable(RCU_PMU); // 进入睡眠模式 pmu_to_sleepmode(WFI_CMD);

}

  1. ```c
  2. static void deepsleep_mode() {
  3. // 电池管理单元时钟
  4. rcu_periph_clock_enable(RCU_PMU);
  5. // 进入深度睡眠模式
  6. pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
  7. }
  1. static void standby_mode() {
  2. // 电池管理单元时钟
  3. rcu_periph_clock_enable(RCU_PMU);
  4. pmu_wakeup_pin_enable();
  5. // 进入待机模式
  6. pmu_to_standbymode();
  7. }

源码

  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "Usart0.h"
  6. static void sleep_mode() {
  7. // 电池管理单元时钟
  8. rcu_periph_clock_enable(RCU_PMU);
  9. // 进入睡眠模式
  10. pmu_to_sleepmode(WFI_CMD);
  11. }
  12. static void deepsleep_mode() {
  13. // 电池管理单元时钟
  14. rcu_periph_clock_enable(RCU_PMU);
  15. // 进入深度睡眠模式
  16. pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_ENABLE, WFI_CMD);
  17. }
  18. static void standby_mode() {
  19. // 电池管理单元时钟
  20. rcu_periph_clock_enable(RCU_PMU);
  21. pmu_wakeup_pin_enable();
  22. // 进入待机模式
  23. pmu_to_standbymode();
  24. }
  25. void Usart0_on_recv(uint8_t* data, uint32_t len) {
  26. printf("recv: %s\r\n", data);
  27. if(data[0] == 0x00) {
  28. printf("sleep mode \r\n");
  29. sleep_mode();
  30. } else if(data[0] == 0x01) {
  31. printf("deepsleep mode \r\n");
  32. deepsleep_mode();
  33. SystemInit();
  34. } else if(data[0] == 0x02) {
  35. printf("standby mode \r\n");
  36. standby_mode();
  37. }
  38. }
  39. static void EXTI0_config() {
  40. uint32_t extix = EXTI_0;
  41. uint32_t extix_irq = EXTI0_IRQn;
  42. uint32_t extix_irq_pre = 1;
  43. uint32_t extix_irq_sub = 1;
  44. uint32_t extix_trig = EXTI_TRIG_BOTH;
  45. uint32_t extix_port_rcu = RCU_GPIOA;
  46. uint32_t extix_port = GPIOA;
  47. uint32_t extix_pin = GPIO_PIN_0;
  48. uint32_t extix_src_port = EXTI_SOURCE_GPIOA;
  49. uint32_t extix_src_pin = EXTI_SOURCE_PIN0;
  50. /*************** gpio ****************/
  51. // PA0,
  52. // 时钟初始化
  53. rcu_periph_clock_enable(extix_port_rcu);
  54. // 配置GPIO模式
  55. gpio_mode_set(extix_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, extix_pin);
  56. /*************** exti ****************/
  57. // 时钟配置
  58. rcu_periph_clock_enable(RCU_SYSCFG);
  59. // 配置中断源
  60. syscfg_exti_line_config(extix_src_port, extix_src_pin);
  61. // 中断初始化
  62. exti_init(extix, EXTI_INTERRUPT, extix_trig);
  63. // 配置中断优先级
  64. nvic_irq_enable(extix_irq, extix_irq_pre, extix_irq_sub);
  65. // 使能中断
  66. exti_interrupt_enable(extix);
  67. // 清除中断标志位
  68. exti_interrupt_flag_clear(extix);
  69. }
  70. static void EXTI1_config() {
  71. uint32_t extix = EXTI_1;
  72. uint32_t extix_irq = EXTI1_IRQn;
  73. uint32_t extix_irq_pre = 1;
  74. uint32_t extix_irq_sub = 1;
  75. uint32_t extix_trig = EXTI_TRIG_BOTH;
  76. uint32_t extix_port_rcu = RCU_GPIOD;
  77. uint32_t extix_port = GPIOD;
  78. uint32_t extix_pin = GPIO_PIN_1;
  79. uint32_t extix_src_port = EXTI_SOURCE_GPIOD;
  80. uint32_t extix_src_pin = EXTI_SOURCE_PIN1;
  81. /*************** gpio ****************/
  82. // PA0,
  83. // 时钟初始化
  84. rcu_periph_clock_enable(extix_port_rcu);
  85. // 配置GPIO模式
  86. gpio_mode_set(extix_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, extix_pin);
  87. /*************** exti ****************/
  88. // 时钟配置
  89. rcu_periph_clock_enable(RCU_SYSCFG);
  90. // 配置中断源
  91. syscfg_exti_line_config(extix_src_port, extix_src_pin);
  92. // 中断初始化
  93. exti_init(extix, EXTI_INTERRUPT, extix_trig);
  94. // 配置中断优先级
  95. nvic_irq_enable(extix_irq, extix_irq_pre, extix_irq_sub);
  96. // 使能中断
  97. exti_interrupt_enable(extix);
  98. // 清除中断标志位
  99. exti_interrupt_flag_clear(extix);
  100. }
  101. void EXTI0_IRQHandler() {
  102. // 判断寄存器状态
  103. if(SET == exti_interrupt_flag_get(EXTI_0)) {
  104. if(gpio_input_bit_get(GPIOA, GPIO_PIN_0) == SET)
  105. {
  106. /* 按键按下操作的功能 */
  107. gpio_bit_toggle(GPIOG, GPIO_PIN_3);
  108. printf("PA0 press!\r\n");
  109. } else {
  110. /* 按键松开操作的功能 */
  111. printf("PA0 release!\r\n");
  112. }
  113. }
  114. // 清除中断标志位
  115. exti_interrupt_flag_clear(EXTI_0);
  116. }
  117. void EXTI1_IRQHandler() {
  118. // 判断寄存器状态
  119. if(SET == exti_interrupt_flag_get(EXTI_1)) {
  120. if(gpio_input_bit_get(GPIOD, GPIO_PIN_1) == SET)
  121. {
  122. gpio_bit_toggle(GPIOD, GPIO_PIN_7);
  123. printf("key2 press!\r\n");
  124. } else {
  125. /* 按键松开操作的功能 */
  126. printf("key2 release!\r\n");
  127. }
  128. }
  129. // 清除中断标志位
  130. exti_interrupt_flag_clear(EXTI_1);
  131. }
  132. int main(void)
  133. {
  134. systick_config();
  135. Usart0_init();
  136. EXTI0_config();
  137. EXTI1_config();
  138. // LED1
  139. rcu_periph_clock_enable(RCU_GPIOE);
  140. gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
  141. gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);
  142. // LED2
  143. rcu_periph_clock_enable(RCU_GPIOD);
  144. gpio_mode_set(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
  145. gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_7);
  146. // LED3
  147. rcu_periph_clock_enable(RCU_GPIOG);
  148. gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);
  149. gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3);
  150. while(1) {
  151. gpio_bit_toggle(GPIOE, GPIO_PIN_3);
  152. delay_1ms(500);
  153. }
  154. }

:::danger 注意:
中断优先级配置为NVIC_PRIGROUP_PRE2_SUB2情况下:

  1. 串口的抢占优先级不能设置为0,否则系统无法正常睡眠
  2. 深度睡眠的外部中断的抢占优先级必须设置为0或1,否则无法正常唤醒 :::

    练习

  • 实现模式切换,体会不同模式工作情况