百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

「STM32」实时时钟(RTC)实验

bigegpt 2024-09-04 02:49 3 浏览

实时时钟的特征和原理

RTCCLK:时钟来源

RTC的时钟有哪些来源呢?

如图,有3个渠道

  • 来自于外部的LSE也就是外部的晶振
  • 来自于HSE的128分频
  • 来自于LSI

一般情况下我们都是采用外部晶振来提供时钟的,因为它还是很精确的。

BKP备份寄存器

RTC相关寄存器

具体设置可查看datasheet。

配置RTC寄存器

读RTC寄存器

RTC相关库函数

RTC一般配置步骤

源码

建议使用pc端浏览源码

rtc.h

#ifndef __RTC_H

#define __RTC_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 "delay.h"

#include "usart.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(2015,1,14,17,42,55); //设置时间

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

/******************************************************************

*示例说明:stm32f103rbt6程序实例

2019.6.9 RTC实验

*作者:小5

*****************************************************************/

/* Standard includes. */

#include <stdio.h>

/* Library includes.*/

#include "stm32f10x.h"

/* Hardware Library */

#include "usart.h"

#include "delay.h"

#include "led.h"

#include "key.h"

#include "exti.h"

#include "wdg.h"

#include "timer.h"

#include "rtc.h"

int main(void)

{

u8 t=0;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

delay_init();

usart_config();

led_gpio_init();

RTC_Init(); //RTC初始化

while(1)

{

if(t!=calendar.sec)

{

t=calendar.sec;

switch(calendar.week)

{

case 0:

printf("1");

break;

case 1:

printf("2");

break;

case 2:

printf("3");

break;

case 3:

printf("4");

break;

case 4:

printf("5");

break;

case 5:

printf("6");

break;

case 6:

printf("7");

break;

}

}

delay_ms(10);

}

}


喜欢我文章的朋友,欢迎关注、点赞、评论、交流。版权个人所有,转载请注明出处。

相关推荐

当Frida来“敲”门(frida是什么)

0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...

服务端性能测试实战3-性能测试脚本开发

前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...

Springboot整合Apache Ftpserver拓展功能及业务讲解(三)

今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...

Linux和Windows下:Python Crypto模块安装方式区别

一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...

Python 3 加密简介(python des加密解密)

Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...

怎样从零开始编译一个魔兽世界开源服务端Windows

第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...

附1-Conda部署安装及基本使用(conda安装教程)

Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...

如何配置全世界最小的 MySQL 服务器

配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...

如何使用Github Action来自动化编译PolarDB-PG数据库

随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...

面向NDK开发者的Android 7.0变更(ndk android.mk)

订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...

信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要

问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...

OpenSSH 安全漏洞,修补操作一手掌握

1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...

Linux:lsof命令详解(linux lsof命令详解)

介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...

幻隐说固态第一期:固态硬盘接口类别

前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...

新品轰炸 影驰SSD多款产品登Computex

分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...