GD32F10X ----RTC

news2024/12/23 8:17:44

 1. RTC的简介 

STM32 的实时时钟(RTC)是一个独立的定时器。STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
        RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护。


 2. RTC的框图

这里用的是STM32其实与GD都是差不多。

 RTC 由两个主要部分组成 第一部分(APB1 接口),第二部分是后备区域。

 RT_DIV寄存器设置可编程产生 1 秒的 RTC 时间基准 TR_CLK。每一秒到来RTC_CNT

寄存器的值就会加1。RTC_CNT是32位的寄存器。1秒到还可以产生中断。以及溢出中断。以及闹钟中断。(当RTC_ALR寄存器与RTC_CNT一样)。

我们通过读取RTC_CNT的大小(多少秒)然后转换成实时时钟(年,月,日,时,分,秒)

如果我要设置一个实时时钟转成秒然后设置到RTC_CNT。

不管是设置与获取都是操作RTC_CNT并且单位是秒。具体怎么转成实时时钟要自己写逻辑。

实时时钟都是以1970年1月1日00 :00:00为开始。(红色字是重点)

由于RTC_CNT是32位的,可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记录 4294967296 秒,约合 136 年左右。所以最大:1970 + 136。如果大于这个年就会溢出。

3. 代码实现

 RTC.h

#ifndef _RTC_H
#define _RTC_H

#include "gd32f10x.h"
#include <stdio.h>

//日期时间结构体
typedef struct{
	//时间
	uint8_t hour;
	uint8_t min;
	uint8_t sec;
	//日期
	uint16_t w_year;
	uint8_t w_month;
	uint8_t w_day;
}_calender_obj;

extern _calender_obj calender;  //日期、时间结构体变量
extern uint8_t const month_table[12];

void RTC_Config(void);  //RTC配置
uint8_t RTC_Init(void);    //RTC初始化
void RTC_NVIC_Config(void); //配置RTC中断

uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec); //将设置时间转化为秒数,给到RTC_CNT
uint8_t RTC_Get(void);  //得到RTC_CNT的值并转换为日期时间
uint8_t Is_Leap_Year(uint16_t year); //判断year是否闰年
uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day);
#endif

RTC.c

#include "RTC.h"

_calender_obj calender;


uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
uint8_t const month_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年月份的天数

uint32_t timecount = 0;

//RTC配置
void RTC_Config(void){
	rcu_periph_clock_enable(RCU_BKPI); //备份区域的时钟要先使能
	rcu_periph_clock_enable(RCU_PMU);  //电源管理时钟使能
	pmu_backup_write_enable();         //使能备份域访问允许
	bkp_deinit();                      //备份域复位
	
	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时钟
	rtc_register_sync_wait();         //等待寄存器与APB1时钟同步
	rtc_lwoff_wait();   //等待RTC的最后一次操作完成
	rtc_interrupt_enable(RTC_INT_SECOND);//使能RTC的秒中断
	rtc_lwoff_wait();   //等待RTC的最后一次操作完成

	rtc_prescaler_set(32767); /* 配置RTC_PRL的值(时钟分频) */
	rtc_lwoff_wait();   //等待RTC的最后一次操作完成
}

//RTC初始化
uint8_t RTC_Init(void){
	RTC_Config();
	RTC_Set(2023, 8, 21, 23, 13, 15);
	RTC_NVIC_Config();//配置中断的优先级
	return 0;
}

// 配置RTC的中断优先级
void RTC_NVIC_Config(void){
	nvic_irq_enable(RTC_IRQn, 2, 0);
}

//RTC的中断服务函数
void RTC_IRQHandler(void){
	if(rtc_flag_get(RTC_FLAG_SECOND) != RESET){ //判断是否为秒中断
		rtc_flag_clear(RTC_FLAG_SECOND);
		RTC_Get();
		printf("Now time is: %d-%d-%d %d:%d:%d\r\n", calender.w_year, calender.w_month, calender.w_day, calender.hour, calender.min, calender.sec);
	}
}

