1. STOP0模式
进入STOP0模式时R-SRAM (16KB) 和SRAM 数据都不会丢失。在进入STOP0模式之前必须要关闭SysTick中断,否则会唤醒STOP0模式,唤醒之后再开启SysTick中断,对应的代码步骤为:
(1)关闭 SysTick (暂停计数+关闭中断),保留原有配置值(LOAD/VAL), 下次开启恢复运行:
SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
(2)进入STOP0模式代码:
正常休眠:
PWR_EnterSLEEPMode(0, PWR_STOPENTRY_WFI);
低功耗休眠:
PWR_EnterSLEEPMode(1, PWR_STOPENTRY_WFI);
(3)开启 SysTick (恢复计数+开启中断),使用之前配置好的时钟源、重装载值:
SysTick->CTRL |= (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
(4)当进入低功耗STOP0模式时,唤醒后必须要清SLEEPDEEP 位,否则处理完中断程序后会进入休眠,具体需要在中断处理函数加上以下两句清除相应标志位:
SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
2. STOP2模式
(1)进入STOP2时,核心数字域彻底掉电、主调压器关闭、HSI/HSE/PLL 全关;仅 7 类 唤醒源有效,且除 R-SRAM (16KB) 和备份寄存器外,其他 SRAM / 寄存器全丢;
(2)进入STOP2模式前须将外总时钟改为内部时钟:
void SetSysClockToHSI(void)
{
RCC_DeInit();
RCC_EnableHsi(ENABLE);
/* Wait HSI ready*/
/* if HSI config fail, User can add here some code to deal with this error */
while((RCC->CTRL & RCC_CTRL_HSIRDF) == 0)
{
}
/* Enable Prefetch Buffer */
FLASH_PrefetchBufSet(FLASH_PrefetchBuf_EN);
/* Flash 0 wait state */
FLASH_SetLatency(FLASH_LATENCY_0);
/* HCLK = SYSCLK */
RCC_ConfigHclk(RCC_SYSCLK_DIV1);
/* PCLK2 = HCLK */
RCC_ConfigPclk2(RCC_HCLK_DIV1);
/* PCLK1 = HCLK */
RCC_ConfigPclk1(RCC_HCLK_DIV1);
/* Select HSI as system clock source */
RCC_ConfigSysclk(RCC_SYSCLK_SRC_HSI);
/* Wait till PLL is used as system clock source */
/* if system clock switch fail, User can add here some code to deal with this error */
while (RCC_GetSysclkSrc() != 0x00)
{
}
RCC_ConfigHse(RCC_HSE_DISABLE);
/* if HSE close fail, User can add here some code to deal with this error */
while((RCC->CTRL & RCC_CTRL_HSERDF) != 0)
{
}
}
(3)唤醒后需要重新配置为外部时钟,同时倍频到需要的时钟频率;
void SYSCLKConfig_STOP(uint32_t RCC_PLLMULL)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
uint32_t Flash_Latency_Temp=0;
uint32_t PLL_Temp=0;
uint32_t System_Temp=0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CTRL |= ((uint32_t)RCC_CTRL_HSEEN);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTRL & RCC_CTRL_HSERDF;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTRL & RCC_CTRL_HSERDF) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->AC |= FLASH_AC_PRFTBFEN;
/* Flash 2 wait state */
Flash_Latency_Temp = FLASH->AC;
Flash_Latency_Temp &= (uint32_t)((uint32_t)~FLASH_AC_LATENCY);
if(RCC_PLLMULL == RCC_CFG_PLLMULFCT18) //144M
{
Flash_Latency_Temp |= (uint32_t)FLASH_AC_LATENCY_4;
}
else if(RCC_PLLMULL == RCC_CFG_PLLMULFCT9) //72M
{
Flash_Latency_Temp |= (uint32_t)FLASH_AC_LATENCY_2;
}
else
{
Flash_Latency_Temp |= (uint32_t)FLASH_AC_LATENCY_1;
}
FLASH->AC = Flash_Latency_Temp;
if(RCC_PLLMULL == RCC_CFG_PLLMULFCT18) //144M
{
RCC_ConfigHclk(RCC_SYSCLK_DIV1);/* HCLK = SYSCLK, MAX = 144M*/
RCC_ConfigPclk2(RCC_HCLK_DIV2);/* PCLK2 = HCLK, MAX = 72M*/
RCC_ConfigPclk1(RCC_HCLK_DIV4);/* PCLK1 = HCLK, MAX = 36M */
}
else if(RCC_PLLMULL == RCC_CFG_PLLMULFCT9) //72M
{
RCC_ConfigHclk(RCC_SYSCLK_DIV1);/* HCLK = SYSCLK, MAX = 144M*/
RCC_ConfigPclk2(RCC_HCLK_DIV1);/* PCLK2 = HCLK, MAX = 72M*/
RCC_ConfigPclk1(RCC_HCLK_DIV2);/* PCLK1 = HCLK, MAX = 36M*/
}
else if(RCC_PLLMULL == RCC_CFG_PLLMULFCT6) //48M
{
RCC_ConfigHclk(RCC_SYSCLK_DIV1);/* HCLK = SYSCLK ,MAX = 144M*/
RCC_ConfigPclk2(RCC_HCLK_DIV1);/* PCLK2 = HCLK, MAX = 72M*/
RCC_ConfigPclk1(RCC_HCLK_DIV2);/* PCLK1 = HCLK, MAX = 36M*/
}
/* PLL configuration: PLLCLK = HSE * 18 = 144 MHz */
PLL_Temp = RCC->CFG;
PLL_Temp &= (uint32_t)((uint32_t) ~(RCC_CFG_PLLSRC | RCC_CFG_PLLHSEPRES | RCC_CFG_PLLMULFCT));
PLL_Temp |= (uint32_t)(RCC_CFG_PLLSRC_HSE | RCC_PLLMULL);
RCC->CFG = PLL_Temp;
/* Enable PLL */
RCC->CTRL |= RCC_CTRL_PLLEN;
/* Wait till PLL is ready */
while ((RCC->CTRL & RCC_CTRL_PLLRDF) == 0)
{
/* If this flag is always not set, it means PLL is failed, user can add here some code to deal with this error */
}
/* Select PLL as system clock source */
System_Temp = RCC->CFG;
System_Temp &= (uint32_t)((uint32_t) ~(RCC_CFG_SCLKSW));
System_Temp |= (uint32_t)RCC_CFG_SCLKSW_PLL;
RCC->CFG = System_Temp;
/* Wait till PLL is used as system clock source */
while ((RCC->CFG & (uint32_t)RCC_CFG_SCLKSTS) != (uint32_t)0x08)
{
/* If this bits are always not set, it means system clock select PLL failed, user can add here some code to deal with this error */
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
(3)因STOP2时,除 R-SRAM (16KB) 和备份寄存器外,其他 SRAM / 寄存器全丢,因而需要将栈重定向到R-SRAM位置,具体做法只修改 startup_n32g455.s的__initial_sp参数既可,如N32G455VEL7的 R-SRAM (16KB) 的空间地址为0x20020000 ~0x20024000, 栈大小为0x1000(4K) , 则__initial_sp可设置为0x20021000 ~0x20024000之间;
原程序(默认到SRAM):
Stack_Size EQU 0x00001000
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
修改后(重定向到R-SRAM):
Stack_Size EQU 0x00001000
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp EQU 0x20022000
注意bootloader程序也需要做相应的改动,否则会直接无法跳转到app,具体改动如下:
改前:
if((*(volatile uint32_t*)APPLICATION_ADDRESS & 0x2FFE0000) == 0x20000000)
改后:
if((*(volatile uint32_t*)APPLICATION_ADDRESS & 0x2FFC0000) == 0x20000000)
(4)需要进入BOOT2时,最好进行复位重启,通过备份域数据判断是否需要进入休眠:
if(BmsRTData.SleepFlag.All)
{
Bkp_WriteData(BKP_DAT(1), BmsRTData.SleepFlag.All);
uint16_t RdBkpDate = Bkp_ReadData(BKP_DAT(1));
if(RdBkpDate == BmsRTData.SleepFlag.All)
{
NVIC_SystemReset();
}
}
(5)休眠时,上电复位后,先读取备份域数据确认是否需要休眠,当进入STOP2休眠时,唤醒后需要将休眠标志重新写入备份域,再次重新复位,因进入STOP2模式SRAM数据会丢失;
void Sleep_PowerOn(uint8_t mode)
{
Rtc_Init();
BmsRTData.SleepFlag.All = Bkp_ReadData(BKP_DAT(1));
if(BmsRTData.SleepFlag.All)
{
Gpio_SleepInit();
Exti_Init();
Delay_ms(200);
Rtc_AlarmEnable(DISABLE);
Rtc_SetAlarm(20);
Rtc_AlarmEnable(ENABLE);
while(BmsRTData.SleepFlag.All)
{
Delay_ms(10);
DBG_ConfigPeriph(DBG_STOP, ENABLE);
SysTick_Disable();
Sleep_ToDeepMode(mode);
__enable_irq();
}
Rtc_Init();
Delay_ms(10);
Bkp_WriteData(BKP_DAT(1), BmsRTData.SleepFlag.All);
if(mode == SLEEP_STOP2)
{
NVIC_SystemReset();
}
}
}
(6)Sleep_ToDeepMode(mode)的函数定义如下:
void Mcu_SleepDeepMode(uint8_t Mode)
{
if(Mode == 2)
{
SetSysClockToHSI();
while(RCC_GetFlagStatus(RCC_FLAG_HSIRD) == RESET)
{
;
}
__disable_irq();
PWR_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
}
else
{
PWR_EnterSLEEPMode(1, PWR_STOPENTRY_WFI);
}
SYSCLKConfig_STOP(RCC_CFG_PLLMULFCT9);
}
3. RTC唤醒
休眠和RTC唤醒全过程:
(1)上电RTC初始化,只上电处理即可,可配置为LSI / LSE(独立时钟),进 STOP2 也不断电,继续跑;
(2)进入休眠前,配置ALARM唤醒时间,配置唤醒时间前要先关闭中断,配置完后再开启中断,如设置唤醒时间为20秒钟,代码如下:
(3)进入休眠模式;
(4)唤醒后重新初始化外设;
4. 详细代码如下:
(1) ALARM中断使能禁止函数:
void Mcu_RtcAlarmEnable(uint8_t En)
{
RTC_EnableAlarm(RTC_A_ALARM, En);
RTC_ConfigInt(RTC_INT_ALRA, En);
}
(2)重新设置时间:
static ErrorStatus Mcu_RtcTimeSet(void)
{
RTC_TimeType RTC_TimeStructure;
RTC_TimeType RTC_TimeRead = {0};
RTC_TimeStructure.H12 = RTC_AM_H12;
RTC_TimeStructure.Hours = 0;
RTC_TimeStructure.Minutes = 0;
RTC_TimeStructure.Seconds = 0;
if (RTC_ConfigTime(RTC_FORMAT_BIN, &RTC_TimeStructure) == ERROR)
{
return ERROR;
}
else
{
RTC_GetTime(RTC_FORMAT_BIN, &RTC_TimeRead);
return SUCCESS;
}
}
(3)设置告警间隔时间,单位为秒:
void Mcu_RtcSetAlarm(uint32_t AlarmTime)
{
RTC_AlarmType RTC_AlarmStructure;
RTC_AlarmType RTC_AlarmRead = {0};
Mcu_RtcTimeSet();
RTC_EnableAlarm(RTC_A_ALARM, DISABLE);
RTC_AlarmStructure.AlarmTime.H12 = RTC_AM_H12;
RTC_AlarmStructure.AlarmTime.Hours = 0;
RTC_AlarmStructure.AlarmTime.Minutes = 0;
RTC_AlarmStructure.AlarmTime.Seconds = AlarmTime;
RTC_AlarmStructure.DateWeekValue = 31;
RTC_AlarmStructure.DateWeekMode = RTC_ALARM_SEL_WEEKDAY_DATE;
RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_WEEKDAY | RTC_ALARMMASK_HOURS | RTC_ALARMMASK_MINUTES;
RTC_SetAlarm(RTC_FORMAT_BIN, RTC_A_ALARM, &RTC_AlarmStructure);
RTC_GetAlarm(RTC_FORMAT_BIN, RTC_A_ALARM, &RTC_AlarmRead);
RTC_ClrIntPendingBit(RTC_INT_ALRA);
EXTI_ClrITPendBit(EXTI_LINE17);
}
(4)休眠前RTC唤醒时间配置,如配置20秒唤醒:
Rtc_AlarmEnable(DISABLE);
Rtc_SetAlarm(20);
Rtc_AlarmEnable(ENABLE);
————————————————
版权声明:本文为CSDN博主「hrx2005」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hrx2005/article/details/161262112
|
|