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;假设订票系统听到用户说&…

关于vue3使用prop传动态参数时父子数据不同步更新问题

子: <template><div><h3>子组件</h3><input :value"modelValue" input"$emit(update:modelValue, $event.target.value)"></div> </template><script setup> import { defineProps, defineEmits } from …

P5635 【CSGRound1】天下第一

题目背景 天下第一的 cbw 以主席的身份在 8102 年统治全宇宙后&#xff0c;开始了自己休闲的生活&#xff0c;并邀请自己的好友每天都来和他做游戏。由于 cbw 想要显出自己平易近人&#xff0c;所以 zhouwc 虽然是一个蒟蒻&#xff0c;也有能和 cbw 玩游戏的机会。 题目描述 …

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

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

logstash和elasticsearch的几种交互接口

Logstash与Elasticsearch是两个非常流行的开源工具&#xff0c;用于处理和存储大量的日志数据。它们之间的集成非常重要&#xff0c;因为Logstash用于收集、处理和转换日志数据&#xff0c;而Elasticsearch用于存储、搜索和分析这些数据。在本文中&#xff0c;我们将详细介绍Lo…

【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;可通过如下指令进行…

uni-app navigateTo路由传参传递对象

传递参数 先通过JSON.stringify将对象转成字符串 toNextPage(obj) {uni.navigateTo({url:/pages/nextpage/index?obj${JSON.stringify(obj)}}); },接收参数 再通过JSON.parse将传递过来的字符串转成对象 onLoad(options) {this.obj JSON.parse(options.obj) }

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

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

微信小程序 - 组件wxml中slot

在组件的 wxml 中可以包含 slot 节点&#xff0c;用于承载组件使用者提供的 wxml 结构。 默认情况下&#xff0c;一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时&#xff0c;可以在组件 js 中声明启用。 Component(options: {multipleSlots: true // 在组件定义时的…

P1611 循环的数字

题目描述 你曾经因为看见一样的东西一遍又一遍地重复、循环而对电视节目感到厌烦么&#xff1f;好吧&#xff0c;虽然我并不关心电视节目的好坏&#xff0c;不过有时却也很像那样不断循环的数字。 让我们假定两个不同的正整数 (n,m) 是循环的&#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; …

[MS5146T替代ADS1246T、MS5147替代ADS1247、MS5148T替代ADS1248] 2kSPS、24bit Σ-Δ ADC

[MS5146T替代ADS1246T、MS5147替代ADS1247、MS5148T替代ADS1248] 2kSPS、24bit Σ-Δ ADC 主要特点 ◼ 可编程转换速率&#xff1a;最高 2kSPS ◼ 集成输入多路选择器 ◼ PGA 噪声&#xff1a; 70nV(RMS)PGA128 ◼ 集成双路匹配可编程电流源 ◼ 集成低温漂 2.048…

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

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