lpc1114深度睡眠模式Deep-sleep

  • 学习6931次

在深度睡眠模式,除了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. 示例程序

为了很容易的看到效果,这里用流水灯的运行和停止运行来观察是否进入睡眠模式。开始流水灯执行,过一会儿,进入睡眠模式后,流水灯停止流水。

新建一个工程,结构如下图所示:

pmu

在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来唤醒单片机。

发表评论