//将设置时间转化为秒数,给到RTC_CNT
uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec){
	uint32_t seccounts = 0;
	uint16_t temp_year = 1970;
	uint8_t temp_month;
	if(syear<1970 || syear>2099){  //设置的时间不合理
		return 1;
	}
	
	//整年的秒数
	while(temp_year < syear){
		if(Is_Leap_Year(temp_year))seccounts += 31622400; //闰年,一年的秒数  366* 24 * 60 *60
		else seccounts += 31536000;  //平年,一年的秒数  365* 24 * 60 *60
		temp_year++;
	}

	//整月的秒数
	smonth--;
	for(temp_month = 0; temp_month<smonth; temp_month++){
		seccounts += (uint32_t)month_table[temp_month]*86400;
		if(Is_Leap_Year(syear)&&temp_month==1)seccounts += 86400; //如果设置的年份是闰年,在二月这个月份要加多一天
	}
	
	//日、时、分、秒的处理
	seccounts += (uint32_t)(sday-1)*86400; //整日的秒数  24 * 60 * 60
	seccounts += (uint32_t)shour*3600;//小时
	seccounts += (uint32_t)smin*60;   //分
	seccounts += ssec;      //秒
	
	rtc_lwoff_wait();
	rtc_counter_set(seccounts);
	return 0;
}

//得到RTC_CNT的值并转换为日期时间
uint8_t RTC_Get(void){
	//把timecount转换为日期时间,并赋给calender
	uint32_t temp_days = timecount/86400;
	uint16_t temp_year = 1970;
	uint16_t temp_month;
  uint32_t temp_seconds;
	
	timecount = rtc_counter_get();//读取RTC_CNT寄存器的值

	//处理天数中的整年,
	if(temp_days>0){
		while(temp_days>=365){
			if(Is_Leap_Year(temp_year)){//如果是闰年
				if(temp_days>365){
					temp_days -= 366;
				}
				else{
					break;
				}
			}else{
				temp_days -= 365;
			}
			temp_year++;
		}
		calender.w_year = temp_year;
		
		//剩下不足一年的,再处理整月
		temp_month = 1; //用来临时存放月份
		while(temp_days >= 28){ //超过了一个月
			if(Is_Leap_Year(calender.w_year) && temp_month == 2){
				if(temp_days>=29){ //闰年的2月是29天
					temp_days -= 29;
				}else{
					break;
				}
			}else{
				if(temp_days >= month_table[temp_month-1]){//剩余的天数是不是大于temp_month这个月整月的天数
					temp_days -= month_table[temp_month-1];
				}else{
					break;
				}
			}
			temp_month++;
		}
	}

	calender.w_month = temp_month;
	calender.w_day = temp_days+1;
	
	//处理剩下的不足一天的秒数,时:分:秒
	temp_seconds = timecount%86400; //不足一天的秒数
	calender.hour = temp_seconds/3600;
	calender.min = (temp_seconds%3600)/60;
	calender.sec = temp_seconds%60;
	
	return 0;
}


uint8_t Is_Leap_Year(uint16_t year){ //判断year是否闰年
	if(year%4 == 0){
		if(year%100 == 0){
			if(year%400 == 0)
				return 1;
			else
				return 0;
		}else{
			return 1;
		}
	}else{
		return 0;
	}
}


//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//year,month,day:公历年月日 
//返回值:星期号																						 
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世纪,年份数加100  
	if (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);
}

main.c

#include "gd32f10x_eval.h"

#include "LED.h"
#include "SYSTICK_DELAY.h"
#include "RTC.h"

int main(){
	gd_eval_com_init(EVAL_COM0);    // 初始化USART0
	LED_Init();
	my_systick_config();
	printf("This is a RTC DEMO test.\r\n");
	
	RTC_Init();
	
	while(1){
		LED1_Toggle();
		my_systick_delay_ms(1000);	//delay 1000 ms
	}
}

