经过初步了解,HPM6750有一种低功耗模式,叫做掉电状态,此时CPU会处于停止状态,但是一单有外部中断,则马上恢复。
不过掉电状态的时候,RAM中的数据都会都是,所以掉电状态不能进行调试,需要直接将固件烧录到代码中。

经过一番尝试,在RGB-LED灯的实例基础上,结合掉电状态进行测试。
预期如下:
1. 板子启动后,RGB-LED呼吸灯自动启动
2. 按PBUTN按键 16秒以后,RGB-LED呼吸灯停止
3. 一会后,再按PBUTN或者WBUTN按键0.5秒以上,则RGB-LED呼吸灯恢复。

实际参考的两个例子如下:
1. samples/RGB-LED
2. samples/drivers/butn

合并后的代码如下:
  1. /*
  2. * Copyright (c) 2021 HPMicro
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. *
  6. */
  7. #include <stdio.h>
  8. #include "board.h"
  9. #include "hpm_debug_console.h"
  10. #include "hpm_clock_drv.h"
  11. #include "hpm_pwm_drv.h"
  12. #include "hpm_butn_drv.h"
  13. #include "hpm_bpor_drv.h"
  14. #define TEST_BPOR HPM_BPOR
  15. #define TEST_BPOR_POWER_ON_CAUSE bpor_power_on_cause_wbutn
  16. #ifdef BOARD_RED_PWM
  17. #define RED_PWM_IRQ BOARD_RED_PWM_IRQ
  18. #define RED_PWM BOARD_RED_PWM
  19. #define RED_PWM_CMP BOARD_RED_PWM_CMP
  20. #define RED_PWM_CMP_INITIAL_ZERO BOARD_RED_PWM_CMP_INITIAL_ZERO
  21. #define RED_PWM_OUT BOARD_RED_PWM_OUT
  22. #define RED_PWM_CLOCK_NAME BOARD_RED_PWM_CLOCK_NAME
  23. #else
  24. #error BOARD_RED_PWM needs to be defined
  25. #endif
  26. #ifdef BOARD_GREEN_PWM
  27. #define GREEN_PWM_IRQ BOARD_GREEN_PWM_IRQ
  28. #define GREEN_PWM BOARD_GREEN_PWM
  29. #define GREEN_PWM_CMP BOARD_GREEN_PWM_CMP
  30. #define GREEN_PWM_CMP_INITIAL_ZERO BOARD_GREEN_PWM_CMP_INITIAL_ZERO
  31. #define GREEN_PWM_OUT BOARD_GREEN_PWM_OUT
  32. #define GREEN_PWM_CLOCK_NAME BOARD_BLUE_PWM_CLOCK_NAME
  33. #else
  34. #error BOARD_GREEN_PWM needs to be defined
  35. #endif
  36. #ifdef BOARD_BLUE_PWM
  37. #define BLUE_PWM_IRQ BOARD_BLUE_PWM_IRQ
  38. #define BLUE_PWM BOARD_BLUE_PWM
  39. #define BLUE_PWM_CMP BOARD_BLUE_PWM_CMP
  40. #define BLUE_PWM_CMP_INITIAL_ZERO BOARD_BLUE_PWM_CMP_INITIAL_ZERO
  41. #define BLUE_PWM_OUT BOARD_BLUE_PWM_OUT
  42. #define BLUE_PWM_CLOCK_NAME BOARD_BLUE_PWM_CLOCK_NAME
  43. #else
  44. #error BOARD_BLUE_PWM needs to be defined
  45. #endif
  46. #define PWM_PERIOD_IN_MS (1U)
  47. #define PWM_DUTY_STEP_COUNT (1000U)
  48. typedef struct {
  49.     PWM_Type *pwm;
  50.     uint32_t reload;
  51.     uint32_t step;
  52.     bool pwm_cmp_initial_zero;
  53.     uint8_t pwm_irq;
  54.     uint8_t pwm_cmp;
  55.     uint8_t pwm_ch;
  56. } led_pwm_t;
  57. typedef enum {
  58.     red = BOARD_RGB_RED,
  59.     green = BOARD_RGB_GREEN,
  60.     blue = BOARD_RGB_BLUE
  61. } led_index_t;
  62. led_pwm_t leds[3];
  63. uint32_t reload;
  64. volatile bool do_update;
  65. volatile led_index_t current;
  66. void pwm_isr(void)
  67. {
  68.     pwm_clear_status(leds[current].pwm, PWM_IRQ_HALF_RELOAD);
  69.     do_update = true;
  70. }
  71. SDK_DECLARE_EXT_ISR_M(RED_PWM_IRQ, pwm_isr)
  72. #if (GREEN_PWM_IRQ != RED_PWM_IRQ)
  73. SDK_DECLARE_EXT_ISR_M(GREEN_PWM_IRQ, pwm_isr)
  74. #endif
  75. #if (BLUE_PWM_IRQ != RED_PWM_IRQ) && (GREEN_PWM_IRQ != BLUE_PWM_IRQ)
  76. SDK_DECLARE_EXT_ISR_M(BLUE_PWM_IRQ, pwm_isr)
  77. #endif
  78. void config_pwm(PWM_Type * ptr, uint8_t pin, uint8_t cmp_index, uint32_t reload, bool cmp_initial_zero, uint8_t hw_event_cmp, bool off_level_high)
  79. {
  80.     pwm_cmp_config_t cmp_config = {0};
  81.     pwm_config_t pwm_config = {0};
  82.     pwm_stop_counter(ptr);
  83.     pwm_get_default_pwm_config(ptr, &pwm_config);
  84.     pwm_get_default_cmp_config(ptr, &cmp_config);
  85.     pwm_config.enable_output = false;
  86.     pwm_config.dead_zone_in_half_cycle = 0;
  87.     pwm_config.invert_output = !(cmp_initial_zero && off_level_high);
  88.     /*
  89.      * reload and start counter
  90.      */
  91.     pwm_set_reload(ptr, 0, reload);
  92.     pwm_set_start_count(ptr, 0, 0);
  93.     cmp_config.mode = pwm_cmp_mode_output_compare;
  94.     cmp_config.cmp = cmp_initial_zero ? 0 : reload + 1;
  95.     cmp_config.update_trigger = pwm_shadow_register_update_on_modify;
  96.     /* config initial compare value which should take affect immediately */
  97.     pwm_config_cmp(ptr, cmp_index, &cmp_config);
  98.     /* update trigger type so that compare value will be updated on hardware event (RELOAD) */
  99.     cmp_config.update_trigger = pwm_shadow_register_update_on_hw_event;
  100.     /*
  101.      * config pwm as output driven by cmp
  102.      */
  103.     if (status_success != pwm_setup_waveform(ptr, pin, &pwm_config, cmp_index, &cmp_config, 1)) {
  104.         printf("failed to setup waveform\n");
  105.         while(1);
  106.     }
  107.     /*
  108.      * config hw event
  109.      */
  110.     cmp_config.cmp = reload - 1;
  111.     cmp_config.update_trigger = pwm_shadow_register_update_on_hw_event;
  112.     pwm_load_cmp_shadow_on_match(ptr, hw_event_cmp, &cmp_config);
  113. }
  114. void turn_on_rgb_led(led_index_t i)
  115. {
  116.     PWM_Type *pwm;
  117.     uint8_t pwm_cmp;
  118.     uint32_t initial;
  119.     /* make sure cmp value is set to initial, before enabling output */
  120.     pwm = leds[i].pwm;
  121.     pwm_cmp = leds[i].pwm_cmp;
  122.     initial = leds[i].pwm_cmp_initial_zero ? 0 : (leds[i].reload + 1);
  123.     pwm_update_raw_cmp_edge_aligned(pwm, pwm_cmp, initial);
  124.     board_enable_output_rgb_led(i);
  125. }
  126. void turn_off_rgb_led(led_index_t i)
  127. {
  128.     board_disable_output_rgb_led(i);
  129. }
  130. void update_rgb_led(void)
  131. {
  132.     uint32_t duty;
  133.     bool increase_step = true;
  134.     PWM_Type *pwm;
  135.     uint32_t reload, pwm_cmp, step;
  136.     step = leds[current].step;
  137.     reload = leds[current].reload;
  138.     pwm = leds[current].pwm;
  139.     pwm_cmp = leds[current].pwm_cmp;
  140.     duty = step;
  141.     pwm_enable_irq(pwm, PWM_IRQ_HALF_RELOAD);
  142.     turn_on_rgb_led(current);
  143.     while(!(!increase_step && (duty < 2 * step))) {
  144.         if (increase_step && (duty + step > reload)) {
  145.             increase_step = false;
  146.         }
  147.         if (increase_step) {
  148.             duty += step;
  149.         } else {
  150.             duty -= step;
  151.         }
  152.         while (!do_update) {
  153.             ;
  154.         }
  155.         pwm_update_raw_cmp_edge_aligned(pwm, pwm_cmp, duty);
  156.         do_update = false;
  157.     }
  158.     turn_off_rgb_led(current);
  159.     pwm_disable_irq(pwm, PWM_IRQ_HALF_RELOAD);
  160. }
  161. int main(void){
  162.     uint32_t freq;
  163.     uint32_t hw_event_cmp;
  164.     board_init();
  165.     init_butn_pins();
  166.     board_init_rgb_pwm_pins();
  167.     printf("rgb led example\n");
  168.     freq = clock_get_frequency(RED_PWM_CLOCK_NAME);
  169.     leds[red].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  170.     leds[red].pwm = RED_PWM;
  171.     leds[red].pwm_ch = RED_PWM_OUT;
  172.     leds[red].pwm_cmp = RED_PWM_CMP;
  173.     leds[red].pwm_cmp_initial_zero = RED_PWM_CMP_INITIAL_ZERO;
  174.     leds[red].pwm_irq = RED_PWM_IRQ;
  175.    
  176.     freq = clock_get_frequency(GREEN_PWM_CLOCK_NAME);
  177.     leds[green].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  178.     leds[green].pwm = GREEN_PWM;
  179.     leds[green].pwm_ch = GREEN_PWM_OUT;
  180.     leds[green].pwm_cmp = GREEN_PWM_CMP;
  181.     leds[green].pwm_cmp_initial_zero = GREEN_PWM_CMP_INITIAL_ZERO;
  182.     leds[green].pwm_irq = GREEN_PWM_IRQ;
  183.     freq = clock_get_frequency(BLUE_PWM_CLOCK_NAME);
  184.     leds[blue].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  185.     leds[blue].pwm = BLUE_PWM;
  186.     leds[blue].pwm_ch = BLUE_PWM_OUT;
  187.     leds[blue].pwm_cmp = BLUE_PWM_CMP;
  188.     leds[blue].pwm_cmp_initial_zero = BLUE_PWM_CMP_INITIAL_ZERO;
  189.     leds[blue].pwm_irq = BLUE_PWM_IRQ;
  190.     hw_event_cmp = PWM_SOC_CMP_MAX_COUNT;
  191.     for (uint8_t i = 0; i < PWM_SOC_CMP_MAX_COUNT; i++) {
  192.         if ((i != RED_PWM_CMP) && (i != GREEN_PWM_CMP) && (i != BLUE_PWM_CMP)) {
  193.             continue;
  194.         }
  195.         hw_event_cmp = i;
  196.         break;
  197.     }
  198.     if (hw_event_cmp == PWM_SOC_CMP_MAX_COUNT) {
  199.         printf("Failed to find a comparator for hardware event\n");
  200.         while (1) {
  201.         };
  202.     }
  203.     for (uint8_t i = 0; i < ARRAY_SIZE(leds); i++) {
  204.         leds[i].step = leds[i].reload / PWM_DUTY_STEP_COUNT;
  205.         config_pwm(leds[i].pwm, leds[i].pwm_ch, leds[i].pwm_cmp, leds[i].reload, leds[i].pwm_cmp_initial_zero, hw_event_cmp, BOARD_LED_OFF_LEVEL);
  206.         pwm_start_counter(leds[i].pwm);
  207.         intc_m_enable_irq_with_priority(leds[i].pwm_irq, 1);
  208.     }
  209.     bpor_select_power_on_cause(TEST_BPOR, TEST_BPOR_POWER_ON_CAUSE);
  210.     printf("Please press PBUTN 16s to enter power down mode, then press PBUTN or WBUTN 0.5s to wake up from power down mode.\n");
  211.    
  212.     current = red;
  213.     while(1) {
  214.         update_rgb_led();
  215.         do_update = false;
  216.         current = (current + 1) % (blue + 1);
  217.     }
  218.     return 0;
  219. }

