0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

GD32开发实战指南(基础篇) 第15章 低功耗

嵌入式大杂烩 来源:嵌入式大杂烩 作者:嵌入式大杂烩 2023-05-17 08:59 次阅读

开发环境:

MDK:Keil 5.30

开发板:GD32F207I-EVAL

MCU:GD32F207IK

1 GD32电源管理

GD32的工作电压(VDD)为2.0~3.6V。通过内置的电压调节器提供所需的1.8V电源。当主电源VDD掉电后,通过VBAT脚为实时时钟(RTC)和备份寄存器供电源。

1684245485489k9nyiyuks9

使用电池或其他电源连接到VBAT脚上,当VDD断电时,可以保存备份寄存器的内容和维持RTC的功能。

VBAT脚为RTC、 LSE振荡器和PC13至PC15端口供电,可以保证当主电源被切断时RTC能继续工作。切换到VBAT供电的开关,由复位模块中的掉电复位功能控制。

当备份域由VDD供电(VBAK连接至VDD)时,以下功能可用:

● PC13可以作为通用I/O口或RTC功能引脚;

● PC14和PC15可以作为通用I/O口或LXTAL晶振引脚。

PI8可以作为通用I/O口或RTC功能引脚

当备份域由VBAT电源供电时(VBAK连接至VBAT),以下功能可用:

● PC13仅可以作为RTC功能引脚;

● PC14和PC15仅可作为LXTAL晶振引脚。

● PI8仅可以作为RTC功能引脚

注意:由于PC13至PC15引脚是通过电源切换器供电的,电源切换器仅可通过小电流,因此当PC13至PC15的GPIO口在输出模式时,其工作的速度不能超过2MHz(最大负载为30pF)。

2 GD32低功耗模式

在系统或电源复位以后,微控制器处于运行状态。当CPU不需继续运行时,可以利用多种低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗、最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。

GD32F207有三种低功耗模式:

  • 睡眠模式(Cortex™-M3内核停止,所有外设包括Cortex-M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行)

● 深度睡眠/停止模式(所有的时钟都已停止)

● 待机模式(1.8V电源关闭)

此外,在运行模式下,可以通过以下方式中的一种降低功耗:

● 降低系统时钟

● 关闭APB和AHB总线上未被使用的外设时钟。

1684245486046vja3pvlwnn

从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。

在睡眠模式中,仅关闭了 CPU 时钟, CPU 停止运行 ,但其片上外设,CM3 核心外设全都还照常运行。有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt)和 WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。

在深度睡眠模式中, 进一步关闭了其它所有的时钟 ,于是所有的外设都停止了工作,但由于其 1.8V 区域的电源没有关闭,还保留了 CPU 的寄存器、内存的信息,所以深度睡眠模式唤醒,并重新开启时钟后,还可以从上次深度睡眠处继续执行代码。深度睡眠模式可以由任意一个外部中断(EXTI)唤醒。在深度睡眠模式中可以选择电压调节器为开模式或低功耗模式,若选择低功耗模式,在唤醒时会加上电压调节器的唤醒延迟。

待机模式,这与我们平时印象中的手机关机模式相似,它除了关闭所有的时钟,还把1.8V 区域的电源也关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。它有四种唤醒方式,分别是 WKUP(PA0)引脚的上升沿,RTC 闹钟事件,NRST 引脚的复位和FWDGT复位。

在运行模式下,任何时候都可以通过停止为外设和内存提供时钟来减少功耗。为了在睡眠模式下更多地减少功耗,可在执行WFI或WFE指令前关闭所有外设的时钟。

通过设置AHB外设时钟使能寄存器、APB2外设时钟使能寄存器和APB1外设时钟使能寄存器来开关各个外设模块的时钟。

2.1睡眠模式

  • 进入睡眠模式

通过执行WFI或WFE指令进入睡眠状态。根据Cortex™-M3中SCR(系统控制寄存器)的SLEEPONEXIT位,有两种睡眠进入机制选项:

● Sleep-now:如果SLEEPONEXIT位被清零,一旦执行WFI或WFE指令, MCU立即进入睡眠模式;

● Sleep-on-exit:如果SLEEPONEXIT位被置位,当系统从最低优先级的中断处理程序离开后, MCU立即进入睡眠模式。

在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态。

  • 退出睡眠模式

如果执行WFI指令进入睡眠模式,任意一个被嵌套向量中断控制器响应的外设中断都能将系统从睡眠模式唤醒。

如果执行WFE指令进入睡眠模式,则一旦发生唤醒事件时,微处理器都将从睡眠模式退出。唤醒事件可以通过下述方式产生:

● 在外设控制寄存器中使能一个中断,而不是在NVIC(嵌套向量中断控制器)中使能,并且在Cortex-M3系统控制寄存器中使能SEVONPEND位。当MCU从WFE中唤醒后,外设的中断挂起位和外设的NVIC中断通道挂起位(在NVIC中断清除挂起寄存器中)必须被清除。

● 配置一个外部或内部的EXIT线为事件模式。当MCU从WFE中唤醒后,因为与事件线对应的挂起位未被设置,不必清除外设的中断挂起位或外设的NVIC中断通道挂起位。

该模式唤醒所需的时间最短,因为没有时间损失在中断的进入或退出上。

SLEEP-NOW****模式 说明
进入 在以下条件下执行WFI(等待中断)或WFE(等待事件)指令: –SLEEPDEEP = 0和–SLEEPONEXIT = 0参考Cortex-M3系统控制寄存器。
退出 如果执行WFI进入睡眠模式:中断。 如果执行WFE进入睡眠模式:唤醒事件。
唤醒延时
SLEEP-ON_EXIT****模式 说明
进入 在以下条件下执行WFI指令:–SLEEPDEEP = 0和–SLEEPONEXIT = 1参考Cortex™-M3系统控制寄存器
退出 中断:参考中断向量表
唤醒延时

2.2 深度睡眠模式

深度睡眠模式是在Cortex™-M3的深睡眠模式基础上结合了外设的时钟控制机制,深度睡眠模式与 Cortex™-M3 的 SLEEPDEEP 模式相对应。在深度睡眠模式下,1.2V 域中的所有时钟全部关闭,IRC8M、HXTAL及PLLs 也全部被禁用。SRAM和寄存器中的内容被保留。

在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。

  • 进入深度睡眠模式

在深度睡眠模式下,通过设置电源控制寄存器PMU_CTL的LDOLP位使内部调节器进入低功耗模式,能够降低更多的功耗。

如果正在进行闪存编程,直到对内存访问完成,系统才进入深度睡眠模式。

如果正在进行对APB的访问,直到对APB访问完成,系统才进入深度睡眠模式。可以通过对独立的控制位进行编程。

在深度睡眠模式下,如果在进入该模式前ADCDAC没有被关闭,那么这些外设仍然消耗电流。

  • 退出深度睡眠模式

当一个中断或唤醒事件导致退出深度睡眠模式时,IRC8M、IRC40K振荡器被选为系统时钟。

当电压调节器处于低功耗模式下,当系统从深度睡眠模式退出时,将会有一段额外的启动延时。如果在深度睡眠模式期间保持内部调节器开启,则退出启动时间会缩短,但相应的功耗会增加。

深度睡眠模式 说明
进入 在以下条件下执行WFI(等待中断)或WFE(等待事件)指令: –设置Cortex-M3系统控制寄存器中的SLEEPDEEP位 –清除电源控制寄存器(PMU_CTL)中的STBMOD位 –通过设置PMU_CTL中LDOLP位选择电压调节器的模式 注:为了进入停止模式,所有的外部中断的请求位(挂起寄存器)和RTC的闹钟标志都必须被清除,否则停止模式的进入流程将会被跳过,程序继续运行。
退出 如果执行WFI进入停止模式: 设置任一外部中断线为中断模式(在NVIC中必须使能相应的外部中断向量)。参见中断向量。 如果执行WFE进入停止模式: 设置任一外部中断线为事件模式。参见唤醒事件管理。
唤醒延时 IRC8M、IRC40K唤醒时间+电压调节器从低功耗唤醒的时间。

2.3待机模式

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

  • 进入待机模式

进入待机模式前,先将Cortex™-M3 系统控制寄存器的 SLEEPDEEP 位置 1,再将 PMU_CTL 寄存器的 STBMOD 位置 1,再清除 PMU_CS 寄存器的 WUF 位,然后执行 WFI 或 WFE 指令,系统进入待机模式,PMU_CS 寄存器的 STBF 位状态表示 MCU 是否已进入待机模式。

  • 退出待机模式

当一个外部复位(NRST引脚)、 FWDGT复位、 WKUP引脚上的上升沿或RTC闹钟事件的上升沿发生时,微控制器从待机模式退出。从待机唤醒后,除了电源控制/状态寄存器(PWR_CS),所有寄存器被复位。

从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚、读取复位向量等)。 电源控制/状态寄存器(PWR_CS)将会指示内核由待机状态退出。

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

待机模式 说明
进入 在以下条件下执行WFI(等待中断)或WFE(等待事件)指令: –设置Cortex™-M3系统控制寄存器中的SLEEPDEEP位 –设置电源控制寄存器(PWR_CTL)中的STBMOD位 –清除电源控制/状态寄存器(PWR_CS)中的WUF位
退出 WKUP引脚的上升沿、RTC闹钟事件的上升沿、NRST引脚上外部复位、FWDGT复位。
唤醒延时 复位阶段时电压调节器的启动

3 低功耗的寄存器描述

电源控制寄存器(PWR_CTL),该寄存器的各位描述如下图所示:

1684245486565uey0p7r2pq

我们通过设置 PWR_CTL的 STBMOD位,使 CPU 进入深度睡眠时进入待机模式。

电源控制/状态寄存器( PWR_CS)的各位描述如图所示。

1684245486948ys3dsyd2c7

通过设置 PWR_CS的 WUPEN 位,来使能 WKUP 引脚用于待机模式唤醒。我们还可以从 WUF 来检查是否发生了唤醒事件。

4 低功耗具体代码实现

通过以上介绍,我们了解了进入低功耗模式的三种方法,在者三种模式中待机模式功耗最低。笔者这里使用的是按键唤醒,其电路如下:

1684245487308dwt16x8vn9

4.1睡眠模式

睡眠模式很简单,就是通过以下指令进入睡眠:

__WFI();	
__WFE();

那么以上两条指令又是啥意思呢?WFI(Wait for interrupt)和WFE(Wait for event)是两个让ARM核进入low-power standby模式的指令,由ARM architecture定义,由ARM core实现。我们可以在core_cmx3.h(笔者使用的是GD32F207,对应的就是core_cmx3.h,其他内核类似),中找到以上指令的定义。

static __INLINE void __WFI()                      { __ASM volatile ("wfi"); }
static __INLINE void __WFE()                      { __ASM volatile ("wfe"); }

以上就是把汇编指令都封装成了诸如__Commnad()的函数形式,并且预编译为二进制包。那么以上指令都能让ARM进入睡眠模式,又有啥区别呢?

对WFI来说,执行WFI指令后,ARM core会立即进入low-power standby state,直到有WFI Wakeup events发生。

而WFE则稍微不同,执行WFE指令后,根据Event Register(一个单bit的寄存器,每个PE一个)的状态,有两种情况:如果Event Register为1,该指令会把它清零,然后执行完成(不会standby);如果Event Register为0,和WFI类似,进入low-power standby state,直到有WFE Wakeup events发生。

总结一下,这两条指令的作用都是令MCU进入休眠/待机状态以便降低功耗,但是略有区别:

WFI: wait for Interrupt 等待中断,即下一次中断发生前都在此hold住不干活

WFE: wait for Events 等待事件,即下一次事件发生前都在此hold住不干活

因此我们要项唤醒MCU,最简单的就是通过中断唤醒。

睡眠模式时通过按键中断唤醒,代码如下:

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    //usart init 115200 8-N-1
    com_init(COM1, USART_MODE_GPIO, 115200, 0, 1);

    /* configure LED1 GPIO port */
    led_init(LED1);

    /* configure LED2 GPIO port */
    led_init(LED2);

    /* configure LED3 GPIO port */
    led_init(LED3);

    /* configure LED4 GPIO port */
    led_init(LED4);

    //key init
    key_init(KEY_WAKEUP, KEY_MODE_EXTI);

    printf("\\r\\n Sleep Test \\r\\n");

    while(1)
    {
        led_toggle(LED1);
        Delay(0xFFFFF);
        led_toggle(LED2);
        Delay(0xFFFFF);
        led_toggle(LED3);
        Delay(0xFFFFF);
        led_toggle(LED4);
        //__WFI(); //进入睡眠模式,等待中断唤醒  方式一
        __WFE(); //方式二
    }
}

进入睡眠模式也可调用库函数。

void pmu_to_sleepmode(uint8_t sleepmodecmd)

值得注意的是,这里不能用滴答定时器来延时,因为这里使用的是中断方式实现的。

WFI和WFE两条指令让MCU进入睡眠模式,均可通过按键中断唤醒,但是他们的唤醒本质是有区别的。

https://images2017.cnblogs.com/blog/1138116/201708/1138116-20170815083132084-771850603.png

如上图所示,图中的蓝色虚线箭头标出了中断信号的传输路径,而红色箭头标出了事件的传输路径。虽然中断和事件的产生源都是一样的,都是通过按键产生,但是路径却有不同,中断是需要CPU参与的,需要软件的中断服务函数才能完成中断后产生的结果;事件是靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,比如引起DMA操作,AD转换等。

4.2 深度睡眠模式

进入深度睡眠模式之后,任何外部中断都可以唤醒低功耗,但是需要重新配置时钟,不然系统将以默认时钟(没有经过倍频)运行。笔者这里还是使用外部中断唤醒。我们先看看主函数。

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    //usart init 115200 8-N-1
com_init(COM1, USART_MODE_GPIO, 115200, 0, 1);

    /* configure LED1 GPIO port */
    led_init(LED1);

    /* configure LED2 GPIO port */
    led_init(LED2);

    /* configure LED3 GPIO port */
    led_init(LED3);

    /* configure LED4 GPIO port */
    led_init(LED4);

    //key init
    key_init(KEY_WAKEUP, KEY_MODE_EXTI);

    /* 使能电源管理单元的时钟 */
rcu_periph_clock_enable(RCU_PMU);

    printf("\\r\\n Enter stop mode \\r\\n");

    /* 进入深度睡眠模式,设置电压调节器为低功耗模式,等待中断唤醒*/
    pmu_to_deepsleepmode(PMU_LDO_LOWPOWER,WFI_CMD);	

    while(1)
    {
        led_toggle(LED1);
        Delay(0xFFFFF);
        led_toggle(LED2);
        Delay(0xFFFFF);
        led_toggle(LED3);
        Delay(0xFFFFF);
        led_toggle(LED4);
    }
}

以上最重要的就一句:

pmu_to_deepsleepmode(PMU_LDO_LOWPOWER,WFI_CMD);

这是MCU进入低功耗模式的库函数,在gd32f20x_pmu.c中实现,原型如下:

