12:STM32 |
您所在的位置:网站首页 › 实时时钟模块的作用 › 12:STM32 |
目录 一:时间相关 1:Unix时间戳 2: UTC/GMT 3:时间戳转化 二:BKP 1:简历 2:基本结构 三: RTC 1:简历 2: 框图 3:RTC基本结构 4:RTC操作注意 四:案例 A:读写备份寄存器 1:连接图 2: 步骤 3: 代码 B:实时时钟 1:连接图 2:函数介绍 3:代码 一:时间相关 1:Unix时间戳Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间 2: UTC/GMTGMT(Greenwich Mean Time)格林尼治标准时间是一种以地球自转为基础的时间计量系统。它将地球自转一周的时间间隔等分为24小时,以此确定计时标准 UTC(Universal Time Coordinated)协调世界时是一种以原子钟为基础的时间计量系统。它规定铯133原子基态的两个超精细能级间在零磁场下跃迁辐射9,192,631,770周所持续的时间为1秒。当原子钟计时一天的时间与地球自转一周的时间相差超过0.9秒时,UTC会执行闰秒来保证其计时与地球自转的协调一致 3:时间戳转化C语言的time.h模块提供了时间获取和时间戳转换的相关函数,可以方便地进行秒计数器、日期时间和字符串之间的转换 二:BKP 1:简历BKP(Backup Registers)备份寄存器 BKP可用于存储用户应用程序数据。当VDD(2.0~3.6V)电源被切断,他们仍然由VBAT(1.8~3.6V)维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位 TAMPER引脚产生的侵入事件将所有备份寄存器内容清除 RTC引脚输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲 存储RTC时钟校准寄存器 用户数据存储容量: 20字节(中容量和小容量)/ 84字节(大容量和互联型) 2:基本结构BKP处于后备区域, 后备区域的作用: 当VDD主电源掉电时,后备区域仍然可以由VBAT的备用电池供电; 当VDD主电源上电时,后备区域供电会由VBAT切换到VDD, 也就是主电源有电时,VBAT不会用到,这样可以节省电池电量 BKP里主要有 : 数据寄存器、控制寄存器、状态寄存器和RTC时钟校准寄存器, 其中数据寄存器是主要部分,用来存储数据的, 每个数据奇存器都是16位的, 一个数据奇存器可以存2个字节, 对于小的来说里面有DR1、DR2、一直到、DR10一共10个数据寄存器; 对于大容量和互联型里面有DR11~DR42的数据寄存器 可以从PC13位置的TAMPERS脚引入一个检测信号器, 当TAMPER产生上升沿或者下降沿时, 清除BKP所有的内容,以保证安全-----对应简历中的第三条 时钟输出, 可以把RTC的相关时钟, 从PC13位置的RTC引脚输出出去,供外部使用, 其中,输出校准时钟时, 再配合这个校准寄存器,可以对RTC的误差进行校准 三: RTC 1:简历RTC(Real Time Clock)实时时钟 RTC是一个独立的定时器,可为系统提供时钟和日历的功能 RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD(2.0~3.6V)断电后可借助VBAT(1.8~3.6V)供电继续走时 32位的可编程计数器,可对应Unix时间戳的秒计数器 20位的可编程预分频器,可适配不同频率的输入时钟 可选择三种RTC时钟源: HSE时钟除以128(通常为8MHz/128) LSE振荡器时钟(通常为32.768KHz) LSI振荡器时钟(40KHz) 2: 框图 3:RTC基本结构3个时钟,选择一个当作RTCCLK, 之后RTCCLK先通过预分频器,对时钟进行分频. 余数寄存器(DIV) : 是一个自减计数器,存储当前的计数值. 重装寄存器 : 是计数目标,决定分频值, 分频之后,得到1Hz的秒计数信号, 通向32位计数器一秒自增一次 3个信号可以触发中断, 分别是秒信号、计数器溢出信号和闹钟信号, 三个信号先通过中断输出控制, 三个信号先通过中断输出控制, 使能的中断才能通向NVIC 4:RTC操作注意执行以下操作将使能对BKP和RTC的访问: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 设置PWR_CR的DBP,使能对BKP和RTC的访问 若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器--------其实这个操作在库函数中, 每个写奇存器的函数, 它都自动帮我们加上了这个操作, 所以我们就不用再单独调用代码,进入配置模式了 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器 四:案例 A:读写备份寄存器 1:连接图 2: 步骤1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 2: 设置PWR_CR的DBP,使能对BKP和RTC的访问 3: 代码简单的实现BKP的功能 #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Key.h" void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } uint8_t Key_GetNum(void) { uint8_t KeyNum = 0; if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) { Delay_ms(20); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); Delay_ms(20); KeyNum = 1; } if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) { Delay_ms(20); while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); Delay_ms(20); KeyNum = 2; } return KeyNum; } /* 执行以下操作将使能对BKP和RTC的访问: 1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 2: 设置PWR_CR的DBP,使能对BKP和RTC的访问 */ uint16_t ArrayWrite[]={0x1234,0x5678}; uint16_t ArrayRead[2]; uint16_t KeyNum; int main(void) { Key_Init(); OLED_Init(); //1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE); //2: 设置PWR_CR的DBP,使能对BKP和RTC的访问 PWR_BackupAccessCmd(ENABLE); OLED_ShowString(1,1,"W:"); OLED_ShowString(3,1,"R:"); while (1) { KeyNum=Key_GetNum(); if (KeyNum==1) { ArrayWrite[0]++; ArrayWrite[1]++; BKP_WriteBackupRegister(BKP_DR1,ArrayWrite[0]); BKP_WriteBackupRegister(BKP_DR2,ArrayWrite[1]); OLED_ShowHexNum(1,3,ArrayWrite[0],4); OLED_ShowHexNum(1,8,ArrayWrite[1],4); } ArrayRead[0]=BKP_ReadBackupRegister(BKP_DR1); ArrayRead[1]=BKP_ReadBackupRegister(BKP_DR2); OLED_ShowHexNum(3,3,ArrayRead[0],4); OLED_ShowHexNum(3,8,ArrayRead[1],4); } }执行以下操作将使能对BKP和RTC的访问: 1: 设置RCC_APB1ENR的PWREN和BKPEN,使能PWR和BKP时钟 2: 设置PWR_CR的DBP,使能对BKP和RTC的访问 B:实时时钟 1:连接图 2:函数介绍在stm32f10x_rcc.h的文件中-----时钟相关的函数 void RCC_LSEConfig(uint8_t RCC_LSE); void RCC_LSICmd(FunctionalState NewState); void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource); void RCC_RTCCLKCmd(FunctionalState NewState) RCC_LSEConfig : 配置外部低速时钟(LSE) RCC_LSICmd : 配置内部低速时钟(LSI) RCC_RTCCLKConfig: 这个函数用来选择RTCCLK的时钟源 , 实际上就是配置PPT的数据选择器 RCC_RTCCLKCmd : 使能--开启或者关闭RTC时钟 时钟在选择完毕后 , 需要获取标志位,等待标志完成后在操作 RCC_LSEConfig(RCC_LSE_ON);//选择外部低速时钟 while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET);//LSE准备ok了在stm32f10x_rcc.h的文件中-----获取标志位函数 FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG); 在stm32f10x_rtc.h的文件中-----进入RTC配置模式 void RTC_EnterConfigMode(void) 这个函数作用: 置CRL的CNF为1,进入配置模式 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器 在stm32f10x_rtc.h的文件中-----退出RTC配置模式 void RTC_ExitConfigMode(void) 作用 : 就是把CNF位清零 在stm32f10x_rtc.h的文件中-----CNT计数器相关 uint32_t RTC_GetCounter(void) void RTC_SetCounter(uint32_t CounterValue) RTC_GetCounter : 获取RTC计数器值 RTC_SetCounter : 写入CNT的值 在stm32f10x_rtc.h的文件中-----预分频器 void RTC_SetPrescaler(uint32_t PrescalerValue) RTC_SetPrescaler : 写入预分频器的值----这个值会写入到预分频器的PRL重装寄存器中, 用来配置预分频器的分频系数 在stm32f10x_rtc.h的文件中-----写入闹钟的值 void RTC_SetAlarm(uint32_t AlarmValue) 在stm32f10x_rtc.h的文件中-----读取预分频器中的DIV余数寄存器 uint32_t RTC_GetDivider(void); 余数奇存器是一个自减计数器 , 获取余数奇存器的值,一般是为了得到更细致的时间 在stm32f10x_rtc.h的文件中-----等待完成操作 void RTC_WaitForLastTask(void); void RTC_WaitForSynchro(void); RTC_WaitForLastTask : 等待上次操作完成 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器 RTC_WaitForSynchro : 等待同步----清除RSF标志位,然后循环,直到RSF为1 若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1 3:代码 #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "MYRTC.h" uint16_t MyRTC_Time[] = {2023, 1, 1, 23, 59, 55}; void MYRTC_init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE); PWR_BackupAccessCmd(ENABLE); if(BKP_ReadBackupRegister(BKP_DR1)!=0xA5A5) { //LSE的频率是32.768KHz,也就是32768Hz RCC_LSEConfig(RCC_LSE_ON);//选择外部低速时钟 while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET);//LSE准备ok了 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//配置RTC时钟RTCCLK RCC_RTCCLKCmd(ENABLE);//开启RTC时钟 RTC_WaitForLastTask();//等待上次操作完成 RTC_WaitForSynchro();//等待同步时钟 //写预分频器 RTC_SetPrescaler(32768-1); RTC_WaitForLastTask(); RTC_SetCounter(1672588795); //写入CNT RTC_WaitForLastTask(); BKP_WriteBackupRegister(BKP_DR1,0xA5A5); } else { RTC_WaitForLastTask();//等待上次操作完成 RTC_WaitForSynchro();//等待同步时钟 } } void MYRTC_Settime(void) { //内部的定义typedef unsigned int time_t; /* date/time in unix secs past 1-Jan-70 */ 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; //mktimer日期类型的时间数据类型--转化为--秒计数器数据类型 RTC_SetCounter(time_cnt);//写入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; } int main(void) { OLED_Init(); MYRTC_init(); OLED_ShowString(1, 1, "Date:XXXX-XX-XX"); OLED_ShowString(2, 1, "Time:XX:XX:XX"); OLED_ShowString(3, 1, "CNT :"); OLED_ShowString(4, 1, "DIV :"); while (1) { MyRTC_ReadTime(); OLED_ShowNum(1, 6, MyRTC_Time[0], 4); OLED_ShowNum(1, 11, MyRTC_Time[1], 2); OLED_ShowNum(1, 14, MyRTC_Time[2], 2); OLED_ShowNum(2, 6, MyRTC_Time[3], 2); OLED_ShowNum(2, 9, MyRTC_Time[4], 2); OLED_ShowNum(2, 12, MyRTC_Time[5], 2); OLED_ShowNum(3, 6, RTC_GetCounter(), 10); OLED_ShowNum(4, 6, RTC_GetDivider(), 10); } } |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |