lpc1114深度睡眠模式Deep-sleep
在深度睡眠模式,除了BOD模块和看门狗振荡器的时钟可以继续工作,其它所有的时钟都会停止工作。可以通过PDSLEEPCFG寄存器来配置在深度睡眠模式期间BOD模块和看门狗振荡器是否需要工作。
在深度睡眠模式,消除了flash、所有模拟外设、处理器自身、存储器、相关控制器和内部总线的功耗。处理器的状态和寄存器、外设的寄存器、内部SRAM的数据和单片机引脚的电平保持不变。
在深度睡眠模式,看门狗振荡器是唯一可以工作的时钟源。当需要定时器定时唤醒单片机的时候,可以开启看门狗时钟。其它的时钟,诸如系统振荡器、IRC和PLL都会掉电。如果需要看门狗振荡器工作,它的时钟必须设置到最小值,即把WDTOSCCTRL寄存器中的FREQSEL位设置为0001。
当需要定时器唤醒单片机,可以在SYSAHBCLKCTRL寄存器中开启看门狗振荡器和一个通用定时器。
1. 怎样进入深度睡眠模式
执行以下步骤进入深度睡眠模式
1. 设置PCON寄存器的DPDEN位为0;
2. 配置PDSLEEPCFG寄存器
a) 如果需要定时器唤醒单片机,在PDRUNCFG寄存器中,把看门狗的时钟打开,然后在MAINCLKSEL把主时钟源选择为看门狗振荡器时钟;
b) 如果不需要定时器唤醒单片机,在PDRUNCFG寄存器中,把看门狗的时钟关闭,把IRC的时钟打开,在MAINCLKSEL寄存器中选择IRC时钟为主时钟源;
3. 配置PDAWAKECFG寄存器;
4. 如果使用外部引脚来唤醒单片机,配置STARTAPRP0,STARTERP0,STARTRSRP0CLR寄存器,初始化外部唤醒引脚,并用NVIC开启该引脚的中断。
5. 在SYSAHBCLKCTRL寄存器中,关闭所有的外设模块。如果用到WDT和定时器,则开启这两的时钟。
6. 设置SCR寄存器中的SLEEPDEEP位为1;
7. 执行WFI指令。
2. 进入深度睡眠模式后,如何唤醒单片机
1. 复位
2. 定时器匹配输出引脚唤醒
3. 外部引脚逻辑信号
深度睡眠模式的外部唤醒引脚一共有13个,分别是P0.0~P0.11和P1.0。可以通过配置具体由哪个引脚唤醒,上升沿或下降沿唤醒。可以通过外部信号唤醒,也可以通过单片机自身定时器的匹配输出引脚电平变化功能来实现。当使用定时器匹配引脚时,只能是在P0.1 、P0.8~P0.11引脚上,因为只有这几个引脚有定时器匹配输出功能。
3. 示例程序
为了很容易的看到效果,这里用流水灯的运行和停止运行来观察是否进入睡眠模式。开始流水灯执行,过一会儿,进入睡眠模式后,流水灯停止流水。
新建一个工程,结构如下图所示:
在main.c函数中,输入以下代码:
#include "lpc11xx.h" #include "pmu.h" #define LED1_ON? LPC_GPIO1->DATA &= ~(1<<0) #define LED1_OFF LPC_GPIO1->DATA |= (1<<0) #define LED2_ON? LPC_GPIO1->DATA &= ~(1<<1) #define LED2_OFF LPC_GPIO1->DATA |= (1<<1) /***********************************/ /* 函数名称:延时函数????????????? */ /***********************************/ void delay() { ???uint16_t i,j; ???for(i=0;i<5000;i++) ???for(j=0;j<200;j++); } /***********************************/ /* 函数名称:LED灯初始化? ?????????*/ /***********************************/ void led_init() { ???LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16); // 使能IOCON时钟 ???LPC_IOCON->R_PIO1_0 &= ~0x07;??? ???LPC_IOCON->R_PIO1_0 |= 0x01; //把P1.0脚设置为GPIO ???LPC_IOCON->R_PIO1_1 &= ~0x07; ???LPC_IOCON->R_PIO1_1 |= 0x01; //把P1.1脚设置为GPIO ???LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<16); // 禁能IOCON时钟 ???LPC_GPIO1->DIR |= (1<<0); // 把P1.0设置为输出引脚 ???LPC_GPIO1->DATA |= (1<<0); // 把P1.0设置为高电平 ???LPC_GPIO1->DIR |= (1<<1); // 把P1.1设置为输出引脚 ???LPC_GPIO1->DATA |= (1<<1); // 把P1.1设置为高电平 } /***********************************/ /* 函数名称:主函数??????????????? */ /***********************************/ int main() { ???uint8_t cnt=0; ???led_init(); ???while(1) ???{ ??????delay(); ??????LED1_ON; ??????LED2_OFF; ??????delay(); ??????LED1_OFF; ??????LED2_ON; ??????cnt++; ??????if(cnt>20) ??????{ ?????????cnt = 0; ?????????Entry_Deep_Sleep(); ??????} ???} }
第35行,main函数开始看起。
第37行,定义了一个变量,用来计数。
第38行,初始化led。(关于初始化led的部分,请看第三章内容)
第39行,进入while循环。LED1和LED2将交替闪烁,知道cnt计数到20。
第51行,执行进入深度睡眠模式函数。
在pmu.h文件中,输入以下代码:
#ifndef? __LPC11XX_PMU_H #define? __LPC11XX_PMU_H extern void Entry_Deep_Sleep(void); #endif
在pmu.c文件中,输入以下代码:
#include "lpc11xx.h" #include "pmu.h" void Entry_Deep_Sleep(void) { ???// step1 ???LPC_PMU->PCON &= ~(1<<1);????? // DPDEN=0; 选择sleep/deep-sleep模式 ???// step2 ???LPC_SYSCON->MAINCLKSEL??? = 0x0;?????? ?/* Select IRC? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01;???? ?/* Update MCLK Clock Source */ ???LPC_SYSCON->MAINCLKUEN??? = 0x00;???? ?/* Toggle Update Register?? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01; ???while (!(LPC_SYSCON->MAINCLKUEN & 0x01));?? /* Wait Until Updated ?*/ ???// step3 ???LPC_SYSCON->PDAWAKECFG &= ~(1<<5);? // SYSOSC_PD 唤醒后系统振荡器上电 ???LPC_SYSCON->PDAWAKECFG &= ~(1<<7);?? // SYSPLL_PD 唤醒后PLL上电 ???// step4 ???LPC_SYSCON->STARTRSRP0CLR |= (1<<7); // RESET P0.7 ???LPC_SYSCON->STARTAPRP0 &= ~(1<<7);? // 配置P0.7脚为唤醒引脚 ???LPC_SYSCON->STARTERP0 |= (1<<7); // Enable P0.7脚作为唤醒引脚?? ???NVIC_EnableIRQ(WAKEUP7_IRQn); ???// step5 ???LPC_SYSCON->SYSAHBCLKCTRL = 0x01f; // 关闭所有外设模块 ???// step6 ???SCB->SCR |= (1<<2);????? // SLEEPDEEP=1; 选择deep_sleep模式 ???// step7 ???__wfi();??????????????????????????? ????????? // 写wfi指令进入低功耗模式 } void WAKEUP_IRQHandler(void) { ???// 时钟配置 ???LPC_SYSCON->MAINCLKSEL??? = 0x3;?? ?/* Select PLL Clock Output? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01;? /* Update MCLK Clock Source */ ???LPC_SYSCON->MAINCLKUEN??? = 0x00;???? /* Toggle Update Register?? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01; ???while (!(LPC_SYSCON->MAINCLKUEN & 0x01)); /* Wait Until Updated?????? */ ???// 清唤醒标志 ???LPC_SYSCON->STARTRSRP0CLR |= (1<<7); // RESET P0.7 ???LPC_PMU->PCON &= ~(1<<8); // SLEEPFLAG标志位清零???? ???LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); // GPIO上电 }
在pmu.c文件中,定义了两个函数:
Entry_Deep_Sleep()函数的功能是进入深度睡眠模式。
WAKUP_IRQHandler()是唤醒引脚的中断处理函数。
Entry_Deep_Sleep()函数里面的代码分为7个步骤配置。
???// step1 ???LPC_PMU->PCON &= ~(1<<1);????? // DPDEN=0; 选择sleep/deep-sleep模式
第一步,给PCON寄存器的bit1位写0。(PCON寄存器的说明,请看9.1.3节。)
???// step2 ???LPC_SYSCON->MAINCLKSEL??? = 0x0;?????? ?/* Select IRC? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01;???? ?/* Update MCLK Clock Source */ ???LPC_SYSCON->MAINCLKUEN??? = 0x00;???? ?/* Toggle Update Register?? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01; ???while (!(LPC_SYSCON->MAINCLKUEN & 0x01));?? /* Wait Until Updated ?*/
第二步,选择IRC为主时钟。(看不懂这部分程序的,请看第二章。)
???// step3 ???LPC_SYSCON->PDAWAKECFG &= ~(1<<5);? // SYSOSC_PD 唤醒后系统振荡器上电 ???LPC_SYSCON->PDAWAKECFG &= ~(1<<7);?? // SYSPLL_PD 唤醒后PLL上电
第三步,配置退出深度睡眠模式后,系统振荡器和PLL上电。(为什么?因为退出深度睡眠模式后,我们要继续使用外部晶振倍频到5倍工作于50MHz主频。)
PDAWAKECFG:掉电唤醒配置寄存器
位 | 符号 | 值 | 描述 | 复位值 |
0 | IRCOUT_PD | IRC 振荡器输出唤醒配置 | 0 | |
0 | 上电 | |||
1 | 掉电 | |||
1 | IRC_PD | IRC 振荡器掉电唤醒配置 | 0 | |
0 | 上电 | |||
1 | 掉电 | |||
2 | Flash_PD | Flash 唤醒配置 | 0 | |
0 | 上电 | |||
1 | 掉电 | |||
3 | BOD_PD | BOD唤醒配置 | 0 | |
0 | 上电 | |||
1 | 掉电 | |||
4 | ADC_PD | ADC唤醒配置 | 1 | |
0 | 上电 | |||
1 | 掉电 | |||
5 | SYSOSC_PD | 系统振荡器唤醒配置 | 1 | |
0 | 上电 | |||
1 | 掉电 | |||
6 | WDTOSC_PD | 看门狗振荡器唤醒配置 | 1 | |
0 | 上电 | |||
1 | 掉电 | |||
7 | SYSPLL_PD | 系统PLL唤醒配置 | 1 | |
0 | 上电 | |||
1 | 掉电 | |||
8 | – | – | 保留位,总是给这些位写1 | 1 |
9 | – | – | 保留位,总是给这些位写0 | 0 |
10 | – | – | 保留位,总是给这些位写1 | 1 |
11 | – | – | 保留位,总是给这些位写1 | 1 |
12 | – | – | 保留位,总是给这些位写0 | 0 |
15:13 | – | – | 保留位,总是给这些位写111 | 111 |
31:16 | – | – | 保留位 |
这个寄存器是用来配置从深度睡眠模式唤醒单片机以后,哪个模块继续工作。从复位值可以看出,默认情况下,IRC、FLASH存储器和BOD模块继续工作。
???// step4 ???LPC_SYSCON->STARTRSRP0CLR |= (1<<7); // RESET P0.7 ???LPC_SYSCON->STARTAPRP0 &= ~(1<<7);? // 配置P0.7脚为唤醒引脚 ???LPC_SYSCON->STARTERP0 |= (1<<7); // Enable P0.7脚作为唤醒引脚?? ???NVIC_EnableIRQ(WAKEUP7_IRQn);
第四步,配置0.7脚作为从深度睡眠模式唤醒单片机的引脚,并且开了P0.7引脚的中断。当唤醒后,程序首先会进入中断处理函数,然后再回到进入深度睡眠模式后的地方继续开始运行。
STARTRSRP0CLR:唤醒引脚复位寄存器
位 | 符号 | 描述 | 复位值 |
11:0 | RSRPIO0_n | PIO0_0~PIO0~11的启动逻辑配置
0 = 无用 1 = 写1允许对应位启动逻辑 |
不定 |
12 | RSRPIO1_0 | PIO1_0的启动逻辑配置
0 = 无用 1 = 写1允许PIO1_0启动逻辑 |
不定 |
31:13 | – | 保留位,不能给这些位写1 | 不定 |
第17行,给STARTSRP0CLR寄存器bit7写1,复位清零P0.7唤醒引脚。
STARTAPRP0:唤醒引脚模式寄存器
位 | 符号 | 描述 | 复位值 |
11:0 | APRPIO0_n | PIO0_0~PIO0~11的边沿启动选择
0 = 下降沿 1 = 上升沿 |
0x0 |
12 | APRPIO1_0 | PIO1_0的边沿启动逻辑
0 = 下降沿 1 = 上升沿 |
0x0 |
31:13 | – | 保留位,不能给这些位写1 | 0x0 |
第18行,给STARTAPRP0寄存器的bit7写0,配置P0.7引脚上下降沿唤醒。
STARTERP0:唤醒引脚允许寄存器
位 | 符号 | 描述 | 复位值 |
11:0 | ERPIO0_n | PIO0_0~PIO0_11的启动允许位
0 = 禁止 1 = 允许 |
0x0 |
12 | ERPIO1_0 | PIO1_0的启动允许位
0 = 禁止 1 = 启动 |
0x0 |
31:13 | – | 保留位,禁止给这些位写1 | 0x0 |
第19行,允许P0.7引脚作为唤醒引脚。
第20行,开启P0.7引脚的NVIC唤醒中断。
// step5 LPC_SYSCON->SYSAHBCLKCTRL = 0x01f; // 关闭所有外设模块
第五步,关闭所有外设模块。(注意:bit0~bi4不属于外设模块,不能关)
???// step6 ???SCB->SCR |= (1<<2);????? // SLEEPDEEP=1; 选择deep_sleep模式
第六步,给SCR寄存器的bit2写1,选择深度睡眠模式。关于SCR寄存器的介绍,请看9.1.3节。
???// step7 ???__wfi();??????????????????????????? ????????? // 写wfi指令进入低功耗模式
第七步,执行WFI指令。WFI是专门为了节省功耗设计的指令,执行该指令后,单片机将挂起等待一个中断到来,挂起后,相当于一直执行NOP指令。
void WAKEUP_IRQHandler(void) { ???// 时钟配置 ???LPC_SYSCON->MAINCLKSEL??? = 0x3;?? ?/* Select PLL Clock Output? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01;? /* Update MCLK Clock Source */ ???LPC_SYSCON->MAINCLKUEN??? = 0x00;???? /* Toggle Update Register?? */ ???LPC_SYSCON->MAINCLKUEN??? = 0x01; ???while (!(LPC_SYSCON->MAINCLKUEN & 0x01)); /* Wait Until Updated?????? */ ???// 清唤醒标志 ???LPC_SYSCON->STARTRSRP0CLR |= (1<<7); // RESET P0.7 ???LPC_PMU->PCON &= ~(1<<8); // SLEEPFLAG标志位清零 ???LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); // GPIO上电 }
当通过P0.7引脚唤醒单片机后,首先会进入这个中断函数执行。
第31~35行,把主时钟重新配置为50M主频。(看不懂这部分的童鞋,请看第一章。)
第37行,清P0.7唤醒标志。
第38行,清SLEEPFLAG标志位。
第39行,给GPIO上电。(为什么?因为刚才进入深度睡眠模式之前,把GPIO时钟关了,唤醒后,我们要执行流水灯程序,所以要开了GPIO时钟。)
在RATION LPC1114V3.0开发板上,把P0.7引脚与P1.9脚用一条杜邦线短接,可以通过按键KEY1来唤醒单片机。