stm32——RTC实时时钟

一、关于时间

  2038年问题

  在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作。所有使用UNIX时间表示时间的程序都将将受其影响,因为它们以自1970年1月1日经过的秒数(忽略闰秒)来表示时间。这种时间表示法在类Unix(Unix-like)操作系统上是一个标准,并会影响以其C编程语言开发给其他大部份操作系统使用的软件。

  在大部份的32位操作系统上,此“time_t”数据模式使用一个有正负号的32位元整数(signedint32)存储计算的秒数。也就是说最大可以计数的秒数为 2^31次方 可以算得:

2^31/3600/24/365 ≈ 68年
所以依照此“time_t”标准,在此格式能被表示的最后时间是2038年1月19日03:14:07,星期二(UTC)。超过此一瞬间,时间将会被掩盖(wrap around)且在内部被表示为一个负数,并造成程序无法工作,因为它们无法将此时间识别为2038年,而可能会依个别实作而跳回1970年或1901年。
对于PC机来说,时间开始于1980年1月1日,并以无正负符号的32位整数的形式按秒递增,这与UNIX时间非常类似。可以算得:
2^32/3600/24/365 ≈ 136年
到2116年,这个整数将溢出。
Windows NT使用64位整数来计时。但是,它使用100纳秒作为增量单位,且时间开始于1601年1月1日,所以NT将遇到2184年问题。
苹果公司声明,Mac在29,940年之前不会出现时间问题!

二、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 时钟参数。

  1. 查询 RTOFF 位,直到 RTOFF 的值变为’1’
  2. 置 CNF 值为 1 ,进入配置模式
  3. 对一个或多个 RTC 寄存器进行写操作
  4. 清除 CNF 标志位,退出配置模式
  5. 查询 RTOFF,直至 RTOFF 位变为’1’ 以确认写操作已经完成。仅当 CNF 标志位被清除时,写操作才能进行,这个过程至少需要 3 个 RTCCLK 周期。

  按照上述步骤用库函数来配置:

