23.RTC实时时钟

news2024/11/15 19:32:07

1.STM32 RTC介绍:

(1)RTC简介:

STM32的实时时钟(RTC)是一个独立的定时器。STM32的RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。

(2)RTC结构框图:

 注意:
  1. 存在:秒中断、闹钟中断;
  2. 不存在:溢出中断;
  3. 一般使用外部时钟,32.768K晶振时钟频率;

 2.RTC配置步骤:

RTC相关库函数在stm32f10x_rtc.c和stm32f10x_rtc.h文件中;
  1. 使能电源时钟和后备域时钟,开启RTC后备存储器写访问:RCC_APB1PeriphClockCmd()、PWR_BackupAccessCmd();
  2. 复位备份区域,开启外部低速振荡器:BKP_DeInit()、RCC_LSEConfig();
  3. 选择RTC时钟,并使能:RCC_RTCCLKConfig()、RCC_RTCCLKCmd();
  4. 设置RTC的分频以及配置RTC时钟:RTC_EnterConfigMode()、RTC_ExitConfigMode()、     void RTC_SetPrescaler()、void RTC_ITConfig()、void RTC_SetCounter();
  5. 更新配置,设置RTC中断分组:RTC_ExitConfigMode()、void BKP_WriteBackupRegister()、uint16_t BKP_ReadBackupRegister();
  6. 编写RTC中断服务函数:RTC_IRQHandler、FlagStatus RTC_GetFlagStatus()、RTC_ClearITPendingBit();
注意:要访问RTC和RTC备份域,必须先使能电源和后备域的时钟,然后再使能RTC后备域的写访问;

3.RTC实时时钟实验:

功能实现:设置RTC时间日期初值,在RTC秒中断内使用串口打印出RTC日期和时间,LED0指示灯闪烁提示系统运行。

(1)主函数:

#include "delay.h"
#include "led.h"
#include "key.h"
#include "usart1.h"
#include "rtc.h"



int main(){
    
    u8 i=0;    
   
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);      //设置分组
    delay_init();                                        //延时初始化
    LED_Init();  
    usart1_Init(9600);                                   //串口通信初始化
    RTC_Init();                                          //RTC初始化
    
    while(1)
       {
         
          //系统指示灯闪烁
          i++;
          if(i%20==0)
          {
             LED0=!LED0;
          }
          delay_ms(10);
          
      }       
}

(2)头文件:

#ifndef __RTC_H
#define __RTC_H

typedef unsigned char u8;
typedef unsigned short u16;

u8 RTC_Init(void);            //RTC初始化
u8 Is_Leap_Year(u16 year);    //判断是否是闰年
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);   //设置当前时间
u8 RTC_Get(void);             //获取当前时间
u8 RTC_Get_Week(u16 year,u8 month,u8 day);      //获取当前是星期几
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);      //初始化闹钟

#endif

(3)RTC实时时钟功能函数:

#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
#include "rtc.h"


//时间结构体
typedef struct
{
   u8 hour;       //时
   u8 min;        //分
   u8 sec;        //秒
   
   u16 w_year;    //年
   u8 w_month;    //月
   u8 w_date;     //日
   u8 week;       //周
   
}_calendar;   

_calendar calendar;     //定义结构体日历calendar


/*
   功能:中断管理
*/
static void RTC_NVIC_Config(void)
{
   NVIC_InitTypeDef NVIC_InitStruct;
   
   NVIC_InitStruct.NVIC_IRQChannel=RTC_IRQn;                   //设置中断通道
   NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
   NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;        //抢占优先级
   NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;               //子优先级
   NVIC_Init(&NVIC_InitStruct);
   
}



