ARM32开发--RTC内置实时时钟

知不足而奋进 望远山而前行


目录

系列文章目录

文章目录

前言

学习目标

学习内容

RTC时钟介绍

RTC结构框图

RTC原理图

RTC时钟电源

RTC的配置流程

RTC时钟

开发流程

RTC初始化

时钟配置

时钟获取

BCD格式转化

完整代码

RTC时钟备份寄存器

总结


前言

在嵌入式系统开发中,实时时钟(RTC)是至关重要的组件,它提供了准确的日期和时间信息,为各种应用场景提供了时间基准。本文将深入探讨RTC的设计原理和初始化过程,帮助您理解RTC在GD32F407上的实现方式以及如何进行初始化、配置和读取时间等操作。


学习目标

  1. 理解原理图RTC设计部分
  2. 掌握初始化RTC
  3. 掌握设置时间
  4. 掌握读取时间

学习内容

RTC时钟介绍

RTC是实时时钟(Real-Time Clock)的缩写。它是一种硬件模块或芯片,用于提供准确的日期和时间信息。

GD32F407上有RTC的外设,它提供了一个包含日期(年/月/日)和时间(时/分/秒/亚秒)的日历功能。除亚秒用二进制码显示外,时间和日期都以BCD码的形式显示。

RTC本质上就是一个1秒计数器,通过秒来换算出时间。因此需要我们提供一个1HZ频率的时钟。

RTC结构框图

RTC原理图

RTC时钟电源

RTC时钟分为两个电源域。RTC的核心部分位于备份域中。系统复位或从待机模式唤醒时,RTC的设置和时间都保持不变。另一部分包括APB接口及一组控制寄存器位于VDD电源域中。

RTC的配置流程

RTC时钟

开发流程
  1. 加载依赖。gd32f4xx_rtc.c,gd32f4xx_pmu.c
  2. 初始化RTC。
  3. 时钟配置。
  4. 获取时钟。
RTC初始化
// 电池管理加载
rcu_periph_clock_enable(RCU_PMU);
pmu_backup_write_enable();

// 重置备份域(不重置可能导致无法设置晶振,出现不走字情况)
/* reset backup domain */
rcu_bkp_reset_enable();
rcu_bkp_reset_disable();

// 2. 设置时钟的晶振 LXTAL (低速外部时钟, 需焊接)
// rcu_osci_on(RCU_LXTAL);
// rcu_osci_stab_wait(RCU_LXTAL);
// rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);

// 2. 设置时钟的晶振 IRC32K
// rcu_osci_on(RCU_IRC32K);
// // 等待晶振稳定
// rcu_osci_stab_wait(RCU_IRC32K);
// /* 给rtc配置晶振 configure the RTC clock source selection */
// rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);

// 2. 设置时钟的晶振 HXTAL -> 8M
 rcu_osci_on(RCU_HXTAL);
 // 等待晶振稳定
 rcu_osci_stab_wait(RCU_HXTAL);
 /* 给rtc配置晶振 configure the RTC clock source selection */
 rcu_rtc_clock_config(RCU_RTCSRC_HXTAL_DIV_RTCDIV);
 // 分频系数(HXTAL时,需要配置) // DIV25 -> 320K
 rcu_rtc_div_config(RCU_RTC_HXTAL_DIV25); 

// RTC功能加载
rcu_periph_clock_enable(RCU_RTC);
rtc_register_sync_wait();
  • RTC电源供应为外部独立电路,需要加载电源管理,打开rtc电源供应。
  • 重置备份域,不重置可能导致无法设置晶振,出现时钟不走字情况。
  • RTC的时钟晶振为外部,需要加载外部晶振。
  • 加载完成过程,需要等待同步。

时钟配置
rtc_parameter_struct rps;
rps.year = 0x23;
rps.month = 0x04;
rps.date = 0x20;
rps.day_of_week = 0x04;
rps.hour = 0x23;
rps.minute = 0x59;
rps.second = 0x55;
rps.display_format = RTC_24HOUR;
rps.am_pm = RTC_AM;
// 配置异步和同步分频器数值 LXTAL
// rps.factor_asyn = 0x7F;
// rps.factor_syn = 0xFF;

  // 配置异步和同步分频器数值IRC32K
//  rps.factor_asyn = 0x7F;   // 7位异步预分频器, 0x0 - 0x7F
//  rps.factor_syn  = 0xF9;   // 15位同步预分频器。0x0 - 0x7FFF

// IRC32K ck_spre = 1 -> 1秒 -> 1个时钟 (1Hz)
// ck_spre = rtc_clk / ((FACTOR_A + 1)*( FACTOR_S + 1))
// 1 = 32K / ((0x7F + 1)*( FACTOR_S + 1))
// FACTOR_S = 32K / 0x80 - 1 = 32K / 128 - 1 = 249

// 配置异步和同步分频器数值HXTAL_DIV25
rps.factor_asyn = 127;   // 7位异步预分频器, 0x0 - 0x7F
rps.factor_syn  = 2499;   // 15位同步预分频器。0x0 - 0x7FFF

// HXTAL_DIV25 -> 8M/25 -> 320K
// ck_spre = 1 -> 1秒 -> 1个时钟 (1Hz)
// ck_spre = rtc_clk / ((FACTOR_A + 1)*( FACTOR_S + 1))
// 1 = 320K / ((0x7F + 1)*( FACTOR_S + 1))
// FACTOR_S = 320K / 0x80 - 1 = 32K / 128 - 1 = 2499

rtc_init(&rps);

1 = rtc_clk/(asyn + 1)/(syn + 1)

  • asyn取值范围为0到0x7F
  • syn取值范围为0到0x07FF
时钟获取
rtc_parameter_struct rps;
rtc_current_time_get(&rps);

uint16_t year = READ_BCD(rps.year) + 2000;
uint8_t month = READ_BCD(rps.month);
uint8_t date = READ_BCD(rps.date);
uint8_t week = READ_BCD(rps.day_of_week);
uint8_t hour = READ_BCD(rps.hour);
uint8_t minute = READ_BCD(rps.minute);
uint8_t second = READ_BCD(rps.second);

printf("%d-%d-%d %d %d:%d:%d\r\n", year, month, date, week, hour, minute, second);
BCD格式转化

BCD(Binary-Coded Decimal,二进制编码十进制)是一种用于表示十进制数字的二进制编码形式。在RTC(实时时钟)等应用中,BCD格式常用于存储和显示日期和时间信息。它的主要特点是每个十进制数位都被编码成4位二进制数。

在BCD格式中,一个十进制数的每一位被表示为4位二进制数,其中每个二进制数位都对应一个十进制数位。例如:

  • 十进制数 0 用BCD表示为 0000。
  • 十进制数 1 用BCD表示为 0001。
  • 十进制数 9 用BCD表示为 1001。

这样,一个BCD字节(8位)可以表示两个十进制数字。

// 十位取出左移4位 + 个位 (得到BCD数)
#define WRITE_BCD(val) 	((val / 10) << 4) + (val % 10)
// 将高4位乘以10 + 低四位 (得到10进制数)
#define READ_BCD(val) 	(val >> 4) * 10 + (val & 0x0F)
完整代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "Usart.h"

// 十位取出左移4位 + 个位 (得到BCD数)
#define WRITE_BCD(val) 	((val / 10) << 4) + (val % 10)
// 将高4位乘以10 + 低四位 (得到10进制数)
#define READ_BCD(val) 	(val >> 4) * 10 + (val & 0x0F)

void Usart0_recv(uint8_t *data, uint32_t len) {
    printf("%s\r\n", data);
}

static void RTC_config() {
    // 电池管理加载
    rcu_periph_clock_enable(RCU_PMU);
    pmu_backup_write_enable();
    
    // 重置备份域(不重置可能导致无法设置晶振,出现不走字情况)
    /* reset backup domain */
    rcu_bkp_reset_enable();
    rcu_bkp_reset_disable();
    
    // 晶振加载
    rcu_osci_on(RCU_LXTAL);
    rcu_osci_stab_wait(RCU_LXTAL);
    rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
    // RTC功能加载
    rcu_periph_clock_enable(RCU_RTC);
    rtc_register_sync_wait();

    rtc_parameter_struct rps;
    rps.year = WRITE_BCD(23);
    rps.month = WRITE_BCD(4);
    rps.date = WRITE_BCD(20);
    rps.day_of_week = WRITE_BCD(4);
    rps.hour = WRITE_BCD(23);
    rps.minute = WRITE_BCD(59);
    rps.second = WRITE_BCD(55);
    rps.display_format = RTC_24HOUR;
    rps.am_pm = RTC_AM;
    rps.factor_asyn = 0x7F;
    rps.factor_syn = 0xFF;

    rtc_init(&rps);
}

static void RTC_read() {
    rtc_parameter_struct rps;
    rtc_current_time_get(&rps);

    uint16_t year = READ_BCD(rps.year) + 2000;
    uint8_t month = READ_BCD(rps.month);
    uint8_t date = READ_BCD(rps.date);
    uint8_t week = READ_BCD(rps.day_of_week);
    uint8_t hour = READ_BCD(rps.hour);
    uint8_t minute = READ_BCD(rps.minute);
    uint8_t second = READ_BCD(rps.second);

    printf("%d-%d-%d %d %d:%d:%d\r\n", year, month, date, week, hour, minute, second);
}

int main(void)
{
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    systick_config();
    Usart0_init();

    RTC_config();

    while(1) {
        RTC_read();

        delay_1ms(1000);
    }
}

RTC时钟备份寄存器

RTC时钟有20个32位(共80字节)通用备份寄存器,能够在省电模式下保存数据。通过备份寄存器可以实现只配置一次时间即可。

注意:如果在尝试向RTC_BKP0写入数据之前或之后调用了rcu_bkp_reset_enable(),那么这确实会导致写入的数据在系统复位后丢失,因为RTC备份寄存器会被复位重置。因此,要确保在向RTC备份寄存器写入数据并期望这些数据在复位后仍然有效时,不能调用rcu_bkp_reset_enable()。

if( RTC_BKP0 == 0xf234 ){	
	//说明不是第一次初始化,可以不用重新设置时间
}else{//如果后备寄存器0 的值 不为 0XF234
    //设置后备寄存器0的值为 0XF234,标记已经初始化过RTC
    RTC_BKP0 = 0xf234;
    //初始化RTC时间
    RtcTimeConfig(0x23,0x07,0x12,0x03,0x12,0x59,0x50);
}

总结

  1. 理解了RTC的基本原理,包括其在实时时钟设计中的作用和结构。
  2. 掌握了初始化RTC的步骤,包括加载依赖、配置时钟源和初始化RTC等。
  3. 理解了设置时间的方法,包括配置时钟参数和异步同步分频器的数值等。
  4. 掌握了读取时间的操作,包括BCD格式转换和从RTC中获取日期和时间信息。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【CS.DB】深度解析:ClickHouse与Elasticsearch在大数据分析中的应用与优化

文章目录 《深入对比&#xff1a;在大数据分析中的 ClickHouse和Elasticsearch》 1 介绍 2 深入非关系型数据库的世界2.1 非关系型数据库的种类2.2 列存储数据库&#xff08;如ClickHouse&#xff09;2.3 搜索引擎&#xff08;如Elasticsearch&#xff09;2.4 核心优势的归纳 3…

准研究生了解内容:如何挑选论文并下载

本文主要纪录自己从0开始摸索如何找论文&#xff0c;下载论文等的过程。 前言 &#xff08;一点想法&#xff09;## 作为准研究生&#xff0c;上岸后一直非常颓废&#xff0c;除了给人补课挣了点money&#xff0c;剩下时间都是打游戏&#xff0c;被老姐训诫后决定继续学习。毕…

跟着AI学AI_07张量、数组、矩阵

说明这三个概念不是一个范畴的东西&#xff0c;但是很容易混淆&#xff0c;因此放到一起进行说明。 张量&#xff08;Tensor&#xff09; 张量是一个多维数组的通用概念&#xff0c;用于表示具有任意维度的数值数据。在数学和计算机科学中&#xff0c;张量是广泛用于表示数据的…

【星海随笔】云解决方案学习日志篇(一) ELK,kibana,Logstash安装

心路历程 本来想最近再研究研究DPDK的。但是自己做一个东西很多时候没有回报。因为自己的低学历问题&#xff0c;类似工作的面试都没有。所以很多东西学了很快就忘了&#xff0c;没有地方可以用。 今天看到了一个大佬,除了发型外,很多想法还是很共鸣的。 Shay Banon 决定开始跟…

【JavaScript】简单数据类型 与 复杂数据类型 ① ( 堆内存和栈内存 | 简单数据类型内存存储 | 复杂数据类型内存存储 )

文章目录 一、简单数据类型1、简单数据类型简介2、简单数据类型 null 空类型的特殊性 二、复杂数据类型三、堆内存和栈内存 一、简单数据类型 1、简单数据类型简介 JavaScript 中 , " 简单数据类型 “ 又称为 ” 基本数据类型 " 或 " 值类型 " , 与 简单数…

MySQL-分组函数

041-分组函数 重点&#xff1a;所有的分组函数都是自动忽略NULL的 分组函数的执行原则&#xff1a;先分组&#xff0c;然后对每一组数据执行分组函数。如果没有分组语句group by的话&#xff0c;整张表的数据自成一组。 分组函数包括五个&#xff1a; max&#xff1a;最大值mi…

Ecovadis认证准则

Ecovadis准则是一个国际性的企业社会责任(CSR)评估平台&#xff0c;旨在帮助企业、投资者、供应商和利益相关者等评估和管理其全球供应链的社会和环境影响。该准则涵盖了可持续性、人权、劳工权利、环境保护等方面的评估内容&#xff0c;为企业提供了全面的评估标准和管理工具&…

从客户端WebAPI视角下解读前端学习

