【GD32】从零开始学兆易创新32位微处理器——RTC实时时钟+日历例程

1 简介

RTC实时时钟顾名思义作用和墙上挂的时钟差不多,都是用于记录时间和日历,同时也有闹钟的功能。从硬件实现上来说,其实它就是一个特殊的计时器,它内部有一个32位的寄存器用于计时。RTC在低功耗应用中可以说相当重要,因为在使用外部低速晶振的条件下,它在所有的低功耗模式下都可以工作,这使得RTC很适合实现芯片的低功耗唤醒。下面是RTC的框图。

咋一看RTC的内部还挺复杂的。

2 硬件时钟

先看时钟,RTC的时钟可以选择内部32kHz晶振、外部高速晶振分频或外部低速时钟,一般都是使用外部32.768kHz低速晶振来驱动。时钟会先经过数字平滑校准器,用户可以配置进行进行校准;接着进入7位异步预分频器,这个分频器一般设置最大分频,即128分频,因为这个分频器的值越大RTC的功耗会越低;然后时钟进入一个粗校准器,需要注意的是粗校准和前面的数字平滑校准同时只能开启其中一个;之后时钟进入一个15位的同步预分频器,这里一般设置为256分频,这样就刚好能分出1Hz的频率,也就是每秒更新一次RTC。

当然上面介绍的是最常用的RTC时钟配置,根据不同的功能实现还可以有其他的配置。

3 功能

3.1 日历

RTC的日历功能依赖3个寄存器——RTC_DATE(日期寄存器)RTC_TIME(时间寄存器)RTC_SS(亚秒寄存器)。日期寄存器和时间寄存器保存我们熟知的年月日时分秒数据,是以BCD码的方式保存的。亚秒寄存器用于保存毫秒级的时钟数据。

上面的3个寄存器RTC内部会有对应的影子寄存器,在实际应用中我们一般读取它们对应的影子寄存器。每2个RTC周期影子寄存器才会更新一次,虽说影子寄存器的值与实际值会有延迟,但它能保证读出来的值是一致的。如果我们读的是寄存器的实际值,那么有可能在读的过程中RTC对寄存器进行了更新,导致我们读到的值前后不一致

在读取影子寄存器的值时,我们要等待状态寄存器的RSYNF位置1才能读取,此时寄存器的值才是稳定的。另外需注意的是,在深度睡眠和待机模式下,影子寄存器的值是不更新的,因此退出该模式时需要清除RSYNF位

3.2 自动唤醒

在低功耗应用中,RTC的自动唤醒功能可以说是必用到的,RTC内部使用一个16位的向下计数的计数器实现自动唤醒。计数器的驱动时钟可以选择RTC时钟的2/4/8/16分频或内部时钟,一般会选择内部时钟,即ck_spre。如果ck_spre为1Hz的话,唤醒的时间可以设置在1秒到36小时之间。

当计数器到0时,WTF标志位置1,唤醒计数器自动重载RTC_WUT的值。当WTF1后,必须软件清除该标志。如果使能了中断功能,并且芯片处于低功耗模式,唤醒中断会使芯片退出低功耗模式。

3.3 闹钟

RTC内部有2个可配置的闹钟,它与我们日常熟知的闹钟原理是差不多的;通过设置闹钟日期寄存器和闹钟亚秒寄存器来配置唤醒时刻,当到达指定时刻时闹钟会产生中断,该中断也能将芯片从低功耗模式中唤醒。

3.4 时间戳功能

时间戳功能由RTC_TS管脚输入,通过配置TSEN位来使能。当RTC_TS管脚检测到时间戳事件发生时,会将日历的值保存在时间戳寄存器中(RTC_DTS / RTC_TTS / RTC_SSTS),同时时间戳标志(TSF)也将由硬件置1。如果时间戳中断使能被启用(TSIE),时间戳事件会产生一个中断,该中断也能将芯片从低功耗模式中唤醒。

4 例程

4.1 日历

这个例程主要是配置RTC让它正常工作,然后定时读取日历值。

先看main函数。

