GD32实战14__RTC

目的

​ 虽然RTC简单,但是其牵扯的内容却蛮多的,例如时钟控制单元,电源控制,备份寄存器,最主要的目的还是想把下面3章引出来。

原理

​ RTC(Real_Time Clock)实时时钟,用于得到年、月、日、时、分、秒等时间日期信息。目前几乎已经是统一标准了,如图,32.768K经过15次分频后,恰好是1秒,其它时间只要在1秒频率下计数即可,RTC本质上就是一个1秒计数器。为了方便程序使用,内部会转化成年月日时分秒格式存储,并提供通信接口。在这里插入图片描述

​ GD32内置RTC简化了上面的逻辑,直接使用一个32位计数器(2个16bit寄存器拼起来的)存储秒数,例如,1970年1月1号21点30分54秒=0x0+0x0+0x0+21x60x60+30×60+54=77454s,直接写入32位计数器中累加,只要读出该寄存器秒数,再反向运算也就知道日期时间了。

​ 我们经常会遇到下面的需求,

  1. 希望当MCU复位、异常重启时,时间不会丢
  2. 希望MCU断电重启时,时间不会丢
  3. 希望当到达某个时间后,唤醒MCU,其它时间MCU能处于低功耗状态

为此,

1. GD32将RTC分成两部分,把内核部分(预分频器、分频器、计数器、闹钟)放在备份域(后面章节会详解介绍),达到复位重启不丢时间的目的,其它(APB1接口)放在VDD电源域(电源控制章节详细介绍)跟随系统复位初始化,如下1图
2. 增加电池,当VDD断电后,自动切换到电池供电(VBAT),达到MCU断电不丢时间的目的,如下2图
3. 支持各种闹钟,各种中断,直接挂在NVIC上,用于中断响应、唤醒等功能

在这里插入图片描述在这里插入图片描述

功能设计

实现一个时钟,具有如下功能

  1. 每秒在串口输出当前时间,格式xxxx-xx-xx xx:xx:xx
  2. 首次上电时,需要配置时间,串口输入
  3. 复位,断电都不会丢失时间

代码如下:

  1. 判断是否首次上电,此处用到了备份域的知识,详细参考下面备份域章节

    /* TRUE 第一次启动 */
    BOOL DRV_POWER_IsFirstBoot(VOID)
    {
        if (POWER_FIRSTFLAG_VALUE != BKP_ReadBackupRegister(POWER_FIRSTFLAG_REG))
        {
            RCC_APB1PeriphClock_Enable(RCC_APB1PERIPH_PWR | RCC_APB1PERIPH_BKP, ENABLE);
            PWR_BackupAccess_Enable(ENABLE);
            BKP_DeInit();
            BKP_WriteBackupRegister(POWER_FIRSTFLAG_REG, POWER_FIRSTFLAG_VALUE);
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    
  2. 首次上电,需要初始化RTC,就三步

    1. 选择时钟源,如图有三个选择,代码配置的是第二路,在这里插入图片描述

    2. 配置分频,最终得到1Hz的频率供RTC计数器使用

      static RTC_FirstInit(VOID)
      {
          DRV_TRACE("First power on need to configure RTC.");
          
          /* 选择晶振LSE,低速外部晶振,即32.768Khz */
          RCC_RTCCLKConfig(RCC_RTCCLKSOURCE_LSE);
          RCC_LSEConfig(RCC_LSE_EN);
          while (RCC_GetBitState(RCC_FLAG_LSESTB) == RESET)
          {
          }
          RCC_RTCCLK_Enable(ENABLE);
          RTC_WaitRSF();
          RTC_WaitLWOFF();
      
          /* 分频,即1Hz */
          RTC_SetPrescaler(32768-1); /* 1s */
          RTC_WaitLWOFF();
      }
      
    3. 通过串口设置日期时间,并配置到RTC寄存器中

    static time_t RTC_SetTime(VOID)
    {
        U32 year = 0xFF; 
        U32 mon = 0xFF; 
        U32 day = 0xFF;
        U32 hour = 0xFF; 
        U32 min = 0xFF; 
        U32 sec = 0xFF;
        struct tm t;
    
        memset(&t, 0, sizeof(t));
        printf("Please Set Time:\r\n");
        printf("Please input year:\r\n");
        scanf("%u", &year);
        printf("year:%u\r\n", year);
        
        printf("Please input mon:\r\n");
        scanf("%u", &mon);
        printf("mon:%u\r\n", mon);
        
        printf("Please input day:\r\n");
        scanf("%u", &day);
        printf("day:%u\r\n", day);
        
        printf("Please input hour:\r\n");
        scanf("%u", &hour);
        printf("hour:%u\r\n", hour);
        
        printf("Please input min:\r\n");
        scanf("%u", &min);
        printf("min:%u\r\n", min);
        
        printf("Please input sec:\r\n");
        scanf("%u", &sec);
        printf("sec:%u\r\n", sec);
        
        t.tm_sec = sec;
        t.tm_min = min;
        t.tm_hour = hour;
        t.tm_mday = day;
        t.tm_mon = mon-1; /* 0-11 */
        t.tm_year = year-1900; /* 从1900年开始开始计算的 */
    
        APP_DEBUG("%u-%u-%u %u:%u:%u", t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
        return(mktime(&t));
    }
    
    VOID DRV_RTC_TimeAdjust(IN U32 sec)
    {
        RTC_WaitLWOFF();
        RTC_SetCounter(sec);
        RTC_WaitLWOFF();
    }
    
  3. 非首次上电,不需要重新配置RTC,只需要等待AHB接口时钟同步即可,因为RTC内核在备份域,AHB接口VDD供电,所以RTC内核配置不会断电,AHB接口需要同步

    static VOID RTC_NotFristInit(VOID)
    {
        DRV_TRACE("Just need wait clock synchronized.");
        RTC_WaitRSF();
    }
    
  4. 获取并显示时间

    #define time DRV_RTC_GetTime
    time_t DRV_RTC_GetTime(time_t *timer)
    {
        U32 c = 0;
        c = RTC_GetCounter();
        if (timer != NULL)
        {
            *timer = c;
        }
    
        return c;
    }
    
    VOID APP_RTC_Test(VOID)
    {
        BOOL firstBootFlag = FALSE; 
        time_t now = 0;
    
        DRV_POWER_DumpBootReason();
        
        firstBootFlag = DRV_POWER_IsFirstBoot();
        DRV_RTC_Init(firstBootFlag);
        if (TRUE == firstBootFlag)
        {
            DRV_RTC_TimeAdjust(RTC_SetTime());
        }
        
        while(1)
        {
            APP_Delay(1000);
            now = time(NULL);
            printf("%s", ctime(&now));
        }
    }
    

代码路径

https://github.com/YaFood/GD32F103/tree/master/TestRTC

猜你喜欢

转载自blog.csdn.net/qq_17854661/article/details/91955212
今日推荐