×
单片机 > 单片机程序设计 > 详情

lpc1114深度睡眠模式Deep-sleep

发布时间:2020-06-06 发布时间:
|

在深度睡眠模式,除了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:掉电唤醒配置寄存器

符号描述复位值
0IRCOUT_PD
IRC 振荡器输出唤醒配置0
0上电
1掉电
1IRC_PD
IRC 振荡器掉电唤醒配置0
0上电
1掉电
2Flash_PD
Flash 唤醒配置0
0上电
1掉电
3BOD_PD
BOD唤醒配置0
0上电
1掉电
4ADC_PD
ADC唤醒配置1
0上电

1掉电
5SYSOSC_PD
系统振荡器唤醒配置1
0上电
1掉电
6WDTOSC_PD
看门狗振荡器唤醒配置1
0上电
1掉电
7SYSPLL_PD
系统PLL唤醒配置1
0上电
1掉电
8保留位,总是给这些位写11
9保留位,总是给这些位写00
10保留位,总是给这些位写11
11保留位,总是给这些位写11
12保留位,总是给这些位写00
15:13保留位,总是给这些位写111111
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:0RSRPIO0_nPIO0_0~PIO0~11的启动逻辑配置


0 = 无用

1 = 写1允许对应位启动逻辑

不定
12RSRPIO1_0PIO1_0的启动逻辑配置


0 = 无用

1 = 写1允许PIO1_0启动逻辑

不定
31:13保留位,不能给这些位写1不定

 

第17行,给STARTSRP0CLR寄存器bit7写1,复位清零P0.7唤醒引脚。

STARTAPRP0:唤醒引脚模式寄存器

符号描述复位值
11:0APRPIO0_nPIO0_0~PIO0~11的边沿启动选择


0 = 下降沿

1 = 上升沿

0x0
12APRPIO1_0PIO1_0的边沿启动逻辑


0 = 下降沿

1 = 上升沿

0x0
31:13保留位,不能给这些位写10x0

第18行,给STARTAPRP0寄存器的bit7写0,配置P0.7引脚上下降沿唤醒。

 

STARTERP0:唤醒引脚允许寄存器

符号描述复位值
11:0ERPIO0_nPIO0_0~PIO0_11的启动允许位


0 = 禁止

1 = 允许

0x0
12ERPIO1_0PIO1_0的启动允许位


0 = 禁止

1 = 启动

0x0
31:13保留位,禁止给这些位写10x0

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



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
如何升级STM32单片机的代码