API 应用程序接口&#xff08;API&#xff0c;Application Programming Interface&#xff09;是基于编程语言构建的结构&#xff0c;使开发人员更容易地创建复杂的功能。它们抽象了复杂的代码&#xff0c;并提供一些简单的接口规则直接使用。 JavaScript VS 客户端 API VS 客…

Buffer

Buffer 概念 在Node.js中&#xff0c;Buffer是一个非常重要的内置全局对象&#xff0c;Node.js是基于Chrome V8引擎构建的&#xff0c;V8引擎本身不支持处理二进制数据&#xff0c;因此Node.js引入Buffer来弥补这一不足&#xff0c;Buffer中文译为【缓冲区】&#xff0c;是一…

视觉系统辅助引导在激光导航AGV中应用

agv 在全球经济步入“寒冬”的大背景下&#xff0c;大量传统制造业企业开始谋划转变。通过引入AGV系统提升厂内物流效率、降低运营成本&#xff0c;已经成为制造业升级的趋势之一。 AGV是移动机器人的一个重要分支&#xff0c;具有并行作业、自动化、智能化和柔性化等优势&…

centos8 中文打印报错,解决

sudo yum install -y glibc-locale-source sudo localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 sudo yum install -y fontconfig

入门Consul注册、配置中心(代码演示)

1、安装与使用 2、注册服务到consul 3、调用consul上注册的服务 4、配置中心 1、安装与使用 官网地址&#xff1a;Install | Consul | HashiCorp Developer 下载对应的系统 解压缩后文件 打开命令行运行 consul.exe agent -dev 即可运行 可以编写一个bat脚本每次运行ba…

bdcn手顺

GitHub链接 https://github.com/pkuCactus/BDCN 环境配置(Prerequisites) Python 3.8 contourpy1.1.1 cycler0.12.1 filelock3.14.0 fonttools4.53.0 fsspec2024.6.0 importlib_resources6.4.0 intel-openmp2021.4.0 Jinja23.1.4 kiwisolver1.4.5 Mark…

扩展方块加载动画

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>扩展方块加载动画</title><style type="text/css">body {background: #ECF0F1;display: flex;justify-content: center;al…

mysql-community-libs-5.7.44-1.el7.x86_64.rpm 的公钥尚未安装

在 CentOS 或 RHEL 系统上安装 RPM 包时&#xff0c;如果遇到“公钥尚未安装”的问题&#xff0c;通常是因为系统没有导入相应的 GPG 公钥。MySQL 官方提供了一个 GPG 公钥&#xff0c;用于验证 RPM 包的签名。 以下是解决该问题的步骤&#xff1a; 下载并导入 MySQL 官方的 GP…

跨境电商中的IP隔离是什么?怎么做?

一、IP地址隔离的概念和原理 当我们谈论 IP 地址隔离时&#xff0c;我们实际上是在讨论一种网络安全策略&#xff0c;旨在通过技术手段将网络划分为不同的区域或子网&#xff0c;每个区域或子网都有自己独特的 IP 地址范围。这种划分使网络管理员可以更精细地控制哪些设备或用…

shell条件测试和if语句

“C:\Users\Jessica\Desktop\6.12.assets\image-20240612094159578.png”# shell条件测试和if语句 一、条件测试 1、$? 返回码 判断命令或者脚本是否执行成功&#xff08;最近的一条&#xff09; 0 true 为真就是成功 成立 非0 false 失败或者异常 2、test命令 可以进行…

Qt程序打包成单个exe文件

文章目录 0. 准备工作1. 使用 windeployqt 提取必要的动态链接库和资源文件1.1 操作步骤1.2 补充 2. 使用 Enigma Virtual Box将文件夹打包成单个exe2.1 操作步骤 0. 准备工作 Qt程序打包用到的工具有&#xff1a; windeployqt &#xff1a;安装Qt时自带Enigma Virtual Box 下…

探索Facebook对世界各地文化的影响

随着数字化时代的到来&#xff0c;社交媒体已成为连接世界各地人们的重要平台之一。而在这个领域的巨头之一&#xff0c;Facebook不仅是人们沟通交流的场所&#xff0c;更是一座桥梁&#xff0c;将不同地域、文化的人们联系在一起。本文将探索Facebook对世界各地文化的影响&…

Alienware外星人X17R2 原装Win11系统镜像下载 带SupportAssist OS Recovery一键恢复

装后恢复到您开箱的体验界面&#xff0c;包括所有原机所有驱动AWCC、Mydell、office、mcafee等所有预装软件。 最适合您电脑的系统&#xff0c;经厂家手调试最佳状态&#xff0c;性能与功耗直接拉满&#xff0c;体验最原汁原味的系统。 原厂系统下载网址&#xff1a;http://w…