目录
1、背景
最近用到低功耗模式-待机模式和RTC唤醒,因此重新梳理RTC和睡眠模式--待机模式。
1.1什么是待机模式
待机模式: 基于CortexTM-M4F深度睡眠模式,其中调压器被禁止,因此1.2V域断电---PLL、HSI振荡器和HSE振荡器也将关闭。
除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM和寄存器内容都将丢失。
从待机模式唤醒后, 除了备份域+PWR电源控制/状态寄存器(PWR_CSR)外,所有寄存器都将复位。因此程序将按照复位(启动引脚采样、复位向量已获取)后的方式重新执行。
1.2退出待机模式
检测到外部复位(NRST引脚)、IWDG复位、WKUP引脚上升沿、RTC闹钟、RTC唤醒事件、入侵事件或时间戳事件时,微处理器退出待机模式。
特别是RTC的复用功能从低功耗模式唤醒MCU,需要我特别写一章。RTC复用功能包括RTC闹钟(闹钟A和闹钟B)、RTC唤醒事件、RTC入侵事件和RTC时间戳事件。
通过使用RTC闹钟或RTC唤醒事件,无需依赖外部中断即可将系统从低功耗模式唤醒-----自动唤醒模式。
1.2.1RTC唤醒退出低功耗模式(待机为例)
基于STM32F4的RTC唤醒中断知识
要通过RTC唤醒事件从待机模式唤醒器件,必须
a》使能RTC_CR寄存器中的RTC唤醒中断
b》配置RTC已生成RTC唤醒事件。
要使能RTC唤醒中断,需按照如下顺序操作:
1、将EXTI线22配置为中断模式并将其使能,然后选择上升沿有效;
2、配置NVIC中的RTC_WKUP IRQ通道并将其使能
3、配置RTC以生成RTC唤醒定时器事件
STM32F4xx能够处理外部或内部事件来唤醒内核(WFE)。
仅当RTC时钟源为LSE或LSI时,才能从停机和待机模式中唤醒。
1.2.3 进入待机模式之前的建议操作顺序:
从RTC唤醒从低功耗模式唤醒器件时的属性怒
a》禁止RTC唤醒中断(RTC_CR寄存器中WUTIE位 清0);
b》将RTC唤醒(WUTF)标志清零
c》将PWR唤醒(WUF)标志清零
d》使能RTC环形中断
e》重新进入低功耗模式
2、RTC初始化和配置
2.1 RTC寄存器写保护
系统复位(区分上电复位)后,由于PWR_CR 复位后默认值为0x0000 C000, DBP位为0:禁止访问RTC寄存器、RTC备份寄存器和SRAM备份寄存器。 ,需要将DBP位 置1,才能使能对RTC寄存器的写保护。
对于上电复位,所有RTC寄存器均受写保护,向RTC_WPR(写保护寄存器)写入密钥来解开对RTC寄存器的写操作。
解开密钥: 0xCA--->0x53;
锁上密钥:任意 如0xFF;
这部分的程序:以stm32f4
{
u16 retry = 0x1FFF;
/*PWR 中关于RTC配置*/
RCC->APB1ENR|=1<<28; //使能电源控制器PWR接口时钟
PWR->CR|=1<<8; //使能后备区域写访问(RTC+SRAM)
//if(RTC_Read_BKR(0)!=0X5050) //是否第一次配置?
{
/*RTC 时钟源配置,由于RCC备份域控制寄存器RCC_BDCR 中关于RTC的位均在备份域中,因此 使能备份域的指令在前。*/
RCC->BDCR|=1<<0; //LSE 外部低速振荡器时钟开启
while(retry&&((RCC->BDCR&0X02)==0))//等待 LSE 准备好
{
retry--;
delay_ms(5);
}
if(retry==0) return FALSE; //LSE 开启失败
RCC->BDCR |= 0x01<<8; //0b01 选择LSE振荡器时钟用作RTC时钟
RCC->BDCR |= 0x01<<15; //RTC时钟使能
//解开(关闭)RTC寄存器的写保护
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
}
2.2 RTC日历初始化和配置
设置时间格式和预分频器配置 设置初始时间和日期日历值。
操作顺序:
1、初始化和状态寄存器RTC_ISR中INIT位 置1,以进入初始化模式。
2、轮询RTC_ISR寄存器中INITF位。待INITF置 1时,RTC进入了初始化阶段模式。-----大约需要2个RTCCLK时钟周期;
进入初始化模式后,日历计数器将停止工作,且其值可更新。
3、要为日历计数器生成1Hz的时钟,应首先编程RTC_PRER寄存器中的同步预分频系数,再编程异步预分频系数。
必须要RTC_PRER寄存器执行两次单独的写访问。
4、在影子寄存器RTC_TR和RTC_DR 中加载初始时间和日期值,然后通过RTC_CR寄存器中的FMT位配置时间格式(12/24小时制)
5、通过清零INIT位退出初始化模式。 从影子寄存器 自动到实际日历计数器值。 4个RTCCLK时钟周期
当初始化系列完成之后,日历开始计数。
这部分的程序:以stm32f4
{
/*进入初始化模式,才可修改 RTC预分频器配置、RTC控制寄存器的小时格式设置*/
RTC_Init_Mode();
{
RCC->PRER |= 0xFF; //先设置同步预分频系数 255,再设置异步预分频系数127 3
RCC->PRER |= 0x7F<<16; //上电复位值为0x007F 00 FF 32768 /(127+1)/(255+1) = 1 Hz
RCC->CR &= ~(0x01<<6); //设置小时格式为24小时/天格式
RTC->ISR &= ~(0x01<<7); //退出RTC初始化模式
RTC->WPR = 0xFF; //使能RTC寄存器写保护
}
//设置日历新值 4
}
进入RTC初始化模式
u8 RTC_Init_Mode(void)
{
retry = 0x1FFF;
RCC->ISR |= 0x01<<7; //置1,初始化模式,用于编程(设置、写)时间和日期寄存器(RTC_TR和RTC_DR),以及预分频寄存器(RTC_PRTR)。进入后计数器停止计数; 当INIT位被复位(清零)后,计数器从新值开始计数。 1
while(retry&&((RCC->ISR&0x40)==0x00)) //超时或设置成功才可退出循环 2
{
retry --;
delay_ms(5);
}
if(retry==0) return FALSE;
return SUCCESS;
}
设置日历新值 -----即对RTC_TR(RTC时间寄存器)和RTC_DR(RTC日期寄存器)进行设置;RTC_TR和RTC_DR是日历时间和日期影子寄存器,只有在初始化模式下才能对这些寄存器执行写操作。
/*********************************************************
function name: RTC_Set_Time
paras: hour 小时,min 分钟, sec 秒钟, ampm am/pm, 0=AM/24H, 1= PM
return: 0 success, 1 fail
*********************************************************/
u8 RTC_Set_Time(u8 hour, u8 min, u8 sec, u8 ampm)
{
u8 temp = 0;
//使能后备域写使能后,关闭RTC寄存器写保护
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
if(RTC_Init_Mode()) return 1; //进入RTC初始化模式失败,则返回1
// temp = (((u32)ampm&0x01)<<22) | (((u32)(hour/10)&0x03)<<20) | (((u32)(hour%10)&0x0F)<<16) | ()
temp = (((u32)ampm&0x01)<<22) | ((u32)RTC_Byte2BCD(hour)<<16) | ((u32)RTC_Byte2BCD(min)<<8) | ((u32)RTC_Byte2BCD(sec));
RTC_TR = temp; //设置日历时间
RTC->ISR &= ~(0x01<<7); //退出初始化模式,回到自由运行模式
return 0;
}
/*********************************************************
function name: RTC_Set_Data
paras: year年,month月, date 日, week 星期(1~7,0 非法)
return: 0 success, 1 fail
*********************************************************/
u8 RTC_Set_Date(u8 year, u8 month, u8 date, u8 week)
{
u8 temp;
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
if(RTC_Init_Mode()) return 1; //进入RTC初始化模式失败,则返回1
temp = (((u32)week&0x07)<<13) | ((u32)RTC_Byte2BCD(year)<<16) | ((u32)RTC_Byte2BCD(month)<<8) | (u32)RTC_Byte2BCD(month);
RTC->DR = temp; //设置日历日期
RTC->ISR &= ~(1<<7); //退出初始化模式,回到自由运行模式
return 0;
}
/*************************************************************************
function name : RTC_Byte2BCD(u8 byte)
*************************************************************************/
u8 RTC_Byte2BCD(u8 byte)
{
u8 bcdHight = 0;
u8 bcdLow = 0;
bcdHight = byte/10; //十位
bcdLow = byte%10; //个位
return ((u8)(bcdHight << 4) | bcdLow ); //十位和个位均用4个bits表示
}
//读取RTC日历时间
要想正确读取RTC日历寄存器(包括RTC_SSR、RTC_TR和RTC_DR),APB1时频必须大于或等于RTC时钟的七倍——这是处于同步机制行为的安全性。
同步机制------为了避免前后两次读取间隔小于2个RTCCLK周期(每次将日历寄存器中的值赋值到RTC_SSR、RTC_TR、RTC_DR影子寄存器是,RTC_ISR寄存器中RSF位都会置1,每两个TRCCCLK周期执行一次复制),因此必须在读取完后清零RSF位,等待RSF置1之后才可在此读取RTC_SSR、RTC_TR、RTC_DR的值。 这就有了等待RSF同步的操作
u8 RTC_Wait_Synchro(void)
{ u32 retry = 0xFFFFFF;
RTC->WPR = 0xCA; //关闭RTC写保护
RTC->WPR = 0x55;
RTC->ISR = ~(1<<5); //清除寄存器同步标志----日历影子寄存器尚未同步
if(retry&&(RTC->ISR&(1<<5))==0x00)
{ retry--;
}
if(retry==0) return 1; 同步失败
RTC->WPR = 0xFF; //使能RTC写保护
return 0;
}
2.3 设置RTC周期性唤醒
设置RTC周期性唤醒 以及设置RTC闹钟中断等步骤都是十分类似。
步骤: 1、禁止唤醒定时器(即RTC_CR中的WUTE(wake up Timer Enable)清零)
2、轮询直到RTC_ISR(RTC初始化状态寄存器)中WUTWF(write flag)位 置1(这是硬件置1), 置1代表允许更新唤醒定时器配置(获得写执行能力)。
3、配置唤醒定时器寄存器以设置唤醒自动重载值,并在RTC_CR中选择唤醒唤醒时钟(即RTC_CR的WUCKSEL[2:0]位),最后使能唤醒定时器(即RTC_CR的WUTE位置1)。唤醒定时器重新开始递减计数。
4、设置中断使能和中断线配置,配置NVIC
void RTC_Set_WakeUp(u8 wksel, u16 cnt)
{
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
RTC->CR &= ~(0x01<<10); //清零RTC_CR的WUTE位, 除能RTC 唤醒定时器
while(RTC->ISR & (0x04)==0x00) delay_us(10); //等待允许更新唤醒 定时器设置 write
/选择RTC 唤醒定时器的时钟 RTC的 2 4 8 16 分频 或 1Hz的ck_spre
RTC->CR &= ~(0x07); //清零WUCKSEL[2:0]
RTC->CR |= wksel & 0x07; //设置时钟
RTC->WUTR = cnt; //设置唤醒定时器的自动重载值
//设置中断
RTC->ISR &= ~(0x01<<10); //清除RTC_ISR 的唤醒定时器标志 WUTF
RTC->CR |=0x01<<14; //使能唤醒定时器 置1使能
//RTC->CR |= 0x01<<10; //使能唤醒定时器 开始计数 配合低功耗一般不在配置时开启
RTC->WPR = 0xFF; //使能RTC写保护
对于RTC唤醒事件 连接的是EXTI线22.
EXTI->PR &= ~(0x1<<22); //清除线22的挂起标志位
EXTI->IMR |= (0x01<<22); //开放line22的中断请求
EXTI->RTSR |= (0x01<<22); //line22事件上升沿触发
MY_NVIC_Init(2,2,RTC_WKUP_IRQn,2); //抢占2,子优先级2,组2
}
3、进入待机模式
进入待机模式的步骤
1、将CortexTM-M4F系统控制寄存器SCB_SCR的SLEEEPDEEP位 置1-----MCU进入深度睡眠的前提
2、将电源控制寄存器(PWR_CR)中的PDDS位 零0------选择MCU进入深度睡眠时进入的是待机模式,而非停止模式
3、将电源控制/状态寄存器(PWR_CSR)中的WUF位清零-----唤醒标志位清零
4、将所选唤醒源对应的RTC中断标志清零-------唤醒源中断标志清零
5、进入待机模式命令
进入待机模式前其他外设的处理步骤
a》
b》
c》
d》
void Sys_Enter_Standby(void)
{
}
4、实操
任务:正常运行,按键3秒以上,进入待机模式,待机5s唤醒重新工作。
实操后修正: