经过初步了解,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

合并后的代码如下:
/*
  • * Copyright (c) 2021 HPMicro
  • *
  • * SPDX-License-Identifier: BSD-3-Clause
  • *
  • */
  • #include <stdio.h>
  • #include "board.h"
  • #include "hpm_debug_console.h"
  • #include "hpm_clock_drv.h"
  • #include "hpm_pwm_drv.h"
  • #include "hpm_butn_drv.h"
  • #include "hpm_bpor_drv.h"
  • #define TEST_BPOR HPM_BPOR
  • #define TEST_BPOR_POWER_ON_CAUSE bpor_power_on_cause_wbutn
  • #ifdef BOARD_RED_PWM
  • #define RED_PWM_IRQ BOARD_RED_PWM_IRQ
  • #define RED_PWM BOARD_RED_PWM
  • #define RED_PWM_CMP BOARD_RED_PWM_CMP
  • #define RED_PWM_CMP_INITIAL_ZERO BOARD_RED_PWM_CMP_INITIAL_ZERO
  • #define RED_PWM_OUT BOARD_RED_PWM_OUT
  • #define RED_PWM_CLOCK_NAME BOARD_RED_PWM_CLOCK_NAME
  • #else
  • #error BOARD_RED_PWM needs to be defined
  • #endif
  • #ifdef BOARD_GREEN_PWM
  • #define GREEN_PWM_IRQ BOARD_GREEN_PWM_IRQ
  • #define GREEN_PWM BOARD_GREEN_PWM
  • #define GREEN_PWM_CMP BOARD_GREEN_PWM_CMP
  • #define GREEN_PWM_CMP_INITIAL_ZERO BOARD_GREEN_PWM_CMP_INITIAL_ZERO
  • #define GREEN_PWM_OUT BOARD_GREEN_PWM_OUT
  • #define GREEN_PWM_CLOCK_NAME BOARD_BLUE_PWM_CLOCK_NAME
  • #else
  • #error BOARD_GREEN_PWM needs to be defined
  • #endif
  • #ifdef BOARD_BLUE_PWM
  • #define BLUE_PWM_IRQ BOARD_BLUE_PWM_IRQ
  • #define BLUE_PWM BOARD_BLUE_PWM
  • #define BLUE_PWM_CMP BOARD_BLUE_PWM_CMP
  • #define BLUE_PWM_CMP_INITIAL_ZERO BOARD_BLUE_PWM_CMP_INITIAL_ZERO
  • #define BLUE_PWM_OUT BOARD_BLUE_PWM_OUT
  • #define BLUE_PWM_CLOCK_NAME BOARD_BLUE_PWM_CLOCK_NAME
  • #else
  • #error BOARD_BLUE_PWM needs to be defined
  • #endif
  • #define PWM_PERIOD_IN_MS (1U)
  • #define PWM_DUTY_STEP_COUNT (1000U)
  • typedef struct {
  •     PWM_Type *pwm;
  •     uint32_t reload;
  •     uint32_t step;
  •     bool pwm_cmp_initial_zero;
  •     uint8_t pwm_irq;
  •     uint8_t pwm_cmp;
  •     uint8_t pwm_ch;
  • } led_pwm_t;
  • typedef enum {
  •     red = BOARD_RGB_RED,
  •     green = BOARD_RGB_GREEN,
  •     blue = BOARD_RGB_BLUE
  • } led_index_t;
  • led_pwm_t leds[3];
  • uint32_t reload;
  • volatile bool do_update;
  • volatile led_index_t current;
  • void pwm_isr(void)
  • {
  •     pwm_clear_status(leds[current].pwm, PWM_IRQ_HALF_RELOAD);
  •     do_update = true;
  • }
  • SDK_DECLARE_EXT_ISR_M(RED_PWM_IRQ, pwm_isr)
  • #if (GREEN_PWM_IRQ != RED_PWM_IRQ)
  • SDK_DECLARE_EXT_ISR_M(GREEN_PWM_IRQ, pwm_isr)
  • #endif
  • #if (BLUE_PWM_IRQ != RED_PWM_IRQ) && (GREEN_PWM_IRQ != BLUE_PWM_IRQ)
  • SDK_DECLARE_EXT_ISR_M(BLUE_PWM_IRQ, pwm_isr)
  • #endif
  • 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)
  • {
  •     pwm_cmp_config_t cmp_config = {0};
  •     pwm_config_t pwm_config = {0};
  •     pwm_stop_counter(ptr);
  •     pwm_get_default_pwm_config(ptr, &pwm_config);
  •     pwm_get_default_cmp_config(ptr, &cmp_config);
  •     pwm_config.enable_output = false;
  •     pwm_config.dead_zone_in_half_cycle = 0;
  •     pwm_config.invert_output = !(cmp_initial_zero && off_level_high);
  •     /*
  •      * reload and start counter
  •      */
  •     pwm_set_reload(ptr, 0, reload);
  •     pwm_set_start_count(ptr, 0, 0);
  •     cmp_config.mode = pwm_cmp_mode_output_compare;
  •     cmp_config.cmp = cmp_initial_zero ? 0 : reload + 1;
  •     cmp_config.update_trigger = pwm_shadow_register_update_on_modify;
  •     /* config initial compare value which should take affect immediately */
  •     pwm_config_cmp(ptr, cmp_index, &cmp_config);
  •     /* update trigger type so that compare value will be updated on hardware event (RELOAD) */
  •     cmp_config.update_trigger = pwm_shadow_register_update_on_hw_event;
  •     /*
  •      * config pwm as output driven by cmp
  •      */
  •     if (status_success != pwm_setup_waveform(ptr, pin, &pwm_config, cmp_index, &cmp_config, 1)) {
  •         printf("failed to setup waveform\n");
  •         while(1);
  •     }
  •     /*
  •      * config hw event
  •      */
  •     cmp_config.cmp = reload - 1;
  •     cmp_config.update_trigger = pwm_shadow_register_update_on_hw_event;
  •     pwm_load_cmp_shadow_on_match(ptr, hw_event_cmp, &cmp_config);
  • }
  • void turn_on_rgb_led(led_index_t i)
  • {
  •     PWM_Type *pwm;
  •     uint8_t pwm_cmp;
  •     uint32_t initial;
  •     /* make sure cmp value is set to initial, before enabling output */
  •     pwm = leds[i].pwm;
  •     pwm_cmp = leds[i].pwm_cmp;
  •     initial = leds[i].pwm_cmp_initial_zero ? 0 : (leds[i].reload + 1);
  •     pwm_update_raw_cmp_edge_aligned(pwm, pwm_cmp, initial);
  •     board_enable_output_rgb_led(i);
  • }
  • void turn_off_rgb_led(led_index_t i)
  • {
  •     board_disable_output_rgb_led(i);
  • }
  • void update_rgb_led(void)
  • {
  •     uint32_t duty;
  •     bool increase_step = true;
  •     PWM_Type *pwm;
  •     uint32_t reload, pwm_cmp, step;
  •     step = leds[current].step;
  •     reload = leds[current].reload;
  •     pwm = leds[current].pwm;
  •     pwm_cmp = leds[current].pwm_cmp;
  •     duty = step;
  •     pwm_enable_irq(pwm, PWM_IRQ_HALF_RELOAD);
  •     turn_on_rgb_led(current);
  •     while(!(!increase_step && (duty < 2 * step))) {
  •         if (increase_step && (duty + step > reload)) {
  •             increase_step = false;
  •         }
  •         if (increase_step) {
  •             duty += step;
  •         } else {
  •             duty -= step;
  •         }
  •         while (!do_update) {
  •             ;
  •         }
  •         pwm_update_raw_cmp_edge_aligned(pwm, pwm_cmp, duty);
  •         do_update = false;
  •     }
  •     turn_off_rgb_led(current);
  •     pwm_disable_irq(pwm, PWM_IRQ_HALF_RELOAD);
  • }
  • int main(void){
  •     uint32_t freq;
  •     uint32_t hw_event_cmp;
  •     board_init();
  •     init_butn_pins();
  •     board_init_rgb_pwm_pins();
  •     printf("rgb led example\n");
  •     freq = clock_get_frequency(RED_PWM_CLOCK_NAME);
  •     leds[red].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  •     leds[red].pwm = RED_PWM;
  •     leds[red].pwm_ch = RED_PWM_OUT;
  •     leds[red].pwm_cmp = RED_PWM_CMP;
  •     leds[red].pwm_cmp_initial_zero = RED_PWM_CMP_INITIAL_ZERO;
  •     leds[red].pwm_irq = RED_PWM_IRQ;
  •    
  •     freq = clock_get_frequency(GREEN_PWM_CLOCK_NAME);
  •     leds[green].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  •     leds[green].pwm = GREEN_PWM;
  •     leds[green].pwm_ch = GREEN_PWM_OUT;
  •     leds[green].pwm_cmp = GREEN_PWM_CMP;
  •     leds[green].pwm_cmp_initial_zero = GREEN_PWM_CMP_INITIAL_ZERO;
  •     leds[green].pwm_irq = GREEN_PWM_IRQ;
  •     freq = clock_get_frequency(BLUE_PWM_CLOCK_NAME);
  •     leds[blue].reload = freq / 1000 * PWM_PERIOD_IN_MS - 1;
  •     leds[blue].pwm = BLUE_PWM;
  •     leds[blue].pwm_ch = BLUE_PWM_OUT;
  •     leds[blue].pwm_cmp = BLUE_PWM_CMP;
  •     leds[blue].pwm_cmp_initial_zero = BLUE_PWM_CMP_INITIAL_ZERO;
  •     leds[blue].pwm_irq = BLUE_PWM_IRQ;
  •     hw_event_cmp = PWM_SOC_CMP_MAX_COUNT;
  •     for (uint8_t i = 0; i < PWM_SOC_CMP_MAX_COUNT; i++) {
  •         if ((i != RED_PWM_CMP) && (i != GREEN_PWM_CMP) && (i != BLUE_PWM_CMP)) {
  •             continue;
  •         }
  •         hw_event_cmp = i;
  •         break;
  •     }
  •     if (hw_event_cmp == PWM_SOC_CMP_MAX_COUNT) {
  •         printf("Failed to find a comparator for hardware event\n");
  •         while (1) {
  •         };
  •     }
  •     for (uint8_t i = 0; i < ARRAY_SIZE(leds); i++) {
  •         leds[i].step = leds[i].reload / PWM_DUTY_STEP_COUNT;
  •         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);
  •         pwm_start_counter(leds[i].pwm);
  •         intc_m_enable_irq_with_priority(leds[i].pwm_irq, 1);
  •     }
  •     bpor_select_power_on_cause(TEST_BPOR, TEST_BPOR_POWER_ON_CAUSE);
  •     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");
  •    
  •     current = red;
  •     while(1) {
  •         update_rgb_led();
  •         do_update = false;
  •         current = (current + 1) % (blue + 1);
  •     }
  •     return 0;
  • }
  • 复制代码

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

    而掉电模式,则主要由如下部分配置和处理:
    #define TEST_BPOR HPM_BPOR
  • #define TEST_BPOR_POWER_ON_CAUSE bpor_power_on_cause_wbutn
  • 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,所以功耗比不是特别大。
    如果是运行更多的外设,功耗更大的情况下,那么就会更加的明显。
    后续会进一步测试,获得更多的数据。