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.mzph.cn/news/732716.shtml

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

相关文章

基于Java的物管系统设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术介绍 3 1.1 JSP介绍 3 1.2 MySQL介绍 3 1.3 B/S开发模式 3 1.4 Java介绍 4 2 系统分析 5 2.1 可行性研究 5 2.1.1技术可行性 5 2.2.2经济可行性 5 2.3.1操作可行性 5 2.2 需求分析 6 2.2.1系统用例图 6 2.2.2系统功能模块需求分析…

机器学习--循环神经网络(RNN)1

一、简介 循环神经网络&#xff08;Recurrent Neural Network&#xff09;是深度学习领域中一种非常经典的网络结构&#xff0c;在现实生活中有着广泛的应用。以槽填充&#xff08;slot filling&#xff09;为例&#xff0c;如下图所示&#xff0c;假设订票系统听到用户说&…

揭秘接口测试:完整流程指南!

在讲接口测试之前&#xff0c;首先需要给大家申明下&#xff1a;接口测试对于测试人员而言&#xff0c;非常非常重要&#xff0c;懂功能测试接口测试&#xff0c;就能在企业中拿到一份非常不错的薪资。 这么重要的接口测试&#xff0c;一般也是面试笔试必问。为方便大家更好的…

【C/C++】常量指针与指针常量的深入解析与区分(什么是const int * 与 int * const ?)

目录 一、前言 二、const 的简单介绍 三、常量指针 &#x1f50d;介绍与分析 &#x1f4f0;小结与记忆口诀 四、指针常量 &#x1f50d;介绍与分析 &#x1f4f0;小结与记忆口诀 五、总结与提炼 六、共勉 一、前言 在【C/C】的编程中&#xff0c;指针与const关键字的组合…

防御保护IPSEC实验

要求&#xff1a;在FW5和FW3之间建立一条IPSEC通道&#xff0c;保证10.0.2.0/24网段可以正常访问到192.168.1.0/24. 因为是双机热备状态则只需要配置FW1主设备。 新建ACL待加密数据流 安全建议&#xff1a; IPSec参数配置 FW3配置如下与FW1类似&#xff1a; FW1中新建安全策略…

链表|19.删除链表的倒数第N个节点

力扣题目链接 struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {//定义虚拟头节点dummy 并初始化使其指向headstruct ListNode* dummy malloc(sizeof(struct ListNode));dummy->val 0;dummy->next head;//定义 fast slow 双指针struct ListNode* f…

论文汇总:Rectifying the Shortcut Learning of Background for Few-Shot Learning

原文解读&#xff1a; 论文解读&#xff1a;Rectifying the Shortcut Learning of Background for Few-Shot Learning-CSDN博客 文章汇总 问题&动机&解决方法 图像背景是一种有害知识的来源&#xff0c;这是少数镜头学习模型容易吸收的(问题) 通过在训练和评估中提…

TI IWR6843ISK ROS驱动程序搭建

1、设备准备 1.1 硬件设备 1&#xff09;TI IWR 6843 ISK 1块 2&#xff09;Micro USB 数据线 1条 1.2 系统环境 1&#xff09;VMware Workstation 15 Player 虚拟机 2&#xff09;Ubuntu18.04 并安装有 ROS1 系统 如若没有安装 ROS 系统&#xff0c;可通过如下指令进行…

【分库分表】基于mysql+shardingSphere的分库分表技术

目录 1.什么是分库分表 2.分片方法 3.测试数据 4.shardingSphere 4.1.介绍 4.2.sharding jdbc 4.3.sharding proxy 4.4.两者之间的对比 5.留个尾巴 1.什么是分库分表 分库分表是一种场景解决方案&#xff0c;它的出现是为了解决一些场景问题的&#xff0c;哪些场景喃…

Solidity攻击合约:“被偷走的资金”

在以太坊智能合约开发中&#xff0c;Solidity是最常用的编程语言。然而&#xff0c;由于代码编写不当或缺乏安全意识&#xff0c;合约可能面临各种攻击。本文将通过一个简单的Solidity合约示例&#xff0c;展示一个潜在的攻击合约&#xff0c;并分析其相对于原本合约的危害以及…

计算机设计大赛 疲劳驾驶检测系统 python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#x…

024—pandas 将一列数据等份变形

前言 今天我们将一个 Series 序列数据转为 DataFrame结构。我们将用么 pd.cut() 对数据进行分箱&#xff0c;也会用到 NumPy 的 np.reshape() 对阵列数据进行变形。接下来我们看看具体的需求&#xff0c;再分析一下解决思路&#xff0c;最后用代码实现它。 需求&#xff1a; …

flink实战--Flink任务资源自动化优化

背景 在生产环境Flink任务资源是用户在实时平台端进行配置,用户本身对于实时任务具体配置多少资源经验较少,所以存在用户资源配置较多,但实际使用不到的情形。比如一个 Flink 任务实际上 4 个并发能够满足业务处理需求,结果用户配置了 16 个并发,这种情况会导致实时计算资…

《Effective Modern C++》- 极精简版 15-21条

本文章属于专栏《业界Cpp进阶建议整理》 继续上篇《Effective Modern C》- 极精简版 5-14条。本文列出《Effective Modern C》的15-21条的个人理解的极精简版本。 Item15、尽量使用constexpr constexpr形容对象 constexpr对象都是const&#xff0c;但是const对象不一定是conste…

七、门控循环单元语言模型(GRU)

门控循环单元&#xff08;Gated Recurrent Unit&#xff0c;GRU&#xff09;是 LSTM 的一个稍微简化的变体&#xff0c;通常能够提供同等的效果&#xff0c;并且计算训练的速度更快。 门控循环单元原理图&#xff1a;参考门控循环单元 原理图中各个图形含义&#xff1a; X(t)&a…

C语言逗号运算符(,)

在C语言中&#xff0c;逗号运算符&#xff08;,&#xff09;用于在表达式中分隔多个子表达式&#xff0c;并按照从左到右的顺序依次计算这些子表达式。逗号运算符的运算结果是最后一个子表达式的值。 逗号运算符的底层行为是依次计算每个子表达式&#xff0c;并将每个子表达式…

gumbel-softmax如何实现离散分布可微+torch代码+原理+证明

文章目录 背景方法通俗理解什么是重参数化gumbel-softmax为什么是gumbeltorch实现思考 背景 这里举一个简单的情况&#xff0c;当前我们有p1, p2, p3三个概率&#xff0c;我们需要得到最优的一个即max(p1, p2, p3)&#xff0c;例如当前p3 max(p1, p2, p3)&#xff0c;那么理想…

【从部署服务器到安装autodock vina】

注意&#xff1a;服务器 linux系统选用ubuntu 登录系统&#xff0c;如果没有图形化见面可以先安装图形化界面 可以参考该视频 --> linux安装图形化界面 非阿里云ubuntu 依次执行以下命令 sudo apt-get update sudo apt-get install gnome sudo reboot阿里云ubuntu 需多执…

分布式解决方案

目录 1. 分布式ID1-1. 传统方案1-2. 分布式ID特点1-3. 实现方案1-4. 开源组件 1. 分布式ID 1-1. 传统方案 时间戳UUID 1-2. 分布式ID特点 全局唯一高并发高可用 1-3. 实现方案 方案总结&#xff1a; 号段模式 有两台服务器&#xff0c;给第一台服务器分配0-100&#xff0…

前端手册-实现挂坠灯笼效果

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…