/*!
    \\brief      PMU work in deepsleep mode
    \\param[in]  ldo:
                only one parameter can be selected which is shown as below:
      \\arg        PMU_LDO_NORMAL: LDO work at normal power mode when pmu enter deepsleep mode
      \\arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode
    \\param[in]  deepsleepmodecmd:
                only one parameter can be selected which is shown as below:
      \\arg        WFI_CMD: use WFI command
      \\arg        WFE_CMD: use WFE command
    \\param[out] none
    \\retval     none
*/
void pmu_to_deepsleepmode(uint32_t ldo, uint8_t deepsleepmodecmd)
{
    static uint32_t reg_snap[4];
    /* clear stbmod and ldolp bits */
    PMU_CTL &= ~((uint32_t)(PMU_CTL_STBMOD | PMU_CTL_LDOLP));

    /* set ldolp bit according to pmu_ldo */
    PMU_CTL |= ldo;

    /* set sleepdeep bit of Cortex-M3 system control register */
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

    reg_snap[0] = REG32(0xE000E010U);
    reg_snap[1] = REG32(0xE000E100U);
    reg_snap[2] = REG32(0xE000E104U);
    reg_snap[3] = REG32(0xE000E108U);

    REG32(0xE000E010U) &= 0x00010004U;
    REG32(0xE000E180U)  = 0XFF7FF83DU;
    REG32(0xE000E184U)  = 0XBFFFF8FFU;
    REG32(0xE000E188U)  = 0xFFFFFFFFU;

    /* select WFI or WFE command to enter deepsleep mode */
    if(WFI_CMD == deepsleepmodecmd) {
        __WFI();
    } else {
        __SEV();
        __WFE();
        __WFE();
    }

    REG32(0xE000E010U) = reg_snap[0] ;
    REG32(0xE000E100U) = reg_snap[1] ;
    REG32(0xE000E104U) = reg_snap[2] ;
    REG32(0xE000E108U) = reg_snap[3] ;

    /* reset sleepdeep bit of Cortex-M3 system control register */
    SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
}

上述函数主要节做了四件事:

1.设置Cortex-M3系统控制寄存器中的SLEEPDEEP位(SCB_SCR参考Cortex-M3权威指南182页)。

2.清除电源控制寄存器(PWR_CTL)中的STBMOD位。

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

4.执行WFI或者WFE汇编指令。

我们可以选择事件和中断唤醒两种方式,选择哪种方式是根据库函数的第二个参数决定的,笔者这里使用的中断唤醒。

16842454884212pavkoprct

深度睡眠模式下MCU唤醒之后,时钟和频率是没有经过倍频的,在GD32F207上,低功耗唤醒之后,是8M频率运行,而正常运行是120M。所以,在唤醒停机模式之后,需要重新配置时钟。最简单就是直接调用库函数:

SystemInit();

当然也可以自己重写时钟初始化函数。

好了,我们最后再看看中断唤醒的函数:

/*!
    \\brief      this function handles external lines 0 interrupt request
    \\param[in]  none
    \\param[out] none
    \\retval     none
*/
void EXTI0_IRQHandler(void)
{
    if(RESET != exti_interrupt_flag_get(EXTI_0))
    {
       printf("\\r\\n Enter interrupt \\r\\n");	

       SystemInit();

       printf("\\r\\n Quit interrupt \\r\\n");
       exti_interrupt_flag_clear(EXTI_0);
    }
}

进入中断后主要是重启时钟,然后就会接着运行程序。

4.3 待机模式

待机模式的功耗最低。待机模式的具体步骤如下:

1) 使能电源时钟。

因为要配置电源控制寄存器,所以必须先使能电源时钟。在库函数中,使能电源时钟的方法是:

rcu_periph_clock_enable(RCU_PMU); //使能 PWR 外设时钟
  1. 设置 WK_UP 引脚作为唤醒源。

使能时钟之后再设置 PWR_CS 的WUPEN位,使能WUPEN用于将 CPU 从待机模式唤醒。在库函数中,设置使能WUPEN用于唤醒 CPU 待机模式的函数是:

pmu_wakeup_pin_enable (); //使能唤醒管脚功能

3) 设置 SLEEPDEEP 位,设置 STBMOD位,执行 WFI 指令,进入待机模式。

