STM32 HAL库RTC复位丢失年月日的解决办法

STM32 HAL库RTC复位丢失年月日的解决办法

  • 0.前言
  • 一、实现方式
    • 1.CubeMX配置:
    • 2.MX_RTC_Init()函数修改
    • 2.编写手动解析函数
  • 二、总结


参考文章:stm32f1 cubeMX RTC 掉电后日期丢失的问题

0.前言

  最近在使用STM32F103做RTC实验时,发现RTC复位后时间正常,但是日期丢失的问题。这个问题在之前使用的标准库中没有遇到,说明是HAL的bug,经过对HAL_RTC_SetDate()和HAL_RTC_GetDate()的解析之后,发现在上电初始化时,HAL库直接简单粗暴的对日期时间戳进行了重置,导致无法读取。
  经过多方查找,目前使用较多且较简单的方法是:直接将日期数据写入备份寄存器中,上电时重新进行获取。但是这种方法存在一个问题,假如掉电后经历了日期跨越,那么日期数据就不再准确。所以这里参考标准库的实现方式,直接将HAL库中的初始化过程注释掉,直接从RTC的时间戳寄存器中获取数据,然后手动进行解析。

一、实现方式

1.CubeMX配置:

在这里插入图片描述
直接使能RTC功能即可,日期可以不进行设置,后续手动进行设置。

2.MX_RTC_Init()函数修改

为了尽量保持CubeMX的生成格式,防止后续重新生成时自己的代码被覆盖,这里直接在MX_RTC_Init()函数中,使用宏定义注释掉HAL的日期初始化流程:
在这里插入图片描述
将添加的宏定义放在 USER CODE BEGIN 和 USER CODE END之间,即可保证重新生成时不被刷新。

2.编写手动解析函数

rtc.h:
在头文件中添加以下代码:
在这里插入图片描述
其中包括日历相关的结构体定义,以及日历的全局变量。手动初始化、设置日期、获取日期的相关函数。

rtc.c
在rtc的相关的功能代码中,添加以下代码段:

/* USER CODE BEGIN 0 */
_calendar_obj calendar; // 时钟结构体
// 月份数据表
const uint8_t table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5}; // 月修正数据表
// 平年的月份日期表
const uint8_t mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};// 判断是否是闰年函数
// 月份   1  2  3  4  5  6  7  8  9  10 11 12
// 闰年   31 29 31 30 31 30 31 31 30 31 30 31
// 非闰年 31 28 31 30 31 30 31 31 30 31 30 31
// year:年份
// 返回值:该年份是不是闰年.1,是.0,不是
static uint8_t Is_Leap_Year(uint16_t year)
{if (year % 4 == 0) // 必须能被4整除{if (year % 100 == 0){if (year % 400 == 0)return 1; // 如果以00结尾,还要能被400整除elsereturn 0;}elsereturn 1;}elsereturn 0;
}// 获得现在是星期几
// 功能描述:输入公历日期得到星期(只允许1901-2099年)
// year,month,day:公历年月日
// 返回值:星期号
static uint8_t RTC_Get_Week(uint16_t year, uint8_t month, uint8_t day)
{uint16_t temp2;uint8_t yearH, yearL;yearH = year / 100;yearL = year % 100;// 如果为21世纪,年份数加100if (yearH > 19)yearL += 100;// 所过闰年数只算1900年之后的temp2 = yearL + yearL / 4;temp2 = temp2 % 7;temp2 = temp2 + day + table_week[month - 1];if (yearL % 4 == 0 && month < 3)temp2--;return (temp2 % 7);
}void RTC_Set(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{uint16_t t;uint32_t seccount = 0;if (syear < 1970 || syear > 2099)return;for (t = 1970; t < syear; t++) // 把所有年份的秒钟相加{if (Is_Leap_Year(t))seccount += 31622400; // 闰年的秒钟数elseseccount += 31536000; // 平年的秒钟数}smon -= 1;for (t = 0; t < smon; t++) // 把前面月份的秒钟数相加{seccount += (uint32_t)mon_table[t] * 86400; // 月份秒钟数相加if (Is_Leap_Year(syear) && t == 1)seccount += 86400; // 闰年2月份增加一天的秒钟数}seccount += (uint32_t)(sday - 1) * 86400; // 把前面日期的秒钟数相加seccount += (uint32_t)hour * 3600;        // 小时秒钟数seccount += (uint32_t)min * 60;           // 分钟秒钟数seccount += sec;                          // 最后的秒钟加上去// 设置时钟RCC->APB1ENR |= 1 << 28; // 使能电源时钟RCC->APB1ENR |= 1 << 27; // 使能备份时钟PWR->CR |= 1 << 8;       // 取消备份区写保护// 上面三步是必须的!RTC->CRL |= 1 << 4; // 允许配置RTC->CNTL = seccount & 0xffff;RTC->CNTH = seccount >> 16;RTC->CRL &= ~(1 << 4); // 配置更新while (!(RTC->CRL & (1 << 5))); // 等待RTC寄存器操作完成RTC_Get(); // 设置完之后更新一下数据
}void RTC_Get(void)
{static uint16_t daycnt = 0;uint32_t timecount = 0;uint32_t temp = 0;uint16_t temp1 = 0;timecount = RTC->CNTH; // 得到计数器中的值(秒钟数)timecount <<= 16;timecount += RTC->CNTL;temp = timecount / 86400; // 得到天数(秒钟数对应的)if (daycnt != temp)       // 超过一天了{daycnt = temp;temp1 = 1970; // 从1970年开始while (temp >= 365){if (Is_Leap_Year(temp1)) // 是闰年{if (temp >= 366)temp -= 366; // 闰年的秒钟数elsebreak;}elsetemp -= 365; // 平年temp1++;}calendar.w_year = temp1; // 得到年份temp1 = 0;while (temp >= 28) // 超过了一个月{if (Is_Leap_Year(calendar.w_year) && temp1 == 1) // 当年是不是闰年/2月份{if (temp >= 29)temp -= 29; // 闰年的秒钟数elsebreak;}else{if (temp >= mon_table[temp1])temp -= mon_table[temp1]; // 平年elsebreak;}temp1++;}calendar.w_month = temp1 + 1; // 得到月份calendar.w_date = temp + 1;   // 得到日期}temp = timecount % 86400;                                                         // 得到秒钟数calendar.hour = temp / 3600;                                                      // 小时calendar.min = (temp % 3600) / 60;                                                // 分钟calendar.sec = (temp % 3600) % 60;                                                // 秒钟calendar.week = RTC_Get_Week(calendar.w_year, calendar.w_month, calendar.w_date); // 获取星期
}void rtc_init_user(void)
{//HAL_RTCEx_SetSecond_IT(&hrtc);                        // 秒中断使能,没有配置这个中断可以不加if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050) // 是否第一次配置{RTC_Set(2022, 3, 9, 20, 58, 0);                  // 设置日期和时间HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050); // 标记已经初始化过了}RTC_Get(); // 更新时间
}
/* USER CODE END 0 */

然后在主程序中,即可设置和获取时间信息,并且掉电后不会丢失。
在这里插入图片描述
在这里插入图片描述

二、总结

  HAL库RTC问题,主要就是在HAL_RTC_SetDate()和HAL_RTC_GetDate()这两个函数中,对日期相关的数据处理不当,粗暴的将日期的进位舍去了。修改的原理也很简单,获取到真实时间戳后手动解析即可,在笔者 的实现方式中,则主要对应RTC_Get()和RTC_Set()函数,这里笔者使用的函数还存在限制,对星期几的解析只能计算到2099年,其实这里还有更简单的方法,直接使用 time.h 中提供的时间戳处理方法,修改这两个函数进行日期的计算和解析,有兴趣的读者可以自行尝试(需要在Keil中使能MicroLib库)。不过肯定比通过日期备份的方式更加合理可靠。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/525283.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浏览器工作原理与Javascript高级(前后端异步)

总体介绍 浏览器运行是多进程的&#xff0c;包括主进程、渲染进行、网络进程和GPU进程等等 解析HTML时(渲染进程)用到两大引擎&#xff0c;一个是渲染引擎&#xff08;用于渲染页面&#xff09;、一个是JS引擎用于解析JS代码。又JS引擎运行是单线程的&#xff0c;所以渲染和 …

基于Kronig-Penney能带模型的MATLAB求解与仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于Kronig-Penney能带模型的MATLAB求解与仿真.综合利用 MATLAB提供的求解常微分方程、矩阵行列式、代数表达式化简及绘图等函数 ,可使 Kronig-Penney能带模型分析…

Roguelike游戏经久不衰,到底有什么魔力?

Roguelike作为诸多游戏类型之一&#xff0c;多年来一直蓬勃发展&#xff0c;深受广大玩家们的喜爱。 近几年游戏市场Roguelike游戏爆款频出&#xff0c;从经典的《以撒的结合》开始&#xff0c;到《杀戮尖塔》、《哈迪斯》、《元气骑士》&#xff0c;再到现在的爆款《吸血鬼幸存…

生成对抗网络 (GAN)

生成对抗网络&#xff08;Generative Adversarial Networks&#xff0c;GAN&#xff09;是由Ian Goodfellow等人在2014年提出的一种深度学习模型。GAN由两部分组成&#xff1a;一个生成器&#xff08;Generator&#xff09;和一个判别器&#xff08;Discriminator&#xff09;&…

【ARM Coresight | AMBA BUS | Cache | CoreLink | GCC 专栏导读】

请阅读【嵌入式开发学习必备专栏 】 文章目录 1. ARM Coresight SoC-400/SoC-600 专栏导读目录1.1 ARM Coresight 专题1.1.1 Performance Profiling1.1.2 ARM Coresight Debug 工具系列1.1.2.1 ARM DS5 系列1.1.2.2 劳特巴赫 Trace32 系列1.1.2.3 JTAG OpenOCD 系列 1.2 ARM Ca…

这是谁的女儿?其母亲早已红过头了,现在小小年纪的她也爆红网络,没想到吧?

这是谁的女儿&#xff1f;其母亲早已红过头了&#xff0c;现在小小年纪的她也爆红网络&#xff0c;没想到吧&#xff1f; 原来&#xff0c;作母亲的她在红极一时后似乎沉寂了下来&#xff0c;没想到她11岁的女儿近年来也在社交媒体上走红&#xff0c;她为何也成了小网红呢&…

git分布式管理-头歌实验搭建Git服务器

一、Git服务器搭建 任务描述 虽然有提供托管代码服务的公共平台&#xff0c;但是对一部分开发团队来说&#xff0c;为了不泄露项目源代码、节省费用及为项目提供更好的安全保护&#xff0c;往往需要搭建私有Git服务器用做远程仓库。Git服务器为团队的开发者们&#xff0c;提供了…

Microsoft office Word和有道云写的笔记复制粘贴到csdn,图片加载失败的具体解决方法

由于CSDN的博客接口关闭&#xff08;可能是这个原因&#xff09; 此方法失效&#xff0c;之后找了一个新的方法如下&#xff1a; 1.有道云笔记&#xff1a;转为word格式 2.打开火狐浏览器&#xff0c;即可从Microsoft office Word粘贴文章到CSDN。

蓝桥集训之序列

蓝桥集训之序列 核心思想&#xff1a;多路归并 每次将两个序列合并 –> 两序列n2个和中最小的n个 构成新序列 第一行都是加b1 每次在最外面的元素中取最小(优先队列) #include<iostream>#include<algorithm>#include<cstring>#include<queue>#incl…

kamailio转发电话到目的地,目的返回失败时再转给其他IP

按图中这样测试&#xff1a; A---->kamailio------->B B返回480等失败错误码&#xff08;非200 OK&#xff09;&#xff0c;能进入failure_route[TOVOICEMAIL]&#xff0c;但是t_relay_to_udp执行失败。 好吧&#xff0c;说是&#xff1a;在 failure_route 中处理的是…

Pytorch学习 day09(简单神经网络模型的搭建)

简单神经网络模型的搭建 针对CIFAR 10数据集的神经网络模型结构如下图&#xff1a; 由于上图的结构没有给出具体的padding、stride的值&#xff0c;所以我们需要根据以下公式&#xff0c;手动推算&#xff1a; 注意&#xff1a;当stride太大时&#xff0c;padding也会变得很大…

CorelDRAW Standard2024适合业余爱好者和家庭企业的图形设计软件

CorelDRAW Standard 2024是一款功能强大的矢量图形设计软件&#xff0c;专为图形爱好者、家庭用户、微型企业和学生们设计。该软件在Windows平台上运行&#xff0c;并提供了智能对象、布局、插图和模板等功能&#xff0c;帮助用户快速创建高质量的设计作品。 CorelDRAW Standa…