/*重写fputc*/
int fputc(int ch, FILE *f)
{
	usart_data_transmit(EVAL_COM0,ch);  //通过串口把ch给发送出去
	while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE));
	return ch;
}


	

通过串口工具来显示实时时钟。先设置然后在读取通过串口显示出来。

 

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

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

相关文章

BUUCTF-WEB-刷题记录

题目地址 https://buuoj.cn/challenges[HITCON 2017]SSRFme 代码理解 进入主页后发现是代码审计/ escapeshellarg — 把字符串转码为可以在 shell 命令里使用的参数— 抑制错误输出 mkdir — 创建目录 chdir 更改目录 shell_exec — 通过 shell 环境执行命令&#x…

第5章-宏观业务分析方法-5.3-主成分分析法

5.3.1 主成分分析简介 主成分分析是以最少的信息丢失为前提,将原有变量通过线性组合的方式综合成少数几个新变量;用新变量代替原有变量参与数据建模,这样可以大大减少分析过程中的计算工作量;主成分对新变量的选取不是对原有变量的简单取舍,而是原有变量重组后的结果,因此…

Spring注册Bean系列--方法1:@Component

原文网址&#xff1a;Spring注册Bean系列--方法1&#xff1a;Component_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Spring注册Bean的方法&#xff1a;Component。 注册Bean的方法我写了一个系列&#xff0c;见&#xff1a;Spring注册Bean(提供Bean)系列--方法大全_IT利刃出鞘…

C++代码示例:排列数简单生成工具

文章目录 前言代码仓库内容代码&#xff08;有详细注释&#xff09;编译和运行命令结果总结参考资料作者的话 前言 C代码示例&#xff1a;排列数简单生成工具。 代码仓库 yezhening/Programming-examples: 编程实例 (github.com)Programming-examples: 编程实例 (gitee.com) …

PHP 数码公司运营管理系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 数码公司运营管理系统系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 php 数码公司运营管理系统 代码 https://download.csdn.net/download/qq_41…

实战教程:如何在API监控中实现高效报警和通知

问题 因一业务需要&#xff0c;想要对API服务接口添加一些监控&#xff0c;以帮助跟踪应用程序的性能、问题和用户活动等。实现监控的方式有多种多样的方式&#xff0c;以下是一些常用的方法&#xff1a; 日志记录&#xff1a; 在应用程序中添加详细的日志记录&#xff0c;包括…

如何开发一个微信小程序

微信小程序是微信公众平台推出的一种全新的应用形态&#xff0c;它具有跨平台、小巧、高效等特点&#xff0c;深受用户喜爱。 一直想学习开发小程序&#xff0c;最近找了一个教程来看&#xff0c;发现原生小程序写起来还是挺简单的&#xff0c;主要分为以下几步。 准备开发环境…

SpringCloud Alibaba - Sentinel 限流规则(案例 + JMeter 测试分析)

目录 一、Sentinel 限流规则 1.1、簇点链路 1.2、流控模式 1.2.1、直接流控模式 1.2.2、关联流控模式 a&#xff09;在 OrderController 中新建两个端点. b&#xff09;在 Sentinel 控制台中对订单查询端点进行流控 c&#xff09;使用 JMeter 进行测试 d&#xff09;分…

C语言 Cortex-A7核 PWM实验

1 实验目的 驱动开发板蜂鸣器风扇、马达进行工作 2 代码 pwm.h #ifndef __PWM_H__ #define __PWM_H__ #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_ti…

网页一直跳转到国家反诈中心页面

很明显&#xff0c;我进入的是vscode的官方下载地址。 但是一直会出现反诈中心的拦截 我们需要在控制面板中&#xff0c;找到网络&#xff0c; 将Internet 协议版本 4 (TCP/IPv4)的属性改成 使用下面的DNS 服务地址(E)&#xff1a;8.8.8.8 这样就可以正常访问相关的页面了

