stm32——RTC实时时钟
stm32——RTC实时时钟
一、关于时间
2038年问题
在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作。所有使用UNIX时间表示时间的程序都将将受其影响,因为它们以自1970年1月1日经过的秒数(忽略闰秒)来表示时间。这种时间表示法在类Unix(Unix-like)操作系统上是一个标准,并会影响以其C编程语言开发给其他大部份操作系统使用的软件。
在大部份的32位操作系统上,此“time_t”数据模式使用一个有正负号的32位元整数(signedint32)存储计算的秒数。也就是说最大可以计数的秒数为 2^31次方 可以算得:
二、RTC使用说明
"RTC"是Real Time Clock 的简称,意为实时时钟。stm32提供了一个秒中断源和一个闹钟中断源,修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块之所以具有实时时钟功能,是因为它内部维持了一个独立的定时器,通过配置,可以让它准确地每秒钟中断一次。但实际上,RTC就只是一个定时器而已,掉电之后所有信息都会丢失,因此我们需要找一个地方来存储这些信息,于是就找到了备份寄存器。其在掉电后仍然可以通过纽扣电池供电,所以能时刻保存这些数据。
配置RTC前须知:
BKP:
RTC模块和时钟配置系统的寄存器是在后备区域的(即BKP),通过BKP后备区域来存储RTC配置的数据可以让其在系统复位或待机模式下唤醒后,RTC里面配置的数据维持不变。
PWR:
PWR为电源的寄存器,我们需要用到的是电源控制寄存器(PWR_CR),通过使能PWR_CR的DBP位来取消后备区域BKP的写保护。
RTC:
由一组可编程计数器组成,分成两个模块。第一个模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC)TR_CLK 周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时,将产生一个闹钟中断。
下面讲解下配置整体过程:
第一步: 通过设置寄存器 RCC_APB1ENR 的 PWREN 和 BKPEN 位来打开电源和后备接口的时钟
RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE );
第二步:电源控制寄存器(PWR_CR) 的 DBP 位来使能对后备寄存器和 RTC 的访问
调用库函数:
PWR_BackupAccessCmd(ENABLE );
第三步:初始化复位 BKP 寄存器
调用库函数:
BKP_DeInit ();
第四步:设置 RTCCLK,如下图:
我们需要将 RTCCLK 设置为 LSE OSC 这个 32.768KHZ 的晶振。
调用的库函数:
RCC_LSEConfig (RCC_LSE_ON);
While(!RCC_GetFlagStatus (RCC_FLAG_HSERDY));//设置后需要等待启动
第五步:将 RTC 输入时钟 选择为 LSE 时钟输入并使能 RTC,等待 RTC 和 APB 时钟同步
调用库函数:
RCC_RTCCLKConfig (RCC_RTCCLKSource_LSE);//选择 LSE 为 RTC 设备的时钟
RCC_RTCCLKCmd (ENABLE );//使能
RTC RTC_WaitForSynchro();//等待同步
第六步:配置 RTC 时钟参数。
- 查询 RTOFF 位,直到 RTOFF 的值变为’1’
- 置 CNF 值为 1 ,进入配置模式
- 对一个或多个 RTC 寄存器进行写操作
- 清除 CNF 标志位,退出配置模式
- 查询 RTOFF,直至 RTOFF 位变为’1’ 以确认写操作已经完成。仅当 CNF 标志位被清除时,写操作才能进行,这个过程至少需要 3 个 RTCCLK 周期。
按照上述步骤用库函数来配置:
/* 1. 查询 RTOFF 位,直到 RTOFF 的值变为’1’ */ RTC_WaitForLastTask();//大家可以打开函数库看看这个函数内部的代码,就是查询 RTOFF的值 /* 2.置 CNF 值为 1 ,进入配置模式 3.对一个或多个 RTC 寄存器进行写操作 4.清除 CNF 标志位,退出配置模式 */ RTC_SetPrescaler(); // 这里配置了预分频值,大家可以打开函数库看看这个函数的内部的代码,里面就有包含了 2、3、4 讲述的操作。 /*
每完成一个操作一般都要查询 RTOFF 来判断是否 RTC 正在更新数据,如果是则等待它完成!!!
*/
RTC_WaitForLastTask();//等待更新结束 RTC_ITConfig(RTC_IT_SEC, ENABLE);//配置秒中断 RTC_WaitForLastTask();//等待更新结束
三、程序演示
rtc.h
#ifndef __RTC_H
#define __RTC_H
#include "stm32f10x.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;
void RCC_Configuration(void);
void RTC_Init(void);
u8 RTC_Set(u16 year,u8 mon,u8 day,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);
#endif
rtc.c
#include "rtc.h"
_calendar_obj calendar; //时钟结构体
//平均的月份日期表
const u8 mon_table[]={,,,,,,,,,,,};
/*rtc中断向量配置*/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = ;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
} void RTC_Configuration(void) {
/* 使能PWR和BKP时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE);
/* 使能对后备寄存器的访问 */
PWR_BackupAccessCmd(ENABLE);
/* 复位BKP寄存器 */
BKP_DeInit();
/* 使能LSE */
RCC_LSEConfig(RCC_LSE_ON);
/*等待启动完成 */
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {}
/* 将 RTC时钟设置为LSE这个32.768KHZ的晶振*/
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* 使能RTC Clock */
RCC_RTCCLKCmd(ENABLE);
/* 等待同步 */
RTC_WaitForSynchro();
/* 等待对RTC寄存器最后的写操作完成*/
RTC_WaitForLastTask();
/* 配置了预分频值: 设置RTC时钟周期为1s */
RTC_SetPrescaler(); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)*/
/* 等待对RTC寄存器最后的写操作完成 */
RTC_WaitForLastTask();
/* 使能RTC秒中断 */
RTC_ITConfig(RTC_IT_SEC, ENABLE);
/* 等待对RTC寄存器最后的写操作完成 */
RTC_WaitForLastTask(); void RTC_Init(void)
{
/*如果是第一次配置时钟,则执行RCC_Configuration()进行配置*/
if(BKP_ReadBackupRegister(BKP_DR1)!=0x1016)
{
RCC_Configuration();
RTC_Set(,,,,,);
GPIO_SetBits(GPIOD, GPIO_Pin_13);//点亮D1
BKP_WriteBackupRegister(BKP_DR1, 0x1016);//向执行的后备寄存器中写入用户程序数据
}
else
{
RTC_WaitForSynchro();//等待RTC寄存器同步完成
RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能RTC秒中断
RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成
GPIO_SetBits(GPIOG, GPIO_Pin_14);//点亮D2
}
NVIC_Configuration();
RTC_Get();//更新时间
}
u8 Is_Leap_Year(u16 pyear)
{
if(pyear%==)//首先需能被4整除
{
if(pyear%==)
{
if(pyear%==) return ;//如果以00结尾,还要能被400整除
else return ;
}
else
return ;
}
else
return ;
}
/*
*设置时钟
*把输入的时钟转换为秒钟
*以1970年1月1日为基准
*1970~2099年为合法年份
返回值:0,成功;其它:错误
*/
u8 RTC_Set(u16 year,u8 mon,u8 day,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 secCount=;
if(year<||year>)
return ;//³ö´í
for(t=;t<year;t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))//闰年
secCount+=;//闰年的秒钟数
else
secCount+=;
}
mon-=;//先减掉一个月再算秒数(如现在是5月10日,则只需要算前4个月的天数,再加上10天,然后计算秒数)
for(t=;t<mon;t++)
{
secCount+=(u32)mon_table[t]*;//月份秒钟数相加
if(Is_Leap_Year(year)&&t==)
secCount+=;//闰年,2月份增加一天的秒钟数
} secCount+=(u32)(day-)*;//把前面日期的秒钟数相加(这一天还没过完,所以-1)
secCount+=(u32)hour*;//小时秒钟数
secCount+=(u32)min*;//分钟秒钟数
secCount+=sec;
// RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
// PWR_BackupAccessCmd(ENABLE);
RTC_SetCounter(secCount);//设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Get();//更新时间
return ;
} /*
得到当前的时间
成功返回0,错误返回其它
*/
u8 RTC_Get(void)
{
static u16 dayCount=;
u32 secCount=;
u32 tmp=;
u16 tmp1=;
secCount=RTC_GetCounter();
tmp=secCount/;//得到天数
if(dayCount!=tmp)//超过一天
{
dayCount=tmp;
tmp1=;//从1970年开始
while(tmp>=)
{
if(Is_Leap_Year(tmp1))//是闰年
{
if(tmp>=)
tmp-=;//减掉闰年的天数
else
{
// tmp1++;
break;
}
}
else
tmp-=;//平年
tmp1++;
}
calendar.w_year=tmp1;//得到年份
tmp1=;
while(tmp>=)//超过一个月
{
if(Is_Leap_Year(calendar.w_year)&&tmp1==)/当年是闰年且轮循到2月
{
if(tmp>=)
tmp-=;
else
break;
}
else
{
if(tmp>=mon_table[tmp1])//平年
tmp-=mon_table[tmp1];
else
break;
}
tmp1++;
}
calendar.w_month=tmp1+;//得到月份,tmp1=0表示1月,所以要加1
calendar.w_date=tmp+; //得到日期,因为这一天还没过完,所以tmp只到其前一天,但是显示的时候要显示正常日期
}
tmp=secCount%;//得到秒钟数
calendar.hour=tmp/;//小时
calendar.min=(tmp%)/;//分钟
calendar.sec=(tmp%)%;//秒
return ;
}
/*
RTC时钟中断
每秒触发一次
*/
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_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);//清闹钟中断
RTC_WaitForLastTask();
}
main.c
#include "stm32f10x.h"
#include "usart1.h"
#include "LED.h"
#include "delay.h"
#include "flash.h"
#include "rtc.h"
#include "stdio.h"
int main(void)
{
u8 t=;
USART1_Config();
GPIO_Configuration();
RTC_Init();
while()
{
if(t!=calendar.sec)
{
t=calendar.sec;
printf("\r\n now is %d 年 %d 月 %d 日 %d 时 %d 分 %d 秒 \r\n ",
calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
}
Delay(0x02FFFF);
} }
stm32——RTC实时时钟的更多相关文章
- stm32 rtc 实时时钟
STM32的实时时钟是一个独立的定时器 通常会在后备区域供电端加一个纽扣电池,当主电源没有电的时,RTC不会停止工作 若VDD电源有效,RTC可以触发秒中断.溢出中断和闹钟中断 备份寄存器BKP 备份 ...
- 第43章 RTC—实时时钟
第43章 RTC—实时时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fireg ...
- 教你在树莓派使用上RTC实时时钟,不用再担心断电后时间归零的问题,开机后自动同步RTC时钟!!!
准备工作:1.系统建议使用官方最新的镜像文件 2.RTC时钟模块板(I2C接口)建议使用DS1307时钟模块,或者RTC时钟模块RTC时钟模块: 大家知道arduino的电平是5V,树莓派是3.3V, ...
- 第43章 RTC—实时时钟—零死角玩转STM32-F429系列
第43章 RTC—实时时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fireg ...
- RTC实时时钟驱动
RTC(Real-Time Clock)实时时钟为操作系统提供了一个可靠的时间,并且在断电的情况下,RTC实时时钟也可以通过电池供电,一直运行下去. RTC通过STRB/LDRB这两个ARM指令向CP ...
- RTC实时时钟
作者:宋老师,华清远见嵌入式学院讲师. 1.1 RTC介绍 在 一个嵌入式系统中,通常采用RTC 来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关机状态下它也能够正常工作(通常采用后备 ...
- RTC实时时钟-备份区域BKP--原理讲解
RTC(Real Time Clock):实时时钟 BCD码:用4位2进制来表示10以内的十进制的形式. RTC的时钟源:LSE(32.768KHZ).HSE_RTC.LSI.经过一个精密校准(RTC ...
- 【iCore3 双核心板】例程十:RTC实时时钟实验——显示日期和时间
实验指导书及代码包下载: http://pan.baidu.com/s/1jHuZcnc iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...
- 【iCore4 双核心板_ARM】例程十:RTC实时时钟实验——显示时间和日期
实验现象: 核心代码: int main(void) { /* USER CODE BEGIN 1 */ RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; ; ...
随机推荐
- java中获取本地文件的编码
import java.util.*; public class ScannerDemo { public static void main(String[] args) { System.out.p ...
- 在使用easyui,datagrid时,JSON中的如果含有换行符,则不能显示数据
http://www.xuebuyuan.com/2103538.html 每项值需处理换行符 item = item.Replace("\r\n", ""); ...
- JS面试题及答案总结
1. 截取字符串abcdefg的efg <div id="test">abcdefg</div> var mytext=document.getEleme ...
- 终端terminal配色
切换到~/.bash_profile,无此.bash_profile文件可创建 在文件内粘入以下代码: #enables colorin the terminal bash shell export ...
- 突破XSS字符数量限制执行任意JS代码
一.综述 有些XSS漏洞由于字符数量有限制而没法有效的利用,只能弹出一个对话框来YY,本文主要讨论如何突破字符数量的限制进行有效的利用,这里对有效利用的定义是可以不受限制执行任意JS.对于跨站师们来说 ...
- 如何建立批处理文件(.bat或.cmd)
如何建立批处理文件(.bat或.cmd) 建立批处理文件 批处理文件就是把多个dos命令放在一起. 批处理文件是无格式的文本文件,它包含一条或多条命令.它的文件扩展名为 .bat 或 .cmd.在命令 ...
- android-android各大手机系统打开权限管理页面
android系统五花八门,当我们去请求用户的权限的时候,总是会弹出是否允许的对话框. 而且用户一旦不小心点了拒绝,下次就不再询问了,而很多小白用户也不知道怎么去设置.这就导致了很不好的用户体验. 经 ...
- linux find 命令详解
Linux下 利用find命令删除所有vssver2.scc文件 删除所有vssver2.scc文件 这是我当初查找 Linux find 命令的目的所在 1) find / -name ‘vssv ...
- Mac OS X中打zip包时去除.DS_Store等指定文件
在Finder中的Compress “…”很好用,但是也有烦恼的时候,经常打包会包含进来一些.DS_Store文件,.DS_Store是苹果系统中保存当前目录基本信息的文件,包括图标的位置,显示方式等 ...
- 键盘上各键对应的ASCII码与扫描码
键盘上各键对应的ASCII码与扫描码 vbKeyLButton 0x1 鼠标左键vbKeyRButton 0x2 鼠标右键vbKeyCancel 0x3 CANCEL 键vbKeyMButton 0x ...