在深度睡眠模式,除了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来唤醒单片机。
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』