【GD32F303红枫派使用手册】第九节 RTC-万年历实验

2024-06-07 20:20

本文主要是介绍【GD32F303红枫派使用手册】第九节 RTC-万年历实验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

9.1 实验内容

通过本实验主要学习以下内容:

  • RTC简介
  • RTC复位
  • RTC实现万年历
  • RTC使用注意事项

9.2 实验原理

9.2.1 RTC简介

RTC(Real Time Clock)——实时时钟定时器,可以用作日历。RTC 电路分两个电源域部分,其一位于备份域中,该部分包括一个 32 位的累加计数器、一个闹钟、一个预分频器、一个分频器以及RTC时钟配置寄存器。备份域这部分电路不会因为系统复位或者MCU进入低功耗而丢失数据,所以在系统复位或MCU从低功耗下唤醒,RTC 的设置和时间都可以保持不变。另一部分位于VDD 电源域中,该部分只包括 APB 接口以及一组控制寄存器。

关于RTC的两个电源域及分布在两个电源域中的内容,需要读者牢记,否则会由于细节没处理好导致RTC工作异常。

以下为GD32F303的RTC框图:

图中RTC_CNT为计数值,每个SC_CLK时钟到来时,这个计数值增加+1。SC_CLK时钟源有三个:LXTAL(外部低速晶振)、IRC40K(内部40K晶振)和HXTAL/128,三个时钟源经过RTC_DIV产生SC_CLK。RTC_DIV的配置是通过RTC_PSC加载所得,RTC_PSC由寄存器RTC 预分频寄存器高位 (RTC_PSCH)和RTC 预分频寄存器低位 (RTC_PSCL)配置,有效位20bit,即RTC的时钟分频器最大值为2的20次方,完全足够应用了。

了解了上面的内容,RTC的工作原理就很好理解了。举个例子,RTC的时钟源选择LXTAL,频率为32.768KHz,然后设置RTC_PSC为32768,那么SC_CLK即为1Hz,也就是说,每秒钟RTC_CNT值+1,所以我们只要获得一段时间内RTC_CNT值增加了多少数,也就知道经过了多少秒。

我们从手册中可以看到RTC_CNT由RTC 计数寄存器高位 (RTC_CNTH)和RTC 计数寄存器低位 (RTC_CNTL)设置,这两个寄存器组合起来的有效位为32bit,即RTC_CNT可以记录2的32次方,即4,294,967,296个数,按照每秒增加一次的话,可以记录136多年,

除了基础的记时间的功能,RTC还有一个闹钟功能,RTC运行时,当RTC_CNT的值增加到和RTC_ALRM(由RTC 闹钟寄存器高位 (RTC_ALRMH)和RTC 闹钟寄存器低位 (RTC_ALRML)设置)相等时,则会产生ALRM中断,当然,程序中需要实现使能ALARM中断(ALRMIE)。

RTC还有另外两个中断,一个是秒中断,另一个是溢出中断。秒中断好理解,即每秒钟进入一个中断;溢出中断则是当RTC_CNT溢出时产生中断。

9.2.2 RTC复位

这里把RTC的复位单独作为一个章节来说,是因为这里很容易出错导致想要实现的功能无法实现。

前面说过,RTC分为两个电源域——备份域和VDD电源域,而一般的复位比如NRST脚复位、软件复位等只能复位VDD和VDDA电源域 ,而无法复位备份域;备份域复位需要VBAT掉电或者通过备份域控制寄存器( RCU_BDCTL)的BKPDRST来进行备份域复位。

上节中的RTC框图中被深色框住的即属于备份域,这里提到一个很容易出错的地方,即RTC的时钟源选择。从框图看到时钟源由RTCSRC[1:0]来设置,这个位域属于备份域控制寄存器( RCU_BDCTL)。

备份域控制寄存器( RCU_BDCTL):

寄存器描述中指出,一旦RTC的时钟源选择后,除了将备份域复位,否则时钟不能被改变。举个例子:一个产品选择LXTAL作为RTC时钟,但可能因为某些原因LXTAL停振了,需要将时钟源切换到IRC40K,程序如何实现呢?没错,需要复位备份域(控制BKPDRST位)才能重新选择时钟源,但一旦备份域进行了复位,包括RTC_CNT等数据都会丢失,所以在备份域复位前需要对RTC内的各个数据进行保存处理,待备份域复位后再重新写入。

9.2.3 RTC实现万年历

本实验用RTC做一个万年历,其中还需要考虑到闰年闰月的情况。实验设置的基准时间是1970年,即当RTC_CNT为0时,为1970年,实验最高可记录到2106年(1970+136)。

9.3 硬件设计

本实验实现每秒钟通过串口打印实时时间,即需要使用到开发板USB串口模块。另外RTC时钟源使用的是外部低速晶振,外部晶振原理图如下:

9.4 代码解析

9.4.1 RTC配置

在driver_rtc.c中定义了RTC配置函数rtc_configuration

C
void rtc_configuration(void)
{/*使能备份域和PMU时钟*/rcu_periph_clock_enable(RCU_BKPI);rcu_periph_clock_enable(RCU_PMU);/*使能备份域写功能*/pmu_backup_write_enable();/*备份域复位*/bkp_deinit();/*依据选择的时钟源进行时钟配置*//*使用LXTAL*/#ifdef RTC_CLOCK_SOURCE_LXTAL/* 使能 LXTAL */rcu_osci_on(RCU_LXTAL);/* 等待LXTAL Ready */rcu_osci_stab_wait(RCU_LXTAL);/*选择LXTAL作为RTC时钟*/rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);#endif/*使用IRC40K*/#ifdef RTC_CLOCK_SOURCE_IRC40K/* 使能 IRC40K*/rcu_osci_on(RCU_IRC40K);/* 等待IRC40K Ready */rcu_osci_stab_wait(RCU_IRC40K);/*选择IRC40K作为RTC时钟*/rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);#endif#ifdef RTC_CLOCK_SOURCE_HXTAL_DIV_128/* 使能LXTAL */rcu_osci_on(RCU_HXTAL);/* 等待HXTAL Ready */rcu_osci_stab_wait(RCU_HXTAL);/*选择HXTAL/128作为RTC时钟*/rcu_rtc_clock_config(RCU_RTCSRC_HXTAL_DIV_128);#endif/*使能RTC时钟*/rcu_periph_clock_enable(RCU_RTC);/*等待RTC寄存器同步*/rtc_register_sync_wait();/*等待上一次操作完成*/rtc_lwoff_wait();/*使能RTC秒中断*/rtc_interrupt_enable(RTC_INT_SECOND);/*等待上一次操作完成*/rtc_lwoff_wait();/*依据选择的时钟源来设置分频,使RTC周期为1s*/#ifdef RTC_CLOCK_SOURCE_LXTALrtc_prescaler_set(32767);#endif#ifdef RTC_CLOCK_SOURCE_IRC40Krtc_prescaler_set(40000);#endif#ifdef RTC_CLOCK_SOURCE_HXTAL_DIV_128rtc_prescaler_set(HXTAL_VALUE/128);#endif/*等待上一次操作完成*/rtc_lwoff_wait();
}

 时钟源通过driver_rtc.h中的宏定义来选择:

C
#define RTC_CLOCK_SOURCE_LXTAL
//#define RTC_CLOCK_SOURCE_IRC40K
//#define RTC_CLOCK_SOURCE_HXTAL_DIV_128 

9.4.2 万年历实现

在bsp_rtc.c中定义了实现万年历的几个函数:rtc_time_set、rtc_time_display、is_leap_year等。

rtc_time_set函数——第一次需要手动设置当前时间:

C
uint32_t rtc_time_set(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
{uint16_t t;uint32_t seccount = 0;if (bkp_read_data(BKP_DATA_0) != 0xA5A5){rtc_configuration();if(year < 1970 || year > 2099)return 1;for(t = 1970; t < year; t++){if(is_leap_year(t)){seccount += 31622400;}else{seccount += 31536000;}}month -= 1;for(t=0; t < month; t++){seccount += (uint32_t)month_table[t] * 86400; if(is_leap_year(year) && t==1){seccount+=86400;}}                        seccount += (uint32_t)(day-1) * 86400;seccount += (uint32_t)hour * 3600;seccount += (uint32_t)minute * 60;seccount += second;rtc_counter_set(seccount);bkp_write_data(BKP_DATA_0, 0xA5A5);return 0;}else{if (rcu_flag_get(RCU_FLAG_PORRST) != RESET){printf("Power On Reset occurred....\r\n");}else if (rcu_flag_get(RCU_FLAG_EPRST) != RESET){printf("External Reset occurred....\r\n");}rcu_all_reset_flag_clear();rcu_periph_clock_enable(RCU_PMU);pmu_backup_write_enable();rtc_register_sync_wait();rtc_interrupt_enable(RTC_INT_SECOND);rtc_lwoff_wait();return 0;}
}

该函数中在第一次上电运行时,会进行初始时间设置,然后写入特定数据到备份域数据寄存器中。当发生系统复位但Vbat未掉电的情况下,则不会重新进行时间设置,但需要重新开启秒时钟中断,因为SCIE处于VDD电源域而不在备份域。

rtc_time_display函数——打印实时时间:

C
void rtc_time_display(uint32_t timevar)
{ static uint16_t daycnt = 0;uint32_t temp = 0;uint16_t temp1 = 0;temp = timevar / 86400;if(daycnt != temp) {daycnt = temp;temp1 = 1970;while(temp >= 365){if(is_leap_year(temp1)){if(temp >= 366)temp-=366;else break;}else temp -= 365;temp1++;}calendar.years = temp1;temp1=0;while(temp >= 28){if(is_leap_year(calendar.years) && temp1 == 1){if(temp >= 29)temp -= 29;elsebreak;}else{if(temp >= month_table[temp1])temp -= month_table[temp1];elsebreak;}temp1++;}calendar.months = temp1 + 1;calendar.days = temp + 1;}temp = timevar % 86400;calendar.hours = temp / 3600;calendar.minutes = (temp % 3600) / 60; calendar.seconds = (temp % 3600) % 60; printf("Time: %0.4d-%0.2d-%0.2d,%0.2d:%0.2d:%0.2d\r\n", calendar.years, calendar.months, calendar.days, calendar.hours, calendar.minutes, calendar.seconds);
}

is_leap_year函数——判断当前年是否为闰年:

C
uint8_t is_leap_year(uint16_t year)
{if((year%4 == 0 && year % 100 != 0) || (year % 400 == 0)){return 1;}else{return 0;}
}

9.4.3 main函数和中断函数实现

以下为main函数代码:

C
int main(void)
{delay_init();//delay函数初始化bsp_uart_init(&BOARD_UART);//BOARD_UART串口初始化nvic_irq_enable(RTC_IRQn,1,0);//打开RTC的NVICrtc_time_set(year_set,month_set,day_set,hour_set,minute_set,second_set); //设置当前时间while (1){/* 判断是否到1s了*/if (timedisplay == 1){/* 显示实时时间*/rtc_time_display(rtc_counter_get());timedisplay = 0;}}
}

本例程main函数首先进行了延时函数初始化,再初始化开发板USB串口,开启RTC的NVIC后设置了当前时间,在while(1)循环中等待RTC数据更新,然后将实时时间打印出来。

中断函数代码:

C
void RTC_IRQHandler(void)
{if (rtc_flag_get(RTC_FLAG_SECOND) != RESET){/* 清除RTC秒中断标志位*/rtc_flag_clear(RTC_FLAG_SECOND);timedisplay = 1;/* 等待上一次操作完成 */rtc_lwoff_wait();}
}

9.5 实验结果

为了验证万年历是否工作正常,设定初始时间为2022年12月31日23时59分50s,当程序开始运行时,每秒钟打印时间,经过10s后,可以看到时间变为2023年1月1日0时0分0秒,说明万年历有效。

由聚沃科技原创,来源于   【红枫派开发板】第九讲 RTC-万年历实验 - 苏州聚沃电子科技有限公司 (gd32bbs.com)

GD32MCU技术交流群:859440462   

这篇关于【GD32F303红枫派使用手册】第九节 RTC-万年历实验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040198

相关文章

高性能并行计算华为云实验五:

目录 一、实验目的 二、实验说明 三、实验过程 3.1 创建PageRank源码 3.2 makefile的创建和编译 3.3 主机配置文件建立与运行监测 四、实验结果与分析 4.1 采用默认的节点数量及迭代次数进行测试 4.2 分析并行化下节点数量与耗时的变化规律 4.3 分析迭代次数与耗时的变化规律 五、实验思考与总结 5.1 实验思考 5.2 实验总结 E

物联网系统运维——移动电商应用发布,Tomcat应用服务器,实验CentOS 7安装JDK与Tomcat,配置Tomcat Web管理界面

一.Tomcat应用服务器 1.Tomcat介绍 Tomcat是- -个免费的开源的Ser Ivet容器,它是Apache基金会的Jakarta 项目中的一个核心项目,由Apache, Sun和其他一 些公司及个人共同开发而成。Tomcat是一一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。 在Tomcat中,应用程序的成部署很简

STM32HAL库--SDRAM实验(速记版)

STM32F429IGT6 自带了 256K 字节的 SRAM,对一般应用来说,已经足够了,不过在一些对内存要求高的场合, STM32F429 自带的这些内存就不够用了。比如使用 LTDC 驱动RGB 屏、跑算法或者跑 GUI 等,就可能不太够用,所以阿波罗 STM32F429 开发板板载了一颗 32M 字节容量的 SDRAM 芯片:W9825G6KH,满足大内存使用的需求。 1

HCIA 19 结束 企业总部-分支综合实验(下)

3.6出口NAT配置可以访问互联网 配置NAT使内网可以访问公网8.8.8.8,当前总部PC1 PING不通公网地址8.8.8.8。 3.6.1总部配置NAT访问互联网 步骤1:配置NAT acl number 2000    rule 5 permit source 192.168.0.0 0.0.255.255 # interface GigabitEthernet0/0/2

STM32G030F6使用CubeMx配置PWM实验

1. 使用 CubeMx 创建 PWM 工程 打开 CubeMx 软件,选中我们此次使用的单片机型号 STM32G030F6P6 ,点击 StartProject. 配置定时器 配置定时器1的通道1和通道2 产生PWM; 设置定时器1的主频:设置了( 63 + 1) 分频即定时器主频为1M 设置PWM定时的周期计数为 1000 即 1000HZ 设置通道一 翻转的计数值为 500 即

STM32G030F6使用CubeMx配置DMA读取多通道ADC实验

1. 使用 CubeMx 创建 ADC 工程 打开 CubeMx 软件,选中我们此次使用的单片机型号 STM32G030F6P6 ,点击 StartProject. 先配置一下串口,用来打印相关信息 再来配置 ADC 配置DMA PS:DMA 需要要配置成循环模式,否则只填满一次缓存数组后就停止工作,需要重调用启动 DMA 的函数. 配置时钟 ps:本实验使用内部高速时钟

STM32G030F6使用CubeMx配置RTC及闹钟实验

1. 使用 CubeMx 创建 RTC 工程 打开 CubeMx 软件,选中我们此次使用的单片机型号 STM32G030F6P6 ,点击 StartProject. 先配置一下串口,用来打印相关信息 再来配置 RTC 配置时钟 ps:本实验使用内部低速时钟测试,未使用外部晶振. 配置工程相关选项 配置完成后点击右上角 GENERATE CODE完成工程的创建 2. 编程

Python 算法交易实验72 QTV200第一步: 获取原始数据并存入队列

说明 最近的数据流往前进了一步,我觉得基本可以开始同步的推进QTV200了。上次规划了整体的数据流,现在开始第一步。 内容 1 结构位置 这是上次的总体图: 以下是这次要实现的一小部分: 从结构上,这个是整体数据流的起点,系统因为这些不断 运行的数据才开始“动”了起来,可以称为源点。 2 规范与约束 源点是基于每分钟的节拍从外界读取数据,这部分目前我没用用付费接口(数据的需求量很

物联网系统运维——移动电商服务器单点部署,web服务器部署,Nginx Web服务介绍,Nginx性能,部署,架构,及实验:安装并设置Nginx(重点)

一.web服务器介绍 Web服务器一般指网站服务器,是指驻留于因特网上提供某种特定类型计算机的程序,Web服务器可以向浏览器等Web客户端提供文档,也可以放置网站文件,让全世界浏览,可以放置数据文件,让全世界下载。目前最主流的三个Web服务器是Apache、Nginx、IIS。  二.Nginx Web服务介绍 ●模块化设计:良好的扩展性,可以通过模块方式进行功能扩展。●高可靠性:主控进

【非常实验】如何在移动设备上运行 Docker?

本章就从在 DevOps 中最基本但也是最强大的工具 Docker 开始。最近,我在尝试更多Termux的可能性,于是就想着试试Docker适不适合arm架构。 我用的是天玑9000芯片,而不是高通,所以显示不出来 Qualcomm。所以我决定从在手机上运行 docker 开始,但这可能吗?让我们一起来看看吧。 步骤 0:获取 SSH 访问权限 (这是一个可选步骤,如果你不想在电脑上工作