【day10.01】使用select实现服务器并发

用select实现服务器并发&#xff1a; linuxlinux:~/study/1001$ cat server.c #include <myhead.h>#define ERR_MSG(msg) do{\printf("%d\n",__LINE__);\perror(msg);\ }while(0)#define PORT 8880#define IP "192.168.31.38"int main(int argc, c…

动态规划算法(1)--矩阵连乘和凸多边形剖分

目录 一、动态数组 1、创建动态数组 2、添加元素 3、删除修改元素 4、访问元素 5、返回数组长度 6、for each遍历数组 二、输入多个数字 1、正则表达式 2、has.next()方法 三、矩阵连乘 1、什么是矩阵连乘&#xff1f; 2、动态规划思路 3、手推m和s矩阵 4、完…

Vue封装全局SVG组件

1.SVG图标配置 1.安装插件 npm install vite-plugin-svg-icons -D 2.Vite.config.ts中配置 import { createSvgIconsPlugin } from vite-plugin-svg-icons import path from path export default () > {return {plugins: [createSvgIconsPlugin({// Specify the icon fo…

cesium 热力图(CesiumHeatmap)

cesium 热力图 可添加、删除、显示、隐藏 完整代码 <!DOCTYPE html> <html lang="en"><head><meta charset="utf-8">

计算机毕设 大数据B站数据分析与可视化 - python 数据分析 大数据

文章目录 0 前言1 课题背景2 实现效果3 数据获取4 数据可视化5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做…

C++指针的使用

文章目录 1.C指针1.1 定义指针1.2 使用指针 2.空指针和野指针2.1 空指针2.2 野指针 3.指针所占空间4.使用const修饰指针4.1 const修饰指针4.2 const修饰常量4.3 const 既修饰指针也修饰常量 5.指针操作数组6.指针做函数参数7.使用指针知识实现冒泡排序 1.C指针 指针其实就是一…

基础数据结构之——【顺序表】(上)

从今天开始更新数据结构的相关内容。&#xff08;我更新博文的顺序一般是按照我当前的学习进度来安排&#xff0c;学到什么就更新什么&#xff08;简单来说就是我的学习笔记&#xff09;&#xff0c;所以不会对一个专栏一下子更新到底&#xff0c;哈哈哈哈哈哈哈&#xff01;&a…

掌动智能:替代JMeter的压力测试工具有哪些

JMeter是一个广泛使用的开源压力测试工具&#xff0c;但在实际应用中&#xff0c;也有一些其他优秀的替代品可供选择。本文将介绍几个可替代JMeter的压力测试工具&#xff0c;它们在功能、性能和易用性方面都具有独特优势&#xff0c;可以满足不同压力测试需求的选择。 一、Gat…

使用ExLlamaV2在消费级GPU上运行Llama2 70B

Llama 2模型中最大也是最好的模型有700亿个参数。一个fp16参数的大小为2字节。加载Llama 270b需要140 GB内存(700亿* 2字节)。 只要我们的内存够大&#xff0c;我们就可以在CPU上运行上运行Llama 2 70B。但是CPU的推理速度非常的慢&#xff0c;虽然能够运行&#xff0c;速度我…

[管理与领导-108]:IT人看清职场中的隐性规则 - 5 - 你会在不经意间被归属在不同的分类中,一旦分类定型,你就会被打上了某种标签(职场分类方法大全)

目录 前言&#xff1a; 一、关于分类 1.1 什么是分类 1.2 分类是人们理解复杂问题的一种常见方式 1.3 分类的优点与缺点 1.4 职场中的分类方法 二、职场对人的分类方法1&#xff1a;组织架构 2.1 职位和职级分类 2.2 按照部门、岗位进行分类 三、职场对人的分类方法2…