#include "gd32f4xx.h"
#include "systick.h"
#include "debug.h"
#include "rtc.h"struct tm time_struct = {.tm_year = 2024,.tm_mon = 1,.tm_mday = 1,.tm_hour = 0,.tm_min = 0,.tm_sec = 0,.tm_wday = RTC_MONDAY
};int main(void)
{systick_config();debug_init();rtc_config(&time_struct);printf("rtc demo\r\n");while(1){struct tm ts = {0};rtc_get_time(&ts);printf("%02d-%02d-%02d %02d:%02d:%02d\r\n", ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);delay_1ms(1000);}
}

 main函数调用rtc_config函数初始化RTC,传入struct tm结构体,这个结构体是time.h里面定义的,是系统库自带的,它的定义如下。

struct tm {int tm_sec;   /* seconds after the minute, 0 to 60(0 - 60 allows for the occasional leap second) */int tm_min;   /* minutes after the hour, 0 to 59 */int tm_hour;  /* hours since midnight, 0 to 23 */int tm_mday;  /* day of the month, 1 to 31 */int tm_mon;   /* months since January, 0 to 11 */int tm_year;  /* years since 1900 */int tm_wday;  /* days since Sunday, 0 to 6 */int tm_yday;  /* days since January 1, 0 to 365 */int tm_isdst; /* Daylight Savings Time flag */union {       /* ABI-required extra fields, in a variety of types */struct {int __extra_1, __extra_2;};struct {long __extra_1_long, __extra_2_long;};struct {char *__extra_1_cptr, *__extra_2_cptr;};struct {void *__extra_1_vptr, *__extra_2_vptr;};};
};

初始化函数的内部如下。

void rtc_config(struct tm *t)
{/* 使能PMU时钟 */rcu_periph_clock_enable(RCU_PMU);/* 使能RTC寄存器访问 */pmu_backup_write_enable();/* 使用外部低速晶振 */rcu_osci_on(RCU_LXTAL);rcu_osci_stab_wait(RCU_LXTAL);rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);rcu_periph_clock_enable(RCU_RTC);rtc_register_sync_wait();/* 初始化时钟 */rtc_set_time(t);
}

因为RTC使用的是VBAT供电域,默认配置下该供电域的寄存器是写禁止的,因此需要调用pmu_backup_write_enable函数使能写操作。下面就是配置外部低速时钟,然后调用rtc_register_sync_wait等待影子寄存器同步数据。初始化完毕就可以配置日历了。

void rtc_set_time(const struct tm *time_struct)
{rtc_parameter_struct rtc_initpara = {0};/* RTC时钟频率 = 32.768kHz / (255 + 1) / (127 + 1) = 1Hz */rtc_initpara.factor_asyn = 127;rtc_initpara.factor_syn = 255;rtc_initpara.display_format = RTC_24HOUR;rtc_initpara.year = BIN_TO_BCD(time_struct->tm_year - 1970);rtc_initpara.month = BIN_TO_BCD(time_struct->tm_mon);rtc_initpara.date = BIN_TO_BCD(time_struct->tm_mday);rtc_initpara.hour = BIN_TO_BCD(time_struct->tm_hour);rtc_initpara.minute = BIN_TO_BCD(time_struct->tm_min);rtc_initpara.second = BIN_TO_BCD(time_struct->tm_sec);rtc_initpara.day_of_week = time_struct->tm_wday;rtc_init(&rtc_initpara);
}

配置日历使用rtc_init函数,按需要填充结构体即可;factor_asyn成员是异步分频器的值,factor_syn是同步分频器的值。另外需要注意日历寄存器内的值是以BCD码形式保存,因此需要写一个简单的转换宏,像下面。

#define BIN_TO_BCD(x) ((((x) / 10) << 4) + ((x) % 10))

还有就是RTC的寄存器年份内容只能保存2个数字,即只能计算99年,所以根据需要填合适的起始值进去,像我这里就是起始年份定为1970年,把当前年份减去1970转换为BCD码后填进寄存器。

 RTC运行时可以随时调用rtc_current_time_get获取当前时间。

