[其他] stm32f1 cubeMX RTC 掉电后日期丢失的问题

[复制链接]
1365|9
 楼主| 键盘手没手 发表于 2022-2-28 16:34 | 显示全部楼层 |阅读模式
AD, AC, sd, CubeMX, mx, tc
f1系列的RTC只是一个32位的秒计数器,而HAL库中并没有将秒计数值转换成日期,而是在上电的时候粗暴地重新赋值。

本文旨在简单方便地解决日期丢失的问题(注:stm32cube FW_F1 V1.6.0 代码在正点原子例程的基础上修改)

前提:板子上备用电池能正常使用,工程使用cubeMX正确配置(可参照微雪课堂)了!

Table of Contents

步骤一:在rtc.h文件中添加如下代码:

步骤二:在rtc.c文件开始添加这些代码:

步骤三:注释掉rtc.c文件中MX_RTC_Init函数中设置时间和日期部分:

步骤四:怎么设置时间和获取时间

 楼主| 键盘手没手 发表于 2022-2-28 16:37 | 显示全部楼层
步骤一:在rtc.h文件中添加如下代码:
【添加到/* USER CODE BEGIN Private defines */和/* USER CODE END Private defines */之间】
  1. typedef struct
  2. {
  3.         __IO uint8_t hour;
  4.         __IO uint8_t min;
  5.         __IO uint8_t sec;                       
  6.         //公历日月年周
  7.         __IO uint16_t w_year;
  8.         __IO uint8_t  w_month;
  9.         __IO uint8_t  w_date;
  10.         __IO uint8_t  week;       
  11. }_calendar_obj;       
  12. extern _calendar_obj calendar;                                //日历结构体
 楼主| 键盘手没手 发表于 2022-2-28 16:38 | 显示全部楼层
【添加到/* USER CODE BEGIN Prototypes */和/* USER CODE END Prototypes */之间】
  1. void rtc_init_user(void);
  2. void RTC_Set(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec);
  3. void RTC_Get(void);
  4. uint8_t Is_Leap_Year(uint16_t year);
  5. uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day);
 楼主| 键盘手没手 发表于 2022-2-28 21:45 | 显示全部楼层
步骤二:在rtc.c文件开始添加这些代码:
【添加到/* USER CODE BEGIN 0 */和/* USER CODE END 0 */之间】
  1. _calendar_obj calendar;//时钟结构体
  2. //月份数据表                                                                                         
  3. const uint8_t table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表         
  4. //平年的月份日期表
  5. const uint8_t mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
 楼主| 键盘手没手 发表于 2022-2-28 22:01 | 显示全部楼层