进入待机模式, 首先要设置 SLEEPDEEP 位( 该位在系统控制寄存器( SCB_SCR)的第二位,详见《 CM3 权威指南》), 接着我们通过 PWR_CTL设置 STBMOD位,使得 CPU 进入深度睡眠时进入待机模式,最后执行 WFI 指令开始进入待机模式,并等待 WK_UP中断的到来。在库函数中,进行上面三个功能进入待机模式是在函数pmu_to_standbymode中实现的:

void pmu_to_standbymode(uint8_t standbymodecmd);

pmu_to_standbymode ()函数原型如下所示:

/*!
    \\brief      pmu work in standby mode
    \\param[in]  standbymodecmd:
                only one parameter can be selected which is shown as below:
      \\arg        WFI_CMD: use WFI command
      \\arg        WFE_CMD: use WFE command
    \\param[out] none
    \\retval     none
*/
void pmu_to_standbymode(uint8_t standbymodecmd)
{
    /* set sleepdeep bit of Cortex-M3 system control register */
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

    /* set stbmod bit */
    PMU_CTL |= PMU_CTL_STBMOD;

    /* reset wakeup flag */
    PMU_CTL |= PMU_CTL_WURST;

    /* select WFI or WFE command to enter standby mode */
    if(WFI_CMD == standbymodecmd) {
        __WFI();
    } else {
        __WFE();
    }
}

该函数中先配置了STBMOD寄存器位及 SLEEPDEEP 寄存器位,接着调用__force_stores函数确保存储操作完毕后再调用 WFI 指令,从而进入待机模式。这里值得注意的是,待机模式也可以使用 WFE 指令进入的。

在进入待机模式后,除了被使能了的用于唤醒的 I/O,其余 I/O 都进入高阻态,而待机模式唤醒后,相当于复位 GD32 芯片,程序重新从头开始执行。

4) 最后编写 WK_UP 中断/事件函数。

因为我们通过 WK_UP 中断/事件( PA0 中断/事件)来唤醒MCU,所以我们有必要设置一下该中断函数,同时我们也通过该函数里面进入待机模式。

/**
  * @brief  用于检测按键是否被长时间按下
  * @param  无
  * @retval 1 :按键被长时间按下  0 :按键没有被长时间按下
  */
uint8_t pwr_check_standby(void)
{
    uint8_t downCnt = 0;    //记录按下的次数
    uint8_t upCnt = 0; //记录松开的次数

    while(1) //死循环,由return结束
    {
        if(RESET == gpio_input_bit_get(GPIOA, GPIO_PIN_0))//检测到按下按键
        {
            led_on(LED1);led_on(LED2);led_on(LED3);led_on(LED4); //点亮所有LED灯

            downCnt++; //记录按下次数
            upCnt=0; //清除按键释放记录
            Delay(0xFFFF);
            if(downCnt>=100) //按下时间足够
            {
                led_off(LED1);led_off(LED2);led_off(LED3);led_off(LED4);
                return 1;  //检测到按键被时间长按下
            }
        }
        else 
        {
            upCnt++;  //记录释放次数
            if(upCnt>5)	 //连续检测到释放超过5次
            {
                led_off(LED1);led_off(LED2);led_off(LED3);led_off(LED4); //关闭所有LED灯
                return 0; //按下时间太短,不是按键长按操作
            }
        }
    }
}

通过以上几个步骤的设置,我们就可以使用 GD32 的待机模式了,并且可以通过 WK_UP来唤醒 MCU。