/*
   功能:RTC初始化
   变量:无
   返回值:返回值不为0:表示初始化成功;返回值为0:表示初始化不成功
*/
u8 RTC_Init()
{
   u8 time;
   
   //1.使能电源时钟和后备域时钟,开启RTC后备寄存器写访问
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);          //使能电源时钟
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);          //使能后备域时钟
   PWR_BackupAccessCmd(ENABLE);	                              //使能后备寄存器访问  
   
   //2.判断初始化是否完成
   if(BKP_ReadBackupRegister(BKP_DR1)!=0xA0A0)                 //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
   {
      //3.复位备份区域,开启外部低速振荡器
      BKP_DeInit();                                            //复位备份区域
      RCC_LSEConfig(RCC_LSE_ON);                               //开启外部低速振荡器
      
      //4.判断RTC外部时钟好坏
      while((RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET)&&(time<100))
      {
         time++;
         delay_ms(10);
          
      }
      
      //5.若出现超时,则晶振可能出现问题
      if(time>100)
      {
         return 1;      //初始化时钟失败,晶振存在问题
      }
      
      //6.选择RTC时钟,并使能
      RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);         //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
      RCC_RTCCLKCmd(ENABLE);                          //使能RTC时钟  
      
      //7.等待APB1时钟和RTC时钟同步
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      RTC_WaitForSynchro();                           //等待RTC寄存器同步
      RTC_ITConfig(RTC_IT_SEC,ENABLE);                //使能RTC秒中断
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      
      //8.设置RTC的分频以及配置RTC时钟
      RTC_EnterConfigMode();                          //进入配置模式
      RTC_SetPrescaler(32767);                        //设置RTC预分频的值,因为外部低速晶振32.768KHz,计数一次为1s
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      
      //9.设置初始化时间
      RTC_Set(2023,7,10,22,20,50);                    //设置时间
      RTC_ExitConfigMode();                           //退出配置模式
      
      //10.写入初始化完成标志值
      BKP_WriteBackupRegister(BKP_DR1,0xA0A0);        //对后备域写入标志位。方便下一次对RTC进行操作,不必再次设置时间  
       
   }
   else           //已经初始化成功,系统继续计时
   {
      RTC_WaitForSynchro();                           //等待RTC寄存器同步
      RTC_ITConfig(RTC_IT_SEC,ENABLE);                //使能RTC秒中断
      RTC_WaitForLastTask();                          //等待最近一次对RTC寄存器的写操作完成
      
   }
   
   //11.配置中断优先级
   RTC_NVIC_Config();
   
   //12.获取当前日历
   RTC_Get();     
   
   return 0;
    
}



/*
   功能:中断服务函数
   变量:无
   返回值:无
*/
void RTC_IRQHandler()
{
   if(RTC_GetITStatus(RTC_IT_SEC))           //判断是否获取到秒中断标志位
   {
      RTC_Get();                             //每一秒更新时间
      printf("RTC TIME:%d-%d-%d  %d:%d:%:\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);                            //打印出日历数据
      
   }
    
}


/*
   功能:判断是否是闰年
   变量:year:要判断的年份
   返回值:返回1则是闰年,返回0则不是闰年
   注意:
      月份:   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
*/
u8 Is_Leap_Year(u16 year)
{
   if(year%4==0) //必须能被4整除
	{ 
		if(year%100==0) 
		{ 
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除 	   
			else return 0;   
		}
      else 
         return 1;   
	}
   else 
      return 0;	
    
}


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


/*
   功能:设置日期时间(以1970年1月1日为基准,把输入的时钟转换为秒钟),1970~2099年为合法年份
   变量:变量分别为:年、月、日、时、分、秒
   返回值:返回值为1则设置失败,返回值为0则设置成功
*/
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
   u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)                            //判断年份是否在范围内
      return 1;	   
	for(t=1970;t<syear;t++)	                              //把所有年份的秒钟相加
	{
		if(Is_Leap_Year(t))
         seccount+=31622400;                             //闰年的秒钟数
		else 
         seccount+=31536000;			                     //平年的秒钟数
	}
   
	smon-=1;                                              //当月除开
   
	for(t=0;t<smon;t++)	                                 //把前面月份的秒钟数相加
	{
		seccount+=(u32)mon_table[t]*86400;                 //月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)
         seccount+=86400;                                //闰年2月份增加一天的秒钟数	   
	}
   
	seccount+=(u32)(sday-1)*86400;         //把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;              //小时秒钟数
   seccount+=(u32)min*60;	               //分钟秒钟数
	seccount+=sec;                         //最后的秒钟加上去

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟  
	PWR_BackupAccessCmd(ENABLE);	   //使能RTC和后备寄存器访问 
	RTC_SetCounter(seccount);	      //设置RTC计数器的值

	RTC_WaitForLastTask();	         //等待最近一次对RTC寄存器的写操作完成  	
	return 0;	    
   
}



