1 概述1.1 MCU低功耗模式当MCU处于死等状态时,可让MCU进入低功耗模式,或者降低系统工作时钟;当需要MCU执行动作时,可产生唤醒信号唤醒MCU,或者恢复至高速时钟。这样可以降低系统在整个运行期间的功耗,且不影响系统的运行。 APM32F103xE有sleep、stop、standby、备份域供电的低功耗工作模式。 通常是通过停止MCU的内部资源来降低功耗,例如关闭内核、关闭时钟源、调整电源域的工作模式、SRAM是否供电、关闭各种外设的时钟。不同的低功耗模式关闭的资源也不一样,具体内容可参考用户手册。 而且唤醒后,因为关闭的资源不同,恢复到进入低功耗模式的工作状态的动作也是不一样的,唤醒的时间也不一样。 1.2 唤醒方式可通过MCU的内部资源唤醒MCU,例如GPIO、串口、SPI等外设。每种外设唤醒的方式由分为中断唤醒、事件唤醒,但并不是所有的外设都支持事件唤醒,手册中有专门描述支持的唤醒事件。使用事件唤醒,不需要配置对应的中断;使用中断方式唤醒后,会立刻进入对应的中断(前提是使能中断,且没有触发其它高优先级的中断)。 例如,GPIO即可支持事件唤醒,由支持中断唤醒,但是呢,各种低功耗模式下让不让你唤醒又是另一回事了,例如standby模式下,只支持WKUP引脚的上升沿、RTC闹钟事件、NRST引脚的外部复位、IWDT复位,GPIO产生的中断、事件就无法唤醒standby模式。 以下是记录了调试分别通过GPIO的中断、事件唤醒APM32F103xE的stop模式的代码、现象。 2 硬件电路选择的开发板是APM32F103ZEmini板。按键B2连接MCU的PA0引脚,通过按键产生上升沿或者下降沿触发MCU中断或者产生事件唤醒MCU另外能唤醒standby模式的GPIO只有PA0,为了减少后续代码的开发,选择PA0作为唤醒GPIO。
3 代码#include "main.h"
#include "apm32f10x_pmu.h"
#include "stdio.h"
void USART1_Init(void)
{
GPIO_Config_T GPIO_ConfigStruct;
USART_Config_T USART_ConfigStruct;
RCM_EnableAPB2PeriphClock((RCM_APB2_PERIPH_T)(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_USART1));
GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
GPIO_ConfigStruct.pin = GPIO_PIN_9;
GPIO_ConfigStruct.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &GPIO_ConfigStruct);
USART_ConfigStruct.baudRate = 115200;
USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_ConfigStruct.mode = USART_MODE_TX;
USART_ConfigStruct.parity = USART_PARITY_NONE;
USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
USART_Config(USART1, &USART_ConfigStruct);
USART_Enable(USART1);
while(USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
}
void T_PWR_SleepPA0EventWakeUp(void)
{
GPIO_Config_T PA0_GPIO_Config;
EINT_Config_T PA0_EVENT_Config;
RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)(RCM_APB1_PERIPH_PMU | RCM_APB1_PERIPH_BAKR));
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
PA0_GPIO_Config.mode = GPIO_MODE_IN_PU;
PA0_GPIO_Config.pin = GPIO_PIN_0;
GPIO_Config(GPIOA, &PA0_GPIO_Config);
PA0_EVENT_Config.line = EINT_LINE_0;
PA0_EVENT_Config.mode = EINT_MODE_EVENT;
PA0_EVENT_Config.trigger = EINT_TRIGGER_RISING;
PA0_EVENT_Config.lineCmd = ENABLE;
EINT_Config(&PA0_EVENT_Config);
GPIO_ConfigEINTLine(GPIO_PORT_SOURCE_A, GPIO_PIN_SOURCE_0);
printf("1进入stop模式\n\r");
PMU_EnterSTOPMode(PMU_REGULATOR_ON,PMU_STOP_ENTRY_WFE);
SystemInit();
USART1_Init();
printf("2通过事件唤醒,退出stop模式\n\r\n\r");
}
void T_PWR_SleepPA0IntWakeUp(void)
{
GPIO_Config_T PA0_GPIO_Config;
EINT_Config_T PA0_Int_Config;
RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)(RCM_APB1_PERIPH_PMU | RCM_APB1_PERIPH_BAKR));
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
PA0_GPIO_Config.mode = GPIO_MODE_IN_PU;
PA0_GPIO_Config.pin = GPIO_PIN_0;
GPIO_Config(GPIOA, &PA0_GPIO_Config);
NVIC_EnableIRQRequest(EINT0_IRQn,0x01,0x01);
PA0_Int_Config.line = EINT_LINE_0;
PA0_Int_Config.mode = EINT_MODE_INTERRUPT;
PA0_Int_Config.trigger = EINT_TRIGGER_RISING;
PA0_Int_Config.lineCmd = ENABLE;
EINT_Config(&PA0_Int_Config);
GPIO_ConfigEINTLine(GPIO_PORT_SOURCE_A, GPIO_PIN_SOURCE_0);
printf("1进入stop模式\n\r");
PMU_EnterSTOPMode(PMU_REGULATOR_ON,PMU_STOP_ENTRY_WFE);
SystemInit();
USART1_Init();
printf("3通过中断唤醒,退出stop模式\n\r\n\r");
}
void EINT0_IRQHandler(void)
{
if(EINT_ReadStatusFlag(EINT_LINE_0) != RESET)
{
EINT_ClearIntFlag(EINT_LINE_0);
USART1_Init();
printf("2通过中断唤醒,退出stop模式,进入中断处理函数EINT0_IRQHandler\n\r");
}
}
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
USART_TxData(USART1, (char) ch);
while(USART_ReadStatusFlag(USART1, USART_FLAG_TXC) == RESET);
return (ch);
}
//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
while (USART_ReadStatusFlag(USART1, USART_FLAG_RXBNE) == RESET);
return (int)USART_RxData(USART1);
}
int main(void)
{
USART1_Init();
T_PWR_SleepPA0EventWakeUp();
// T_PWR_SleepPA0IntWakeUp();
while(1)
{
}
}
4 现象4.1 通过事件唤醒stop模式,串口记录数据如下:
4.2 通过中断唤醒stop模式,串口记录数据如下: 通过中断唤醒,会优先进入中断处理函数。 5 Keil工程附件
|