免费做网站app,网站建设選宙斯王,亚马逊网站推广怎么做,网站运作方式文章目录 前言一、RTC是什么#xff1f;RTC的工作原理#xff1f;二、库函数以及示例1.标准库函数2.示例代码 总结 前言
STM32 的实时时钟#xff08;RTC#xff09;是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数的计数器#xff0c;在相应软件配置下#xf… 文章目录 前言一、RTC是什么RTC的工作原理二、库函数以及示例1.标准库函数2.示例代码 总结 前言
STM32 的实时时钟RTC是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数的计数器在相应软件配置下可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC 模块和时钟配置系统 (RCC_BDCR 寄存器)是在后备区域即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后会自动禁止访问后备寄存器和 RTC以防止对后备区域 (BKP) 的意外写操作。所以在要设置时间之前 先要取消备份区域BKP写保护。
unix时间戳 Unix 时间戳是从1970年1月1日UTC/GMT的午夜开始所经过的秒数不考虑闰秒。 STM32相较于51的外接时钟寄存器DS1302STM32内部计时是没有年月日时分秒寄存器的。STM32是利用单独的秒寄存器以1970年1月1日为0进行每秒自增的定时器计数。 当前为1723138563秒转换后再加上1970年就是当前时间 闰秒由于地球自转的影响每天有一定的计数误差闰秒规定当误差大于0.9秒。当前计数分钟会出现61秒然后下一分钟再从0开始计数。 这样减少了硬件的负担但是增加软件转换的麻烦。于是STM32定义了time.h 头文件。专门用于转换时间。
BKP备份寄存器是42个16位的寄存器中容量小容量为10个16位的寄存器20字节可用来存储84个字节的用户应用程序数据。他们处在备份域里当VDD电源被切断他们仍然由VBAT维持供电。当系统在待机模式下被唤醒或系统复位或电源复位时他们也不会被复位。 此外BKP控制寄存器用来管理侵入检测和RTC校准功能。 复位后对备份寄存器和RTC的访问被禁止并且备份域被保护以防止可能存在的意外的写操作。
一、RTC是什么RTC的工作原理
RTC(Real Time Clock):实时时钟,是指可以像时钟一様输出实际时间的电子设备一般会是集成电路因此也称为时钟芯片。总之RTC只是个能靠电池维持运行的32位定时器并不像实时时钟芯片读出来就是年月日。RTC就只一个定时器而已掉电之后所有信息都会丢失因此我们需要找一个地方来存储这些信息于是就找到了备份寄存器BKP。因为它掉电后仍然可以通过纽扣电池供电所以能时刻保存这些数据。 STM32 的实时时钟RTC是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数的计数器在相应软件配置下可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
TIPS:RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后会自动禁止访问后备寄存器和 RTC以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前 先要取消备份区域BKP写保护 下图中RTC数据有三条RTC_Second(秒中断)、RTC_Overflow(溢出事件)和RTC_Alarm(闹钟中断)以及最下方的WKUP(唤醒常用于PA0引脚)用来进行中断控制增加了闹钟和休眠唤醒等功能。 RTC_PRL计数目标写入n就是n1分频
RTC_DIV自减计数器每来一个输入时钟DIV的值自减一次减到0后再来一个时钟从PRL获取到重装值继续自减
RTC_CNTUnix时间戳的秒计数器
RTC_ALR闹钟寄存器 当CNTALR时会产生RTC_Alarm闹钟信号就能进入右边的中断系统同时也可以让STM32退出待机模式
中断RTC_Second秒中断开启后每秒进一次RTC中断RTC_Overflow溢出中断CNT溢出RTC_Alarm闹钟中断CNTALR时会触发中断同时也可以让STM32退出待机模式
图中浅灰色的部分都是属于备份域的,在VDD掉电时可在VBAT的驱动下继续运行.这部分仅包括RTC的分频器,计数器,和闹钟控制器.若VDD电源有效,RTC可以触发RTC_Second(秒中断)、RTC_Overflow(溢出事件)和RTC_Alarm(闹钟中断).从结构图可以看到到,其中的定时器溢出事件无法被配置为中断.如果STM32原本处于待机状态,可由闹钟事件或WKUP事件(外部唤醒事件,属于EXTI模块,不属于RTC)使它退出待机模式.闹钟事件是在计数器RTC_CNT的值等于闹钟寄存器RTC_ALR的值时触发的.
因为RTC的寄存器是属于备份域,所以它的所有寄存器都是16位的.它的计数RTC_CNT的32位由RTC_CNTL和RTC_CNTH两个寄存器组成,分别保存计数值的低16位和高16位.在配置RTC模块的时钟时,把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR_CLK RTCCLK/37768 1Hz,计时周期为1秒,计时器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值加1(常用)
RTC只是一个时钟但与RTC相连的有两个系统时钟一个是APB1接口的PCLK1另一个是RTC时钟。这样RTC功能也就分为两个部分第一部分APB1接口部分与APB1总线相连MCU也就是通过这条总线对其进行读写操作。另一部分RTC核由一系列可编程计数器组成这部分又再细分为两个组件预分频模块与32位可编程计数器。预分频模块用来产生最长为1秒的RTC时间基准而32位的可编程的计数器可被初始化为当前的系统时间。
电路图简化 流程(会按照流程写出代码即可) 1. 使能电源时钟和备份区域时钟 2. 取消备份区写保护 3. 复位备份区域开启外部低速振荡器 4. 选择 RTC 时钟并使能 5. 设置 RTC 的分频以及配置 RTC 时钟 6. 更新配置设置 RTC 中断分组 7. 编写中断服务函数
RTC 相关的库函数在文件 stm32f10x_rtc.c 和 stm32f10x_rtc.h 文件中 BKP 相关的库函数在 文件 stm32f10x_bkp.c 和文件 stm32f10x_bkp.h 文件中
二、库函数以及示例
1.标准库函数
1、RTC时钟源和时钟操作函数 void RCC_RTCCLKConfig(uint32_t CLKSource);//时钟源选择 void RCC_RTCCLKCmd(FunctionalState NewState);//时钟使能 2、RTC初始化函数 ErrorStatus RTC_Init(RTC_InitTypeDef* RTC_InitStruct); trypedef struct { uint32_t RTC_HourFormat;//小时格式24/12 uint32_t RTC_AsynchPrediv;//异步分频系数 uint32_t RTC_SynchPrediv;//同步分频系数 }RTC_InitTypeDef; 3、日历配置相关函数 ErrorStatus RTC_SetTime(uint32_t RTC_Format,RTC_TimeTypeDef* RTC_TimeStruct);数值当前时间 void RTC_GetTime(uint32_t RTC_Format,RTC_TimeTypeDef* RTC_TimeStruct); ErrorStatus RTC_SetDate(uint32_t RTC_Format,RTC_Dae TypeDef* RTC_DataStruct); void RTC_GetDate(uint32_t RTC_Format,RTC_Date TypeDef* RTC_DateStruct); uint32_t RTC_GetSubSecond(void); 4、RTC闹钟相关函数 ErrorStatus RTC_AlarmCmd(uint32_t RTC_Alarm,FunctionalState NewState); void RTC_SetAlarm(); void RTC_GetAlarm(); void RTC_AlarmSubSecondConfig(); uint32_t RTC_GetAlarmSubSecond(uint32_t RTC_Alarm); 5、RTC周期唤醒相关函数 void RTC_WakeUpClockConfig(); void RTC_SetWakeUpCounter(); uint32_t RTC_GetWakeUpCounter(void); RTC_WakeUpCmd(DISABLE);//关闭WAKEUP 6、RTC中断配置以及状态相关函数 void RTC_ITConfig(); FlagStatus RTC_GetFlgStatus(uint32_t RTC_FLAG); void RTC_ClearFlag(uint32_t RTC_FLAG); ITStatus RTC_GetITStatus(uint32_t RTC_IT); void RTC_ClearITPendingBit(); 7、RTC相关约束函数 void RTC_WriteProtectionCmd();//取消写保护 ErrorStatus RTC_EnterInitNode();//进入配hi模式RTC_ISR_INITF位设置位1 void RTC_ExitInitMode(void);//退出初始化模式 8、其他函数 uint32_t RTC_ReadBackupRegister(); void RTC_WriteBackupRegister(); void RTC_ITConfig();
RTC_ITConfig 使能或者失能指定的 RTC 中断 RTC_EnterConfigMode 进入 RTC 配置模式 RTC_ExitConfigMode 退出 RTC 配置模式 RTC_GetCounter 获取 RTC 计数器的值 RTC_SetCounter 设置 RTC 计数器的值 RTC_SetPrescaler 设置 RTC 预分频的值 RTC_SetAlarm 设置 RTC 闹钟的值 RTC_GetDivider 获取 RTC 预分频分频因子的值 RTC_WaitForLastTask 等待最近一次对 RTC 寄存器的写操作完成 RTC_WaitForSynchro 等待 RTC 寄存器(RTC_CNT, RTC_ALR and RTC_PRL)与 RTC 的 APB 时钟同步 RTC_GetFlagStatus 检查指定的 RTC 标志位设置与否 RTC_ClearFlag 清除 RTC 的待处理标志位 RTC_GetITStatus 检查指定的 RTC 中断发生与否 RTC_ClearITPendingBit 清除 RTC 的中断待处理位 使能电源时钟和备份区域时钟 前面已经介绍了我们要访问 RTC 和备份区域就必须先使能电源时钟和备份区域时钟。 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); 取消备份区写保护 要向备份区域写入数据就要先取消备份区域写保护写保护在每次硬复位之后被使能否则是无法向备份区域写入数据的。我们需要用到向备份区域写入一个字节来标记时钟已经配置过了这样避免每次复位之后重新配置时钟。 取消备份区域写保护的库函数实现方法是 PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问 复位备份区域开启外部低速振荡器 在取消备份区域写保护之后我们可以先对这个区域复位以清除前面的设置当然这个 操作不要每次都执行因为备份区域的复位将导致之前存在的数据丢失所以要不要复位要看情况而定。然后我们使能外部低速振荡器注意这里一般要先判断 RCC_BDCR 的 LSERDY位来确定低速振荡器已经就绪了才开始下面的操作。 BKP_DeInit();//复位备份区域 RCC_LSEConfig(RCC_LSE_ON);// 开启外部低速振荡器
4.选择 RTC 时钟并使能。 这里我们将通过 RCC_BDCR 的 RTCSEL 来选择选择外部 LSI 作为 RTC 的时钟。然后通过RTCEN 位使能 RTC 时钟。 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟
对于 RTC 时钟的选择还有 RCC_RTCCLKSource_LSI 和 RCC_RTCCLKSource_HSE_Div128两个顾名思义前者为 LSI后者为 HSE 的 128 分频这在时钟系统章节有讲解过。使能 RTC 时钟的函数是 RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
5.设置 RTC 的分频以及配置 RTC 时钟。 在开启了 RTC 时钟之后我们要做的就是设置 RTC 时钟的分频数通过 RTC_PRLH 和RTC_PRLL 来设置然后等待 RTC 寄存器操作完成并同步之后设置秒钟中断。然后设置RTC 的允许配置位RTC_CRH 的 CNF 位 设置时间其实就是设置 RTC_CNTH 和 RTC_CNTL两个寄存器。 下面我们一一这些步骤用到的库函数在进行 RTC 配置之前首先要打开允许配置位(CNF)库函数是 RTC_EnterConfigMode();/// 允许配置 在配置完成之后千万别忘记更新配置同时退出配置模式函数是
RTC_ExitConfigMode();//退出配置模式 更新配置
设置 RTC 时钟分频数 库函数是 void RTC_SetPrescaler(uint32_t PrescalerValue); 这个函数只有一个入口参数就是 RTC 时钟的分频数很好理解。 然后是设置秒中断允许 RTC 使能中断的函数是 void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState)
这个函数的第一个参数是设置秒中断类型这些通过宏定义定义的。 对于使能秒中断方法是 RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断 下一步便是设置时间了设置时间实际上就是设置 RTC 的计数值时间与计数值之间是需要换算的。库函数中设置 RTC 计数值的方法是 void RTC_SetCounter(uint32_t CounterValue)//最后在配置完成之后
6.更新配置设置 RTC 中断分组。 在设置完时钟之后我们将配置更新同时退出配置模式这里还是通过 RTC_CRH 的 CNF 来实现。 RTC_ExitConfigMode();//退出配置模式更新配置 在退出配置模式更新配置之后我们在备份区域 BKP_DR1 中写入 0X5050 代表我们已经初始化过时钟了下次开机或复位的时候先读取 BKP_DR1 的值然后判断是否是 0X5050 来决定是不是要配置。接着我们配置 RTC 的秒钟中断并进行分组。
往备份区域写用户数据的函数是 void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data)
这个函数的第一个参数就是寄存器的标号了这个是通过宏定义定义的。 比如我们要往 BKP_DR1 写入 0x5050方法是 BKP_WriteBackupRegister(BKP_DR1, 0X5050);
同时有写便有读读取备份区域指定寄存器的用户数据的函数是 uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR)
编写中断服务函数 我们要编写中断服务函数在秒钟中断产生的时候读取当前的时间值并显示到 oled 模块上。
2.示例代码
代码如下示例
//初始化配置
void MyRTC_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //开启PWR的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); //开启BKP的时钟/*备份寄存器访问使能*/PWR_BackupAccessCmd(ENABLE); //使用PWR开启对备份寄存器的访问//通过写入备份寄存器的标志位判断RTC是否是第一次配置if (BKP_ReadBackupRegister(BKP_DR1) ! 0xA5A5){RCC_LSEConfig(RCC_LSE_ON); //开启LSE时钟while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) ! SET); //等待LSE准备就绪RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择RTCCLK来源为LSERCC_RTCCLKCmd(ENABLE); //RTCCLK使能RTC_WaitForSynchro(); //等待同步RTC_WaitForLastTask(); //等待上一次操作完成RTC_SetPrescaler(32768 - 1); //设置RTC预分频器预分频后的计数频率为1HzRTC_WaitForLastTask(); //等待上一次操作完成MyRTC_SetTime(); //设置时间调用此函数全局数组里时间值刷新到RTC硬件电路//在备份寄存器写入自己规定的标志位用于判断RTC是不是第一次执行配置BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);}else //RTC不是第一次配置{RTC_WaitForSynchro(); //等待同步RTC_WaitForLastTask(); //等待上一次操作完成}
}//设置时间
uint16_t MyRTC_Time[] {2023, 1, 1, 23, 59, 55};
void MyRTC_SetTime(void)
{time_t time_cnt; //定义秒计数器数据类型struct tm time_date; //定义日期时间数据类型time_date.tm_year MyRTC_Time[0] - 1900; //将数组的时间赋值给日期时间结构体time_date.tm_mon MyRTC_Time[1] - 1;time_date.tm_mday MyRTC_Time[2];time_date.tm_hour MyRTC_Time[3];time_date.tm_min MyRTC_Time[4];time_date.tm_sec MyRTC_Time[5];time_cnt mktime(time_date) - 8 * 60 * 60; //调用mktime函数将日期时间转换为秒计数器格式//- 8 * 60 * 60为东八区的时区调整RTC_SetCounter(time_cnt); //将秒计数器写入到RTC的CNT中RTC_WaitForLastTask(); //等待上一次操作完成
}
江科大STM32例程
#include stm32f10x.h // Device header
#include time.huint16_t MyRTC_Time[] {2023, 1, 1, 23, 59, 55};void MyRTC_SetTime(void);void MyRTC_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);PWR_BackupAccessCmd(ENABLE);if (BKP_ReadBackupRegister(BKP_DR1) ! 0xA5A5){BKP_DeInit();RCC_LSEConfig(RCC_LSE_ON);while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) RESET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro();RTC_WaitForLastTask();RTC_SetPrescaler(32767);RTC_WaitForLastTask();MyRTC_SetTime();BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);}else{RTC_WaitForSynchro();RTC_WaitForLastTask();}
}/*
void MyRTC_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);PWR_BackupAccessCmd(ENABLE);if (BKP_ReadBackupRegister(BKP_DR1) ! 0xA5A5){BKP_DeInit();RCC_LSICmd(ENABLE);while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) RESET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro();RTC_WaitForLastTask();RTC_SetPrescaler(40000 - 1);RTC_WaitForLastTask();MyRTC_SetTime();BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);}else{RCC_LSICmd(ENABLE);while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) RESET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro();RTC_WaitForLastTask();}
}*/void MyRTC_SetTime(void)
{time_t time_cnt;struct tm time_date;time_date.tm_year MyRTC_Time[0] - 1900;time_date.tm_mon MyRTC_Time[1] - 1;time_date.tm_mday MyRTC_Time[2];time_date.tm_hour MyRTC_Time[3];time_date.tm_min MyRTC_Time[4];time_date.tm_sec MyRTC_Time[5];time_cnt mktime(time_date) - 8 * 60 * 60;RTC_SetCounter(time_cnt);RTC_WaitForLastTask();
}void MyRTC_ReadTime(void)
{time_t time_cnt;struct tm time_date;time_cnt RTC_GetCounter() 8 * 60 * 60;time_date *localtime(time_cnt);MyRTC_Time[0] time_date.tm_year 1900;MyRTC_Time[1] time_date.tm_mon 1;MyRTC_Time[2] time_date.tm_mday;MyRTC_Time[3] time_date.tm_hour;MyRTC_Time[4] time_date.tm_min;MyRTC_Time[5] time_date.tm_sec;
} 总结
RTC的工作流程通常分为初始化和运行两个阶段。在初始化阶段我们需要配置RTC的时钟源、预分频器、日历寄存器等参数并启用相应的中断或事件。在运行阶段RTC会根据预设的时钟源和分频器递增计数器同时更新日历信息处理闹钟事件等。通过合理配置和管理我们可以充分发挥RTC模块的功能和性能。