/*
   功能:初始化闹钟
   变量:变量分别为:年、月、日、时、分、秒
   返回值:0表示成功,1表示失败
   注意:   以1970年1月1日为基准
*/
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
   u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)
      return 1;	   
	for(t=1970;t<syear;t++)	                     //把所有年份的秒钟相加
	{
		if(Is_Leap_Year(t))seccount+=31622400;    //闰年的秒钟数
		else seccount+=31536000;			         //平年的秒钟数
	}
   
	smon-=1;
   
	for(t=0;t<smon;t++)	                  //把前面月份的秒钟数相加
	{
		seccount+=(u32)mon_table[t]*86400;  //月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)
         seccount+=86400;                 //闰年2月份增加一天的秒钟数	   
	}
   
	seccount+=(u32)(sday-1)*86400;         //把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;              //小时秒钟数
   seccount+=(u32)min*60;	               //分钟秒钟数
	seccount+=sec;                         //最后的秒钟加上去 	
   
	//设置时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
	
	RTC_SetAlarm(seccount);
 
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
	
	return 0;	    
   
}


/*
   功能:获取当前时间
   变量:无
   返回值:0表示成功,1表示失败
*/
u8 RTC_Get()
{
   static u16 daycnt=0;
	u32 timecount=0; 
	u32 temp=0;
	u16 temp1=0;	  
   timecount=RTC_GetCounter();	 
 	temp=timecount/86400;   //得到天数(秒钟数对应的)
   
	if(daycnt!=temp)//超过一天了
	{	  
		daycnt=temp;
		temp1=1970;	//从1970年开始
		while(temp>=365)
		{				 
			if(Is_Leap_Year(temp1))       //是闰年
			{
				if(temp>=366)
               temp-=366;              //闰年的秒钟数
				else 
               {
                  temp1++;
                  break;
               }  
			}
			else
         {
            temp-=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;//闰年的秒钟数
				else 
               break; 
			}
			else 
			{
				if(temp>=mon_table[temp1])
               temp-=mon_table[temp1];//平年
				else 
               break;
			}
			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);//获取星期   
	return 0;
   
}



/*
   功能:获取当前星期几(输入日期得到星期,只允许1901-2099年)
   变量:分别是年、月、日
   返回值:星期号
*/
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{
   u16 temp2;
	u8 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);
   
}
      


注意:串口需要输入模式需要设置为浮空输入模式:

 

(4)实验结果:

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

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

相关文章

虚幻5-could not find root physics body 布料系统问题解决方法

不做不知道自己身体好&#xff0c;又碰到问题了&#xff1a; could not find root physics body 1.据说是Skeleton 和SkeletaMesh傻傻分不清楚 &#xff08;但就是排查后&#xff0c;就不是这个问题&#xff09; 2.重新创造一个Physic Asset吧 Creating a New Physics Asse…

【C语言初阶(13)】三子棋游戏(优化:多子棋实现)

文章目录 一、游戏的实现思路二、游戏的实现步骤1. 菜单函数2. 设置棋盘3. 初始化棋盘4. 打印棋盘5. 玩家下棋6. 电脑下棋7. 多子棋判断输赢8. 判断棋盘是否已满 三、模块化代码实现1. test.c2. game.h3. game.c 四、结果演示 由于模块化编程的需要&#xff0c;我们需要把整个游…

解决Bridge材质导入到Blender为白色的问题

文章目录 前言一、复现问题二、解决方案总结 前言 一、复现问题 在Bridge上看到一块不错的草皮, 导入成功后是白色材质: 二、解决方案 以前用这个方法导入过模型, 那时候还没启用汉化, 也没什么材质问题. 这次操作之前刚启用了汉化, 我猜是汉化导致: 取消勾选’新建数据’, 重…

【Java】-初识java

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录 前言…

Linux 基础之 TOP 命令详解

文章目录 一、前言二、Top说明 一、前言 TOP 命令是 Linux 常用的性能分析工具&#xff0c;能够实时显示系统中各个进程资源占用状况&#xff0c;类似于 Windows 的任务管理器。 二、Top说明 当使用一个工具时&#xff0c;对此最快的了解方式就是查看说明&#xff0c;那就是…

6.任务调度:保存和还原现场,时间片轮转

实现任务调度&#xff0c;模拟时间片 1.任务调度 在进行上下文切换时&#xff0c;需要保存和切换以下内容&#xff1a; 寄存器&#xff1a;包括通用寄存器&#xff08;如 EAX、EBX、ECX等&#xff09;和特殊寄存器&#xff08;如程序计数器 PC、堆栈指针 SP、基址指针 BP等&a…

LeetCode | C++ 动态规划——完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

目录 完全背包518. 零钱兑换 II377. 组合总和 Ⅳ参考 完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包…

【数据挖掘】时间序列教程【九】

第5章 状态空间模型和卡尔曼滤波 状态空间模型通常试图描述具有两个特征的现象 有一个底层系统具有时变的动态关系,因此系统在时间上的“状态”t 与系统在时间的状态t−1有关 .如果我们知道系统在时间上的状态t−1 ,那么我们就有了我们需要知道的一切,以便对当时的状态进行推…