void rtc_get_time(struct tm *time_struct)
{rtc_parameter_struct rtc_initpara = {0};rtc_current_time_get(&rtc_initpara);time_struct->tm_year = BCD_TO_BIN(rtc_initpara.year) + 1970;time_struct->tm_mon = BCD_TO_BIN(rtc_initpara.month);time_struct->tm_mday = BCD_TO_BIN(rtc_initpara.date);time_struct->tm_hour = BCD_TO_BIN(rtc_initpara.hour);time_struct->tm_min = BCD_TO_BIN(rtc_initpara.minute);time_struct->tm_sec = BCD_TO_BIN(rtc_initpara.second);time_struct->tm_wday = rtc_initpara.day_of_week;
}

 取值的时候也是要将BCD码转成对应的数字,用下面的宏。

#define BCD_TO_BIN(x) (10 * ((x) >> 4) + ((x) & 0x0F))

下面是demo的输出。 

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

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

相关文章

【调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法】

调试笔记-系列文章目录 调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法 文章目录 调试笔记-系列文章目录调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调试步骤搜…

Linux 五种IO模型

注&#xff1a;还有一种信号驱动IO&#xff0c;使用较少暂不讨论&#xff1b; 一&#xff0c;区分阻塞、非阻塞和同步、异步 看了很多文章对这两组概念解释和对比&#xff0c;说的太复杂了&#xff0c;其实没必要&#xff0c;两句话就能说清楚。 首先&#xff0c;对于读数据rec…

Qt制作程序启动界面类QSplashScreen实例测试详解

目录 一、QSplashScreen的概述 二、QSplashScreen静态图片加载 1、主程序实现 2、mainwindow.h实现 3、mainwindows.cpp实现 三、QSplashScreen动态图片加载 1、主程序实现 2、mainwindow.h实现 3、mainwindows.cpp实现 一、QSplashScreen的概述 QSplashScreen&#x…

C++并发之协程实例(三)(co_await)

目录 1 协程2 实例3 运行 1 协程 协程(Coroutines)是一个可以挂起执行以便稍后恢复的函数。协程是无堆栈的&#xff1a;它们通过返回到调用方来暂停执行&#xff0c;并且恢复执行所需的数据与堆栈分开存储。这允许异步执行的顺序代码&#xff08;例如&#xff0c;在没有显式回调…

指纹浏览器与虚拟机的区别及在跨境电商中的应用

在如今数字化世界中&#xff0c;隐私和安全变得愈发重要。许多人在网络上进行敏感操作&#xff0c;如网上购物、在线银行、社交媒体管理等。为了保护自己的隐私&#xff0c;人们常常会寻求一些额外的工具&#xff0c;比如指纹浏览器和虚拟机。这两种工具在保护个人隐私方面都有…

sqoop的安装配置

1. 上传并解压安装包 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C ../server/ 重命名&#xff1a;mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop 2. 配置环境变量 sudo vim /etc/profile # 配置sqoop的环境变量 export SQOOP_HOME/export/server/sqoop export PATH$PATH…

Linux htop命令使用

文章目录 简介界面介绍第一行第二行第三行第四行 如何使用 简介 htop 是一个类似于 top 的命令&#xff0c;但具有更丰富的功能和更友好的界面。它可以实时显示系统中各个进程的资源占用情况&#xff0c;如 CPU 使用率、内存使用率等。以下是对 htop 命令的完全解析&#xff1…

嵌入式实验---实验五 串口数据接收实验

一、实验目的 1、掌握STM32F103串口数据接收程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、STM32F103R6能通过查询中断方式接收数据&#xff0c;每接收到一个字节&#xff0c;立即向对方发送一个相同内容的字节&#xff0c;并把该字节的十六进…

Python: HexBinDecOct

因为&#xff1a; f0b1001110# 十进制 int()a0*2**01*2**11*2**21*2**30*2**40*2**51*2**6print(a)# 八进制 oct()print(78/8,78%8)# 110 001 001 8 116print(1*2**00*2**10*2**2,1*2**00*2**10*2**2,0*2**01*2**11*2**2)#十六进制 hex()#0 100 1110 16 4Eprint(sixteenFoo(0*…

如何在Qt Designer中管理QSplitter

问题描述 当按下按钮时&#xff0c;我希望弹出一个对话框&#xff0c;用户可以在其中选择内容并最终按下 ‘Ok’ 按钮。我想在这个对话框中放置一个 QSplitter&#xff0c;左侧面板将显示树状结构&#xff0c;右侧将显示其他内容。如何正确实现这一点&#xff1f; 从 Qt 的示…