上述代码中,RGB-LED的呼吸灯模式,主要就是启用了PWM来进行控制,核心设置为:
  1. #define PWM_PERIOD_IN_MS (1U)
  2. #define PWM_DUTY_STEP_COUNT (1000U)
  3. freq = clock_get_frequency(RED_PWM_CLOCK_NAME);
  4.     leds[red].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  5.     leds[red].pwm = RED_PWM;
  6.     leds[red].pwm_ch = RED_PWM_OUT;
  7.     leds[red].pwm_cmp = RED_PWM_CMP;
  8.     leds[red].pwm_cmp_initial_zero = RED_PWM_CMP_INITIAL_ZERO;
  9.     leds[red].pwm_irq = RED_PWM_IRQ;
其中PWM脉宽计数终值为1000,周期为1秒,实际运行时,形成了很好的呼吸灯的效果。
然后在update_rgb_led()中,启用对应的pwm输出,从而控制对应的灯。

而掉电模式,则主要由如下部分配置和处理:
  1. #define TEST_BPOR HPM_BPOR
  2. #define TEST_BPOR_POWER_ON_CAUSE bpor_power_on_cause_wbutn
  3. bpor_select_power_on_cause(TEST_BPOR, TEST_BPOR_POWER_ON_CAUSE);

将以上代码编译烧录到开发板,然后使用数字功率计测试不同状态下的运行功耗。
1. 正常运行情况下:
image.png
此时的运行电压为4.8V,电流为220+-2ma

2. 掉电状态:
按PBUTN16秒以上,呼吸灯熄灭,进入掉电状态:
image.png
此时的运行电压为4.8V,电流为76+-1ma

3. 恢复正常运行:
按PBUTN或者WBUTN 0.5秒以上,系统恢复,呼吸灯重新开始闪烁
image.png
此时的运行电压为4.8V,电流为220+-2ma

具体的操作过程视频如下:


通过上面的测试步骤,可以清晰得到,掉电状态下,系统功耗约为正常运行状态下的1/3。
因为上述测试,仅为PWM电量RGB-LED,所以功耗比不是特别大。
如果是运行更多的外设,功耗更大的情况下,那么就会更加的明显。
后续会进一步测试,获得更多的数据。