Nuoeriris · 2021年01月28日

MM32F013x——RTC闹钟定时唤醒

RTC可用于周期性从低功耗模式下唤醒MCU,RTC可选三个时钟源:

  • 低功耗 32.768kHz 外部低速晶振 (LSE):该时钟源提供了一个低功耗且精确的时间基准。
  • 低功耗内部低速振荡器 (LSI):使用该时钟源,节省了一个 32.768kHz 晶振的成本,但是精度没有外部晶振的精度高。
  • 外部高速晶振(HSE)128分频:在某些低功耗模式中HSE被关闭会导致RTC无时钟源。

为了用 RTC 闹钟事件将系统从停机模式下唤醒,必须进行如下操作:

  • 配置外部中断线 17 为上升沿触发。
  • 配置 RTC 使其可产生 RTC 闹钟事件。

如果要从待机模式中唤醒, 不必配置外部中断线 17。

MM32F013x有三种低功耗模式:睡眠模式(Sleep Mode)、停机模式(Stop Mode)和待机模式(Standby Mode),三种低功耗模式的对比如下表所示:
1.png

从上表中可以看出,当MCU工作在停机模式时,可以通过任一外部中断事件来唤醒。MM32F013x低功耗模式下的功耗列表如下图所示:

2.png

MM32F013x有22个外部中断源,其中EXTI 17对应的是RTC闹钟事件,所以结合RTC闹钟的功能,本文将重点介绍如何在MM32F013x上通过内部RTC模块的闹钟事件来唤醒处于停机模式下的MCU。

1. 实现功能

通过内部RTC模块的闹钟事件(对应的是外部中断EXTI 17)来唤醒处于停机模式下的MCU。系统在进入停机模式时拉高GPIO端口,在恢复到正常运行状态时拉低GPIO端口。

2. 配置顺序

1)使能PWR和BKP时钟:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
2)使能后备寄存器访问:
PWR_BackupAccessCmd(ENABLE);
3)配置RTC时钟源,使能RTC时钟,如果使用LSE,要打开LSE:
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
4)设置RTC预分频系数:
RTC_SetPrescaler();
5)开启相关中断:
RTC_ITConfig(RTC_IT_ALR, ENABLE);
6)配置外部中断线:
EXTI_StructInit(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line    = EXTI_Line17;
EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
7)配置中断服务函数:
NVIC_InitStructure.NVIC_IRQChannel  = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
8)部分操作要等待写操作完成和同步。

3. 参考代码

3.1 RTC初始化配置:使用外部32.768kHz的晶振源
void RTC_Configure(void)
{
    uint16_t BKP_Value = 0x5A5A;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* Enable PWR Clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

    /* Enable Access To The RTC & Backup Registers */
    PWR_BackupAccessCmd(ENABLE);

    if(BKP_ReadBackupRegister(BKP_DR1) != BKP_Value)
    {
        BKP_DeInit();

        /* Enable LSE Clock Source */
        RCC_LSEConfig(RCC_LSE_ON);

        /* Wait LSI Clock Source Ready */
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);

        /* Config RTC Clock Source : LSE */
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

        /* Enable RTC Clock */
        RCC_RTCCLKCmd(ENABLE);

        /* Wait For Synchronization */
        RTC_WaitForSynchro();
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();

        /* Set The RTC Prescaler Value */
        RTC_SetPrescaler(32767);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
        /* Enable RTC Alarm Interrupt */
        RTC_ITConfig(RTC_IT_ALR, ENABLE);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
        /* Exit From The RTC Configuration Mode */
        RTC_ExitConfigMode();
        BKP_WriteBackupRegister(BKP_DR1, BKP_Value);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
    else
    {
        /* Wait For Synchronization */
        RTC_WaitForSynchro();
        /* Enable RTC Alarm Interrupt */
        RTC_ITConfig(RTC_IT_ALR, ENABLE);
        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
    /* Clear EXTI Line17 Flag */
    EXTI_ClearITPendingBit(EXTI_Line17);

   /* Configure EXTI Line17(RTC Alarm) To Generate An Interrupt On Rising Edge */
    EXTI_StructInit(&EXTI_InitStructure);
    EXTI_InitStructure.EXTI_Line    = EXTI_Line17;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /* Config RTC NVIC */
    NVIC_InitStructure.NVIC_IRQChannel  = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
3.2 RTC闹钟中断函数
void RTC_BKP_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
    {
        GPIO_WriteBit(LED_GPIO, LED_PIN, Bit_RESET);

        /* Clear EXTI Line17 Flag */
        EXTI_ClearITPendingBit(EXTI_Line17);

        /* Check If The Wake-Up Flag Is Set */
        if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET)
        {
            /* Clear Wake Up Flag */
            PWR_ClearFlag(PWR_FLAG_WU);
        }
        /* Clear Alarm Flag */
        RTC_ClearITPendingBit(RTC_IT_ALR);

        /* Wait Until Last Write Operation On RTC REG Has Finished */
        RTC_WaitForLastTask();
    }
}
3.3 设置RTC闹钟时间,系统进入STOP模式
void RTC_EntryStopMode(void)
{    /* Wait till RTC Second event occurs */
    RTC_ClearFlag(RTC_FLAG_SEC);
    while(RTC_GetFlagStatus(RTC_FLAG_SEC) == RESET);

    /* Set The RTC Alarm Value */
    RTC_SetAlarm(RTC_GetCounter() + 3);

    /* Wait Until Last Write Operation On RTC REG Has Finished */
    RTC_WaitForLastTask();

    GPIO_WriteBit(LED_GPIO, LED_PIN, Bit_SET);

    /* Enter Stop Mode */
    PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
}

4. 运行结果

编译软件工程无误后下载代码,调用RTC\_EntryStopMode()函数使系统进入到停机模式,等待RTC闹钟事件3S后唤醒停机模式下的MCU,唤醒后继续执行程序,通过观察GPIO的电平状态来查看运行结果:

3.png

用户可以结合上一篇万年历功能设置在某年某月某时某刻定时唤醒MCU功能,万年历的具体实方式可以参考上一篇《MM32F013x——万年历》。

推荐阅读
关注数
6152
内容数
276
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息