【添加到/* USER CODE BEGIN 1 */和/* USER CODE END 1 */之间】
  1. void rtc_init_user(void)
  2. {       
  3.         HAL_RTCEx_SetSecond_IT(&hrtc);//秒中断使能,没有配置这个中断可以不加
  4.         if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!=0x5050)//是否第一次配置
  5.         {
  6.                 RTC_Set(2019,4,7,16,28,0); //设置日期和时间                                                          
  7.                 HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0x5050);//标记已经初始化过了
  8.                  printf("---first Time---\n");
  9.         }
  10.         RTC_Get();//更新时间
  11. }

  12. void RTC_Set(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec)
  13. {
  14.         uint16_t t;
  15.         uint32_t seccount=0;
  16.        
  17.         if(syear<1970||syear>2099)        return;
  18.         for(t=1970;t<syear;t++)        //把所有年份的秒钟相加
  19.         {
  20.                 if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
  21.                 else seccount+=31536000;                          //平年的秒钟数
  22.         }
  23.         smon-=1;
  24.         for(t=0;t<smon;t++)           //把前面月份的秒钟数相加
  25.         {
  26.                 seccount+=(uint32_t)mon_table[t]*86400;//月份秒钟数相加
  27.                 if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数          
  28.         }
  29.         seccount+=(uint32_t)(sday-1)*86400;//把前面日期的秒钟数相加
  30.         seccount+=(uint32_t)hour*3600;//小时秒钟数
  31.     seccount+=(uint32_t)min*60;         //分钟秒钟数
  32.         seccount+=sec;//最后的秒钟加上去       

  33.         //设置时钟
  34.     RCC->APB1ENR|=1<<28;//使能电源时钟
  35.     RCC->APB1ENR|=1<<27;//使能备份时钟
  36.         PWR->CR|=1<<8;    //取消备份区写保护
  37.         //上面三步是必须的!
  38.         RTC->CRL|=1<<4;   //允许配置
  39.         RTC->CNTL=seccount&0xffff;
  40.         RTC->CNTH=seccount>>16;
  41.         RTC->CRL&=~(1<<4);//配置更新
  42.         while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
  43.        
  44.         RTC_Get();//设置完之后更新一下数据           
  45. }

  46. void RTC_Get(void)
  47. {
  48.         static uint16_t daycnt=0;
  49.         uint32_t timecount=0;
  50.         uint32_t temp=0;
  51.         uint16_t temp1=0;          
  52.         timecount=RTC->CNTH;//得到计数器中的值(秒钟数)
  53.         timecount<<=16;
  54.         timecount+=RTC->CNTL;                         

  55.         temp=timecount/86400;   //得到天数(秒钟数对应的)
  56.         if(daycnt!=temp)//超过一天了
  57.         {          
  58.                 daycnt=temp;
  59.                 temp1=1970;        //从1970年开始
  60.                 while(temp>=365)
  61.                 {                                 
  62.                         if(Is_Leap_Year(temp1))//是闰年
  63.                         {
  64.                                 if(temp>=366)temp-=366;//闰年的秒钟数
  65.                                 else break;  
  66.                         }
  67.                         else temp-=365;          //平年
  68.                         temp1++;  
  69.                 }   
  70.                 calendar.w_year=temp1;//得到年份
  71.                 temp1=0;
  72.                 while(temp>=28)//超过了一个月
  73.                 {
  74.                         if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
  75.                         {
  76.                                 if(temp>=29)temp-=29;//闰年的秒钟数
  77.                                 else break;
  78.                         }
  79.                         else
  80.                         {
  81.                                 if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
  82.                                 else break;
  83.                         }
  84.                         temp1++;  
  85.                 }
  86.                 calendar.w_month=temp1+1;        //得到月份
  87.                 calendar.w_date=temp+1;          //得到日期
  88.         }
  89.         temp=timecount%86400;                     //得到秒钟数             
  90.         calendar.hour=temp/3600;             //小时
  91.         calendar.min=(temp%3600)/60;         //分钟       
  92.         calendar.sec=(temp%3600)%60;         //秒钟
  93.         calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   
  94. }       

  95. //判断是否是闰年函数
  96. //月份   1  2  3  4  5  6  7  8  9  10 11 12
  97. //闰年   31 29 31 30 31 30 31 31 30 31 30 31
  98. //非闰年 31 28 31 30 31 30 31 31 30 31 30 31
  99. //year:年份
  100. //返回值:该年份是不是闰年.1,是.0,不是
  101. uint8_t Is_Leap_Year(uint16_t year)
  102. {                          
  103.         if(year%4==0) //必须能被4整除
  104.         {
  105.                 if(year%100==0)
  106.                 {
  107.                         if(year%400==0)return 1;//如果以00结尾,还要能被400整除           
  108.                         else return 0;   
  109.                 }else return 1;   
  110.         }else return 0;       
  111. }       

  112. //获得现在是星期几
  113. //功能描述:输入公历日期得到星期(只允许1901-2099年)
  114. //year,month,day:公历年月日
  115. //返回值:星期号                                                                                                                                                                                 
  116. uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day)
  117. {       
  118.         uint16_t temp2;
  119.         uint8_t yearH,yearL;
  120.        
  121.         yearH=year/100;        yearL=year%100;
  122.         // 如果为21世纪,年份数加100  
  123.         if (yearH>19)yearL+=100;
  124.         // 所过闰年数只算1900年之后的  
  125.         temp2=yearL+yearL/4;
  126.         temp2=temp2%7;
  127.         temp2=temp2+day+table_week[month-1];
  128.         if (yearL%4==0&&month<3)temp2--;
  129.         return(temp2%7);
  130. }
 楼主| 键盘手没手 发表于 2022-2-28 22:12 | 显示全部楼层
步骤三:注释掉rtc.c文件中MX_RTC_Init函数中设置时间和日期部分:
 楼主| 键盘手没手 发表于 2022-2-28 22:13 | 显示全部楼层
    /**Initialize RTC and set the Time and Date
    */
//  sTime.Hours = 0x12;
//  sTime.Minutes = 0x0;
//  sTime.Seconds = 0x0;

//  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
//  {
//    _Error_Handler(__FILE__, __LINE__);
//  }

//  DateToUpdate.WeekDay = RTC_WEEKDAY_SUNDAY;
//  DateToUpdate.Month = RTC_MONTH_APRIL;
//  DateToUpdate.Date = 0x7;
//  DateToUpdate.Year = 0x19;

//  if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
//  {
//    _Error_Handler(__FILE__, __LINE__);
//  }
 楼主| 键盘手没手 发表于 2022-2-28 22:17 | 显示全部楼层
步骤四:怎么设置时间和获取时间:
【设置时间】

  1. RTC_Set(2019,4,7,16,28,0); //设置日期和时间
 楼主| 键盘手没手 发表于 2022-2-28 22:18 | 显示全部楼层
【获取时间】
  1. RTC_Get();
  2. printf("%04d/%02d/%02d ",calendar.w_year, calendar.w_month, calendar.w_date);
  3. printf("%02d:%02d:%02d",calendar.hour, calendar.min, calendar.sec);
 楼主| 键盘手没手 发表于 2022-2-28 22:19 | 显示全部楼层
缺点:重新用cubeMX生成工程的时候,需要重复步骤三。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

104

主题

1260

帖子

0

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