/* 1.    查询 RTOFF 位,直到 RTOFF 的值变为’1’ */RTC_WaitForLastTask();//大家可以打开函数库看看这个函数内部的代码,就是查询 RTOFF的值/*2.置 CNF 值为 1 ,进入配置模式3.对一个或多个 RTC 寄存器进行写操作4.清除 CNF 标志位,退出配置模式*/RTC_SetPrescaler(32767); // 这里配置了预分频值,大家可以打开函数库看看这个函数的内部的代码,里面就有包含了 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[12]={31,28,31,30,31,30,31,31,30,31,30,31};
/*rtc中断向量配置*/
void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;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(32767); /* 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(2016,5,11,9,7,55);GPIO_SetBits(GPIOD, GPIO_Pin_13);//点亮D1BKP_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==0)//首先需能被4整除{if(pyear%100==0){if(pyear%400==0)    return 1;//如果以00结尾,还要能被400整除else    return 0;}elsereturn 1;}elsereturn 0;
}
/*
*设置时钟
*把输入的时钟转换为秒钟
*以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=0;if(year<1970||year>2099)return 1;//³ö´ífor(t=1970;t<year;t++)    //把所有年份的秒钟相加{if(Is_Leap_Year(t))//闰年secCount+=31622400;//闰年的秒钟数elsesecCount+=31536000;    }mon-=1;//先减掉一个月再算秒数(如现在是5月10日,则只需要算前4个月的天数,再加上10天,然后计算秒数)for(t=0;t<mon;t++){secCount+=(u32)mon_table[t]*86400;//月份秒钟数相加if(Is_Leap_Year(year)&&t==1)secCount+=86400;//闰年,2月份增加一天的秒钟数}secCount+=(u32)(day-1)*86400;//把前面日期的秒钟数相加(这一天还没过完,所以-1)secCount+=(u32)hour*3600;//小时秒钟数secCount+=(u32)min*60;//分钟秒钟数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;
}/*
得到当前的时间
成功返回0,错误返回其它
*/
u8 RTC_Get(void)
{static u16 dayCount=0;u32 secCount=0;u32 tmp=0;u16 tmp1=0;secCount=RTC_GetCounter();tmp=secCount/86400;//得到天数if(dayCount!=tmp)//超过一天{dayCount=tmp;tmp1=1970;//从1970年开始while(tmp>=365){if(Is_Leap_Year(tmp1))//是闰年{if(tmp>=366)    tmp-=366;//减掉闰年的天数else{//    tmp1++;break;}}elsetmp-=365;//平年tmp1++;}calendar.w_year=tmp1;//得到年份tmp1=0;while(tmp>=28)//超过一个月{if(Is_Leap_Year(calendar.w_year)&&tmp1==1)/当年是闰年且轮循到2月{if(tmp>=29)    tmp-=29;elsebreak;}else{if(tmp>=mon_table[tmp1])//平年tmp-=mon_table[tmp1];elsebreak;}tmp1++;}calendar.w_month=tmp1+1;//得到月份,tmp1=0表示1月,所以要加1calendar.w_date=tmp+1;    //得到日期,因为这一天还没过完,所以tmp只到其前一天,但是显示的时候要显示正常日期}tmp=secCount%86400;//得到秒钟数calendar.hour=tmp/3600;//小时calendar.min=(tmp%3600)/60;//分钟calendar.sec=(tmp%3600)%60;//秒return 0;
}
/*
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= 0 ; USART1_Config(); GPIO_Configuration(); RTC_Init(); while( 1 ) { 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时钟配置相关推荐

  1. STM32 RTC时钟掉电日期不更新 STM32 HAL库RTC时钟配置

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 STM32 RTC时钟掉电日期不更新 & STM32 HAL库RTC时钟配置 一.STM32CubeMX RTC配置 二.RT ...

  2. STM32 RCC时钟配置

    STM32 RCC时钟配置 时钟源 外部高速晶振HSE,通常是8MHz. 内部高速晶振HSI,8MHz. 外部低速晶振LSE,32.768KHz. 内部低速晶振LSI,40KHz. 时钟源选择 通常使 ...

  3. linux c设置rtc时间,linux内核的rtc时钟配置

    linux内核的rtc时钟配置,见Device Drivers  --->Real Time Clock .我们的RTC主要是有三种接口配置,一种是I2C接口,一种是SPI接口,还有就是CPU自 ...

  4. 纠结的STM32 RTC时钟源LSE

    一开始,所有实验都是在神舟板上去完成,根本就没有发现RTC的问题.直到我们自己画板来后调试时,才发现STM32 RTC的外部时钟源存在问题. 这也算是STM32的一个鸡肋,对于LSE外部晶振太过于苛刻 ...

  5. STM32的时钟配置——时钟树解析

    此文章由于讲得较详细因此篇幅较长,请带着一点耐心去读,相信会有收获! 目录 STM32为什么要有复杂的时钟系统 详解STM32时钟系统 STM32有几个时钟源 关于时钟输出 软件配置时钟 STM32为 ...

  6. 火牛单片机rtc时钟配置_怎么用STM32F103RTC进行时钟的配置

    #include "sys.h" #include "delay.h" #include "usart.h" #include " ...

  7. STM32 RTC时钟源LSE

    一开始,所有实验都是在神舟板上去完成,根本就没有发现RTC的问题.直到我们自己画板来后调试时,才发现STM32 RTC的外部时钟源存在问题. 这也算是STM32的一个鸡肋,对于LSE外部晶振太过于苛刻 ...

  8. STM32 RTC时钟读取时间

    文章目录 一.RTC简介 1.1 RTC 1.2 RTC特征 1.3 RTC原理框图 1.4 RTC工作流程 1.5 RTC时钟选择 1.6 RTC复位过程 1.7 RTC中断 二.CubeMX配置 ...

  9. 关于STM32 RTC时钟使用内部/外部晶振的切换方法

    对于使用STM32单片开发项目的同志,经常会使用到STM32的RTC功能,而在配置RTC的功能时需要配置晶振的使用,可以使用内部晶振或外部晶振,配置流程参考官方的示例代码即可. 但在之前的项目中遇到一 ...

最新文章

  1. 随身风暴英雄接入云信,玩家即时通讯轻松搞定
  2. 《剑指offer》二叉树的深度
  3. 如何生成自己的Yaas Service yaas
  4. 7-2 页面置换算法--LRU (50 分)
  5. apache php日志配置,HTML_初学:apache与php基本配置,1、APACHE的日志主要分为“ - phpStudy...
  6. 几款基于ODE的机器人仿真软件
  7. 《SEM长尾搜索营销策略解密》一一2.3 一个长尾周期倒下来,还有千千万万个站起来...
  8. 聊一聊DTM子事务屏障功能之SQL Server版
  9. 4个万兆光口+16个千兆光口+8个千兆combo光电复用口万兆三层核心工业以太网交换机HY5700-854XG16GX8GC
  10. 人脸识别技术介绍和表情识别最新研究
  11. CaptchaBuilder验证码不显示
  12. 使用lightGBM进行验证的demo
  13. (转)Nagios 配置及监控
  14. python-pygame与pymunk-台球游戏
  15. lua语言定义一个比较两值大小的函数
  16. 【Maven】阿里云镜像仓库的配置
  17. 实施SAP系统需要注意的三要素
  18. 圆周率估算的一种方式——蒲丰投针
  19. iOS 最完整小说阅读器(Swift-Demo)
  20. maven依赖报错问题处理

热门文章

  1. ASTM D7031 木塑复合材料产品物理力学性能评价指导标准
  2. 你的模型是最好的还是最幸运的?选择最佳模型时如何避免随机性
  3. 华为最近面试后要等多久才有消息?(有经验进)
  4. 手写MySQL常用语法
  5. 如何配置Stash以便与4EVERLAND一起使用
  6. ClickHouse快速的开源OLAP(联机分析处理)数据库管理系统
  7. LeetCode刷题笔记:17.电话号码的字母组合
  8. 男人来自火星,女人来自金星(摘要)
  9. 枚举类与注解的详细介绍
  10. 如何修改系统磁盘盘符