主函数如下:

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    //usart init 115200 8-N-1
    com_init(COM1, USART_MODE_GPIO, 115200, 0, 1);

    /* configure LED1 GPIO port */
    led_init(LED1);

    /* configure LED2 GPIO port */
    led_init(LED2);

    /* configure LED3 GPIO port */
    led_init(LED3);

    /* configure LED4 GPIO port */
    led_init(LED4);

    //key init
    key_init(KEY_WAKEUP, KEY_MODE_GPIO);

    /* 使能电源管理单元的时钟 */
    rcu_periph_clock_enable(RCU_PMU);

    if(pmu_flag_get(PMU_FLAG_WAKEUP) == SET)
    {
        printf("\\r\\n Standby wake-up reset \\r\\n");

    }
    else
    {
        printf("\\r\\n Power-on reset\\r\\n");
    }
    while(1)
    {
        led_toggle(LED1);
        Delay(0xFFFFF);
        led_toggle(LED2);
        Delay(0xFFFFF);
        led_toggle(LED3);
        Delay(0xFFFFF);
        led_toggle(LED4);
        if(pwr_check_standby())
        {
            printf("\\r\\n Enter standby mode\\r\\n");
            /*清除 WU 状态位*/
            pmu_flag_clear (PMU_FLAG_RESET_WAKEUP);
            /* 使能WKUP引脚的唤醒功能 */
            pmu_wakeup_pin_enable ();
            /* 进入待机模式,等待唤醒*/
            pmu_to_standbymode(WFI_CMD);
        }
    }
}

在循环体外使用库函数 pmu_flag_get检测 PMU_FLAG_WAKEUP标志位,当这个标志位为SET 状态的时候,表示本次系统是从待机模式唤醒的复位,否则可能是上电复位。其中PMU_FLAG_WAKEUP的标志位在gd32f20x_pmu.h中定义。

pwr_check_standby()函数用于检测渐渐是否长按,当长按后就进入待机模式。

5 低功耗实验现象

5.1睡眠模式

将程序编译好后下载到板子上,可以看到4个LED依次闪烁,然后熄灭。按下KEY1按键,3个LED再次依次闪烁。

16842454887759lwj6pv9ge

图6

5.2 深度睡眠模式

将程序编译好后下载到板子上。按下KEY1按键,4个LED依次闪烁。串口依次打印信息如下:

1684245489217sm2lujv77e

当程序运行后会首先深度睡眠模式,当按键按下后,退出深度睡眠模式,则程序又会继续运行。

5.3待机模式

将程序编译好后下载到板子上。按下按键,4个LED依次闪烁。长按KEY1按键,MCU进入待机模式,LED熄灭,再次按下KEY1按键,LED会同时点亮,直到同时熄灭,松开KEY1按键,KEY1按下会使 PA0 引脚产生一个上升沿,从而唤醒系统。

系统唤醒后会进行复位,从头开始执行上述过程,与第一次上电时不同的是,这样的复位会使 PWR_FLAG_WU 标志位改为 SET 状态,LED再次依次闪烁。串口打印信息如下。

168424548971232dzb5akrg

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    17123

    浏览量

    350992
  • 电源管理
    +关注

    关注

    115

    文章

    6177

    浏览量

    144447
  • 低功耗
    +关注

    关注

    10

    文章

    2396

    浏览量

    103675
  • 开发板
    +关注

    关注

    25

    文章

    5032

    浏览量

    97375
  • GD32
    +关注

    关注

    7

    文章

    403

    浏览量

    24328
