【单片机】STM32单片机,RTC实时时钟,STM32F103C8T6,程序,万年历,数字时钟

文章目录

  • 基础介绍
  • rtc.h
  • rtc.c
  • main.c

基础介绍

我以STM32F103C8T6为例,但STM32F103的RTC是通用的,STM32F103C8T6有一个原理图:

https://qq742971636.blog.csdn.net/article/details/131288390

用纽扣电池给VBAT供电(要共地),即可实现掉电后依旧走时的能力。
在这里插入图片描述

主要特性(来源于STM32中文参考手册V10.pdf):

在这里插入图片描述
在这里插入图片描述
记得加标准库文件:
在这里插入图片描述

rtc.h

#ifndef __RTC_H
#define __RTC_H	    
#include "sys.h"//时间结构体
typedef struct 
{vu8 hour;vu8 min;vu8 sec;			//公历日月年周vu16 w_year;vu8  w_month;vu8  w_date;vu8  week;		 
}_calendar_obj;					 
extern _calendar_obj calendar;	//日历结构体extern u8 const mon_table[12];	//月份日期数据表
void Disp_Time(u8 x,u8 y,u8 size);//在制定位置开始显示时间
void Disp_Week(u8 x,u8 y,u8 size,u8 lang);//在指定位置显示星期
u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;
u8 Is_Leap_Year(u16 year);//平年,闰年判断
u8 RTC_Alarm_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_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//设置时间			#endif

rtc.c

#include "sys.h"
#include "rtc.h"_calendar_obj calendar;//时钟结构体 static void RTC_NVIC_Config(void)
{	NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码u8 RTC_Init(void)
{//检查是不是第一次配置时钟u8 temp=0;RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  //if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎//{	 			BKP_DeInit();	//复位备份区域 	RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪{temp++;delay_ms(10);}if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro();		//等待RTC寄存器同步  RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成RTC_EnterConfigMode();/// 允许配置	RTC_SetPrescaler(32767); //设置RTC预分频的值RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成RTC_Set(2017,4,30,20,17,00);  //设置时间	RTC_ExitConfigMode(); //退出配置模式  BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据/*}else//系统继续计时{RTC_WaitForSynchro();	//等待最近一次对RTC寄存器的写操作完成RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成}*/RTC_NVIC_Config();//RCT中断分组设置		    				     RTC_Get();//更新时间	return 0; //ok}		 				    
//RTC时钟中断
//每秒触发一次  
//extern u16 tcnt; 
void RTC_IRQHandler(void)
{		 if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断{							RTC_Get();//更新时间   }if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断{RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	RTC_Get();				//更新时间   printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间	} 				  								 RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清闹钟中断RTC_WaitForLastTask();	  	    						 	   	 
}
//判断是否是闰年函数
//月份   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
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
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;	
}	 			   
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值: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};
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;	    
}//初始化闹钟		  
//以1970年1月1日为基准
//1970~2099年为合法年份
//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒   
//返回值:0,成功;其他:错误代码.
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,成功;其他:错误代码.
u8 RTC_Get(void)
{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);
}			  

main.c

#include "sys.h"
#include "usart.h"
#include "rtc.h"int main(void) {u8 t;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);               /* 设置NVIC中断分组2:2位抢占优先级,2位响应优先级 */delay_init();                                                   /* 延时函数初始化 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);      /* 关闭jtag,使能SWD,可以用SWD模式调试 */delay_ms(500);                                                /* 等待稳定 */uart_init(115200);RTC_Init();                //RTC初始化while (1) {if (t != calendar.sec) {t = calendar.sec;printf("20%02d-%02d-%02d %02d:%02d:%02d\r\n", calendar.w_year, calendar.w_month, calendar.w_date,calendar.hour, calendar.min, calendar.sec);switch (calendar.week) {case 0:printf("Sunday   \r\n");break;case 1:printf("Monday   \r\n");break;case 2:printf("Tuesday  \r\n");break;case 3:printf("Wednesday\r\n");break;case 4:printf("Thursday \r\n");break;case 5:printf("Friday   \r\n");break;case 6:printf("Saturday \r\n");break;}}delay_ms(10);}
}

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

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