oracle 主从库中,从库APPLIED为YES ,但是主库任然为NO

主库 从库 从库已经APPLIED但是主库为APPLIED&#xff0c; 主数据库和备用数据库之间的ARCH-RFS心跳Ping负责更新主数据库上v$archived_log的APPLICED列。 在主数据库上有一个指定的心跳ARCn进程来执行此Ping。如果此进程开始挂起&#xff0c;它将不再与远程RFS进程通信&#…

git拉取gitee项目到本地

git安装等不做赘述。 根据需要选择不同操作 1.只是单纯拉取个项目&#xff0c;没有后续的追踪等操作 不需要使用git init初始化本地文件夹 新建一个文件夹用于存储项目&#xff0c;右键选择 git bash here 会出现命令行窗口 如果像我一样&#xff0c;只是拉取个项目作业&…

Python爬虫技术:动态JavaScript加载音频的解析

在当今的互联网世界中&#xff0c;JavaScript已成为构建丰富交互体验不可或缺的技术。然而&#xff0c;对于网络爬虫开发者来说&#xff0c;JavaScript动态生成的内容却带来了不小的挑战。音频内容的动态加载尤其如此&#xff0c;因为它们往往涉及到复杂的用户交互和异步数据加…

xxe漏洞学习

一、什么是xxe漏洞 XXE就是XML外部实体注入&#xff0c;当允许引用外部实体时&#xff0c; XML数据在传输中有可能会被不法分子被修改&#xff0c;如果服务器执行被恶意插入的代码&#xff0c;就可以实现攻击的目的攻击者可以通过构造恶意内容&#xff0c;就可能导致任意文件读…

kafka(四)消息类型

一、同步消息 1、生产者 同步发送的意思就是&#xff0c;一条消息发送之后&#xff0c;会阻塞当前线程&#xff0c;直至返回 ack。 由于 send 方法返回的是一个 Future 对象&#xff0c;根据 Futrue 对象的特点&#xff0c;我们也可以实现同 步发送的效果&#xff0c;只需在调…

python---OpenCv(二),背景分离方法较有意思

目录 边界矩形 旋转矩形(最小外接矩形): 计算轮廓 找4个点的坐标 把浮点型转为Int 画轮廓 边界矩形--&#xff08;最大外接矩形&#xff09; 转灰度 找轮廓 找顶点 画矩形 显示 背景分离方法&#xff08;这个很好玩&#xff0c;可以识别在动的物体&#xff09; 边…

Redis源码学习:quicklist的设计与实现

为什么需要quicklist 假设你已经知道了ziplist的缺陷&#xff1a; 虽然节省空间&#xff0c;但是申请内存必须是连续的&#xff0c;如果内存占用比较多&#xff0c;申请效率低要存储大量数据&#xff0c;超过了ziplist的最佳上限后&#xff0c;性能有影响 借鉴分片思想&…

栅格数据重心迁移变化分析

目前网络上大多是针对矢量重心迁移进行计算&#xff0c;或把栅格转矢量在进行计算&#xff0c;可以不用怎么麻烦&#xff0c;可以直接利用栅格进行得出多期数据的重心&#xff0c;然后进行变化分析等方面的分析。 矢量数据可以通过下面方式进行重心计算&#xff1a; 使用ArcGIS…

1.1 数据采集总览

正所谓巧妇难为无米之炊&#xff0c;数据采集是数据处理的第一步。 什么是数据采集 数据采集&#xff0c;也称为数据收集&#xff0c;是将原始数据从各种来源获取并存储起来的过程。这个过程是数据分析和数据仓库建设的第一步&#xff0c;涉及到从不同的数据源中提取数据&…

Element-UI实现el-dialog弹框拖拽功能

在实际开发中&#xff0c;会发现有些系统&#xff0c;弹框是可以在浏览器的可见区域自由拖拽的&#xff0c;这极大方便用户的操作。但在查看Element-UI中弹框&#xff08;el-dialog&#xff09;组件的文档时&#xff0c;发现并未实现这一功能。不过也无须担心&#xff0c;vue中…