[应用相关] N32G4xx单片机休眠以及RTC唤醒问题

[复制链接]
14|0
Puchou 发表于 2026-6-8 14:59 | 显示全部楼层 |阅读模式
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

您需要登录后才可以回帖 登录 | 注册

本版积分规则

202

主题

523

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0