12-ATF架构下的启动流程

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:Secureboot从入门到精通-[目录] 👈👈👈目录 1、Boot模型2、Boot示例3、Runtime模型4、异常跳转模型5、启动跳转模型

四川佳洲智谷:在AI潮水里加速快跑!

2023年6月28日夏季新品发布会上&#xff0c;摩卡掌舵人李国兴发布了国内首个AI原生HRSaaS整体解决方案——摩卡Eva。该产品的发布&#xff0c;标志着Moka正式成为业内第一家真正交付AI原生HRSaaS产品的公司。 随着AIGC浪潮的到来&#xff0c;越来越多的企业开始意识到人工智能的…

对抗性人工智能如何威胁您的机器学习模型

人工智能&#xff08;AI&#xff09;的出现给社会的几乎每个领域带来了革命性的变化。然而&#xff0c;人工智能的前景光明的能力也伴随着重大挑战&#xff0c;特别是在网络安全方面。 保护机器学习工作流程对于保护这些组织免受新兴威胁至关重要&#xff0c;而对抗性人工智能…

DeRPnStiNK靶机复盘

DeRPnStiNK靶机复盘 拿到靶机后我们第一步先扫描靶机地址。 nmap -sP 192.168.197.0/24扫描出我们的靶机地址。 扫描出来后我们再单独对这个ip进行扫描&#xff0c;发现开了三个端口 然后扫描一下目录&#xff0c;发现一个wordpress登陆网址和一个phpmyadmin登陆地址。 我们…

SOLARIS系统下ORACLE数据恢复案例

服务器数据恢复环境&#xff1a; 两台SPARC SOLARIS操作系统服务器通过光纤交换机共享一台存储作为集群使用。平时是一台服务器&#xff08;以下称为主服务器&#xff09;在运行&#xff0c;如果该服务器发生故障宕机&#xff0c;只需要将这台服务器关机后开启另外一台服务器&a…

Keepalived 安装与配置

安装 Keepalived apt -y install keepalived 里边有一个杠y&#xff0c;就是我安装的时候里面有yes&#xff0c;就直接是yes 添加 Keepalived 配置 安装好之后, 下一步就开始去来写这个配置文件了&#xff0c;就在这里面去建一个 etc 当中&#xff0c;就是在这个 etc 当中建一个…

关于swagger突然跳转登录页面,swagger打开跳转login页面BUG

今天建了一个初始项目&#xff0c;引入swagger之后&#xff0c;启动调用&#xff0c;却总跳转到一个登录页面&#xff0c;手足无措 启动项目后&#xff0c;打开swagger进行测试&#xff0c;但是跳转到下图页面 最后原因是导入了security的包&#xff0c;导致权限安全拦截 注释…

【Spring boot】RedisTemplate中String、Hash、List设置过期时间

Redis中String设置时间的方法 redisTemplate.opsForValue().set("loginCode","254588",2, TimeUnit.SECONDS);//过期时间2秒 redisTemplate.opsForValue().set("loginCode","254588",2, TimeUnit.MINUTES);//过期时间2分钟 redisTemp…

PyQt5及PySide2总结

PyQt5 1. 通过UIC转换成python代码后需在文件中直接添加即可运行。 方便使用代码补全但每次改动ui会生成在原文件中&#xff0c;不小心会发生覆盖&#xff0c;每次都需要将之前的代码重新补充到新生成的文件中。 from PyQt5 import QtCore, QtGui, QtWidgets if __name__ &…

向量相关的计算

点乘 线的表示 向量在某方向的投影 点与平面的关系 &#xff08;有符号的距离也可以用来做碰撞检测&#xff09; 粒子碰撞 叉乘 表示三角形法向量和三角形面积大小 注意&#xff1a;法向量和顶点索引的顺序有关&#xff08;如果符合右手坐标系&#xff0c;且逆时针为正面…

Jvm对象回收算法-JVM(九)

上篇文章介绍了jvm运行时候对象进入老年代的场景&#xff0c;以及如何避免频繁fullGC。 Jvm参数设置-JVM&#xff08;八&#xff09; 老年代分配担保机制 这个机制的目的是为了提升效率&#xff0c;在minorGC之前&#xff0c;会有三次判断&#xff0c;之后再次minorGC速度会…

Python开发项目基于改进高斯混合模型的图割算法

博主介绍&#xff1a;擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例…