收藏 人收藏

    评论

    相关推荐

    GD32开发实战指南(基础) 1 开发环境搭建

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 GD32F207I-EVAL
    的头像 发表于 05-07 23:35 1.1w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>1<b class='flag-5'>章</b> <b class='flag-5'>开发</b>环境搭建

    GD32开发实战指南(基础) 4 GD32启动流程详解(Keil版)

    ,所有的一切都需要由开发者来设置,这里处理器是没有堆栈,没有中断,更没有外围设备,这些工作是需要软件来指定的,而且不同的CPU类型、不同大小的内存和不同种类的外设,其初始化工作都是不同的。本文将以GD32F207IK (基于Cortex-M3)为例进行讲解。
    的头像 发表于 05-10 09:00 1.7w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>4<b class='flag-5'>章</b> <b class='flag-5'>GD32</b>启动流程详解(Keil版)

    GD32开发实战指南(基础) 7 定时器

    系统滴答定时器一般用来提供“心跳”作用,而GD32定时器最基本功能也是定时,可以设置不同时间长度的定时。定时器除了最基本的定时功能外,定时器与GPIO有挂钩使得它可以发挥强大的作用,比如可以输出
    的头像 发表于 05-11 09:00 1.2w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>7<b class='flag-5'>章</b> 定时器

    GD32开发实战指南(基础) 8 定时器

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 PWM输出的工作原理 脉冲宽度调制(PWM) ,是英文“Pulse Wi
    的头像 发表于 05-12 22:14 7928次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>8<b class='flag-5'>章</b> 定时器

    GD32开发实战指南(基础) 11 CPU的高级代理-DMA

    或者存储器和存储器之间的高速数据传输,因而被广泛地使用。早在 8086 的应用中就已经有 Intel 的 8237 这种典型的 DMA 控制器,而 GD32的 DMA 则是以类似外设的形式添加到 Cortex 内核之外的。可以说,DMA就是CPU的高级代理,DMA大大减轻了CPU的负担。
    的头像 发表于 05-16 08:59 3864次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>11<b class='flag-5'>章</b> CPU的高级代理-DMA

    GD32开发实战指南(基础) 12 ADC

    GD32F2系列有 3 个逐次逼近型的ADC,精度为 12 位,有18个多路复用通道,可以转换来自16个外部通道和2个内部通道的模拟信号。其中ADC0 和 ADC1都有 16 个外部通道, ADC2
    的头像 发表于 05-16 09:03 1.1w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>12<b class='flag-5'>章</b> ADC

    GD32开发实战指南(基础) 14 内部温度传感器

    GD32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。该温度传感器在内部和 ADCx_IN16 输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入
    的头像 发表于 05-17 08:58 5340次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>14<b class='flag-5'>章</b> 内部温度传感器

    GD32开发实战指南(基础) 16 RTC

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 RTC工作原理 1.1 RTC简介
    的头像 发表于 05-18 22:14 7154次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>16<b class='flag-5'>章</b> RTC

    GD32开发实战指南(基础) 17 看门狗

    开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK GD32 有两个看门狗, 一个是
    的头像 发表于 06-03 16:00 1.1w次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>17<b class='flag-5'>章</b> 看门狗

    【图书分享】《STM32库开发实战指南

    ,可作为高校电子信息、通信工程、信息工程等相关专业的教材,也适合作为从事嵌入式领域科技工作者的参考书。目录前言第一部分 库开发初级 1 为什么学习STM32 
    发表于 03-13 17:01

    什么是GD32

    一、前言什么GD32GD32是国内开发的一款单片机,据说开发的人员是来自ST公司的,GD32也是以STM32作为模板做出来的。所以
    发表于 08-12 07:46

    GD32 MCU原理及固件库开发指南》 + 初读感悟

    GD32 MCU原理固件库开发指南这本书内容丰富,囊括了GD32中的所有外设,书中首先介绍了如何使用MDK或IAR软件搭建GD32工程环境,让初学者能快速基于工程上手编程。书中主要对
    发表于 03-31 22:11

    GD32 MCU原理及固件库开发指南》+读后感

    2介绍GD32 MCU快速入门与开发平台搭建的方法,包括对软硬件开发平台、调试工具、GD32
    发表于 06-06 21:52

    GD32开发实战指南(基础) 19 程序加密

    GD32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭。96位的产品唯一身份标识所提供的参考号码对任意一个GD32微控制器
    的头像 发表于 05-20 09:10 4142次阅读
    <b class='flag-5'>GD32</b><b class='flag-5'>开发</b><b class='flag-5'>实战</b><b class='flag-5'>指南</b>(基础<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>19<b class='flag-5'>章</b> 程序加密

    gd32和stm32哪个好?

    。 首先,我们从GD32说起。GD32是由国内的同时表示中心研发的一种基于ARM Cortex-M3 CPU的微控制器,该平台具有很高的兼容性、低功耗以及出色的性能。许多GD32产品也
    的头像 发表于 08-16 11:32 1673次阅读