相关文章

分布式操作系统期末复习(辽宁大学王龙主讲)

目录 一、题目 1.1 简答题 1.2 综合题 二、题目答案 2.1 简答题目答案 2.2 综合题目答案 三、期末题型分值分布 3.2 题型和分值 一、题目 1.1 简答题 1什么是中间件 22.1&#xff08;22年期末考试第一题&#xff09; 2 什么是名称解析 3 描述一下客户和服务器之间使…

论文阅读 - SegFormer

文章目录 1 概述2 模型说明2.1 总体结构2.2 Hierarchical Transformer Encoder2.3 Lightweight All-MLP Decoder 3 SegFormer和SETR的比较参考资料 1 概述 图像分割任务和图像分类任务是非常相关的&#xff0c;前者是像素级别的分类&#xff0c;后者是图像级别的分类。基于分类…

第37节:cesium 下雪效果(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><!

【Vue2】Vant2上传文件使用formData方式,base64图片转Blob再转File上传

文章目录 前言一、base64转换为 Blob 对象的方法二、使用步骤1.引入工具类js2.编写formData上传方法3.api方法中的request代码 三、实际操作1.html代码2.js代码 总结 前言 vant2上传组件传送门 使用vant2组件中的uploader组件 <van-uploader v-model"fileList" …

css实现元素纵向排列自动换列

块级元素在容器类纵向排列&#xff0c;当达到最大高度后自动换到下一列 <div id"main-div"><div class"item">1 一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容一条内容…

只用一次集合遍历实现树形结构,非递归方式

一般情况下&#xff0c;我们想要实现这种无限层级的树形结构&#xff0c;都是采用递归的方式&#xff0c;但是递归比较占用内存&#xff0c;也容易导致栈溢出&#xff0c;于是只能尝试其它的方法。 下面采用的方式&#xff0c;只需要一次集合的遍历就可以实现树形的结构。 先手…

Spring 事务管理方案和事务管理器及事务控制的API

目录 一、事务管理方案 1. 修改业务层代码 2. 测试 二、事务管理器 1. 简介 2. 在配置文件中引入约束 3. 进行事务配置 三、事务控制的API 1. PlatformTransactionManager接口 2. TransactionDefinition接口 3. TransactionStatus接口 往期专栏&文章相关导读 …

Spring Boot 中的 @EnableConfigurationProperties 注解

Spring Boot 中的 EnableConfigurationProperties 注解 在 Spring Boot 中&#xff0c;EnableConfigurationProperties 注解是一个非常有用的注解&#xff0c;它可以用于启用对特定配置类的支持。在本文中&#xff0c;我们将深入探讨 EnableConfigurationProperties 注解&…

2023年最新整理网络安全护网蓝队面试题​

如何打击黑灰产工具 全面监控和快速响应&#xff08;溯源&#xff09;&#xff1a;对黑灰进行长期跟进&#xff0c;了解黑灰产工具的传播链条和路径&#xff0c;第一时间捕获活跃的黑灰产工具&#xff08;建立特征词监控&#xff0c;数据取样、交叉分析&#xff09; 建立软件指…

ReentrantReadWriteLock源码

介绍 用一个变量如何维护多种状态 在 ReentrantLock 中&#xff0c;使用 Sync ( 实际是 AQS )的 int 类型的 state 来表示同步状态&#xff0c;表示锁被一个线程重复获取的次数。 但是&#xff0c;读写锁 ReentrantReadWriteLock 内部维护着一对读写锁&#xff0c;如果要用一…

windows下VScode搭建IDApython脚本开发环境遇到的问题

开发环境 IDA pro 7.7 vscode 最新版 python版本 与IDA 自带python 一致 3.8.10 (小版本号也必须要一致) 安装插件 下载 插件仓库的地址&#xff1a;https://github.com/ioncodes/idacode release版本 将插件安装到 IDApath/plugins/ idacode ->setting.py 设置如下:…

SpringMVC原理分析 | Hello程序

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringMVC Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架&#xff0c;本质上相当于 Servlet&#xff1b; 拥有结构最清晰的 ServletJSPJav…