基于STM32的F1的RTC实时时钟+CUBEMX实时获取时间戳实现断电跨天同步时间戳

目录

问题描述

实时获取时间戳断电跨日期时间戳同步


问题描述

        今需要把STM32所采集的数据带日期的形式发送到后端服务器上进行处理,由于STM32F103本身是自带有实时时钟的。该RTC(实时时钟)是一个不断递增的计数器,断电后由纽扣电池继续供电计数。奈何它没有万年历的功能,再加上用CUBEMX生成的hal库有一些相同的问题。这些问题总结起来就是:CUBEMX为STM32F103生成的hal库在获取日期和时间时会把时间戳(也就是计数器里的计数值单位为秒)转换成24h内的时间戳,(CUBEMX为F1生成的hal库就只是把实时时间戳为一天的时间戳)最大也就是24*3600s。我感觉可能是hal库并未完善这点。别的型号的芯片没有测试过。本文只针对于STM32F1论述。

看看CUBEMX配置后生成的hal库函数

在设置时分秒的库函数中

HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)

 

 

它只把时分秒转成了时间戳(24小时内)

在获取时分秒的库函数中

 

HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)

这里更加明显,但时间戳大于24h的时间戳则又转换成24内的时间戳。。

诸如此类的代码在 中都有体现。

HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)

        另外在设置日期的库函数 HAL_RTC_SetDate()中并未把年月日转换成时间戳,这就导致尽管我们设置了年月日,尽管我们在后备寄存器写入了年月日,但跨天依然不会更新,读到的后备寄存器也只是原来的日期因为RTC仅仅只是一个计数器,只是通过秒中断函数不断递增然后把递增的数据写进寄存器。

那么怎么解决这个日期的问题,实现跨日期也能达到日期同步,由于我本篇是记录的实时更新时间戳,

提供思路:

  1. 首先是能实现实时获取时间戳达到断电跨日期时间戳同步,这也就是我本篇文章的目的。
  2. 达到这个之后剩下的也就是自己写算法把时间戳转换成年月日 时分秒,然后自己手写获取日期,获取时分秒的函数。为什么要自己手写?因为hal库上面提供的接口都会改变我们现有的时间戳,只有时间戳才是产生日期的根本,这点是不能改变的。当然如果也想用原来hal库的代码那就只能改hal库的,这不推荐。遵循只增加代码不修改源代码的开闭原则。另外cubemx每次刷新都会覆盖你再hal库文件中修改的代码。。。

实时获取时间戳断电跨日期时间戳同步

CUBEMX配置

 

使用LSE外部时钟更加准确。日期格式使用BCD码 。后面配置的日期后面都会用自己的代码覆盖。

进入void MX_RTC_Init(void)  ,覆盖原代码如下

 

void MX_RTC_Init(void)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef DateToUpdate = {0};

  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
  hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN Check_RTC_BKUP */
      

            __HAL_RCC_BKP_CLK_ENABLE();       //开启后备区域时钟
            __HAL_RCC_PWR_CLK_ENABLE();		  //开启电源时钟
 
 
        
  
        if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!= 0x5011)
        {
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5011);//向指定的后备区域寄存器写入数据
          
            
             uint32_t time=   GetCurrentTimeStamp(); //获取设置时间的时间戳
            printf("time:%d\r\n",time);
             MY_RTC_WriteTimeCounter(&hrtc, time);
    
                  __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);	 //开启RTC时钟秒中断    
                           
              }
            else
            {
 
                __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);	 //开启RTC时钟秒中断		
            }  

            return ;

步骤 

  1. 初始化RTC,开启对应时钟(电源时钟和后备区时钟)
  2. 通过向寄存器任意写一个值判断是否是第一次初始化(也就是我设置时间的初始化)。第二次复位重启后因为读出后备寄存器已经写入该值故不需要再次初始化。最后再开启秒中断,使得每次RTC计数器递增都能写进寄存器。
  3. 获取我们所设置的时间并把它转换成时间戳。
  4. 把我们转换后的时间戳写入寄存器。

 中间调用两个自己的函数

GetCurrentTimeStamp(); //获取设置时间的时间戳

这函数在第一次初始化的时候算出所设置时间的时间戳。

uint32_t GetCurrentTimeStamp(void) {

    //基准时间 2022年 1月 1日 0 时 -分 0秒 以下省略 时分秒
    uint16_t base_year = 2022;
    uint8_t base_month = 1;
    uint8_t base_day = 1;
    //设置时间 2022年 10月8日  时间戳=基准时间戳+相差时间戳
    uint16_t set_year = 2022;
    uint8_t set_month = 10;
    uint8_t set_day = 8;
    uint8_t set_h = 22;//设置时
    uint8_t set_m = 02; //设置分


    uint32_t _time = 1640966400;//2022年1月1日与1970 年1月1日相差时间戳

    //对齐年
    for (uint16_t i = base_year; i < set_year; i++) {

      

            if ((0 == i % 4 && i % 100 != 0 )|| (0 == i % 400)) {
                _time += 31536000;//平年365天时间戳
                _time += (24  * 3600);//多一天

            }
            else
                _time += 31536000;

    }

    //对齐月
    uint8_t montharr[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };//以平年算月份
    for (int i = 1; i < set_month; i++) {
       
        if (i == 2 && ((0 == set_year % 4 && set_year % 100 != 0) || (0 == set_year % 400)))//设置年是闰年
            _time += 29 * 24 * 3600;
        else
            _time += montharr[i - 1] * 24 * 3600;

    }


    //对齐日和分
    _time += (set_day - 1) * 24 * 3600;
    _time += set_h * 3600;
    _time += set_m * 60;

    return _time;



}


写入时间戳 

MY_RTC_WriteTimeCounter(&hrtc, time);

实际上在stm32f1xx_hal_rtc.c中有一个已经实现了的函数,奈何hal库把它定义成static,只在当前文件有效,且该函数中又调用其他的函数(且也被static修饰在本文件有效)。所以。在stm32f1xx_hal_rtc.c中写一个自己的函数调用原库里的这个函数。

原hal库里的这个写入寄存器的函数。 

自己加的一个函数(写入时间戳

void MY_RTC_WriteTimeCounter(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter){

        RTC_WriteTimeCounter(hrtc, TimeCounter);
  
}

在rtc.c中进行声明就能正常调用。

测试

 

结束,断电重启依然保留且持续更新。

附上设置时间戳并校验的一个工具

在线时间戳转换工具 (beijing-time.org)

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值