一、rtc实时时钟特征与原理
查看stm32中文手册 16 实时时钟(rtc)(308页)
rtc (real time clock):实时时钟
实时时钟是一个独立的定时器。rtc模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
rtc模块和时钟配置系统(rcc_bdcr寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, rtc的设置和时间维持不变。
系统复位后,对后备寄存器和rtc的访问被禁止,这是为了防止对后备区域(bkp)的意外写操作。执行以下操作将使能对后备寄存器和rtc的访问:
● 设置寄存器rcc_apb1enr的pwren和bkpen位,使能电源和后备接口时钟
● 设置寄存器pwr_cr的dbp位,使能对后备寄存器和rtc的访问。
rtc特征
可编程的预分频系数:分频系数最高为220。
● 32位的可编程计数器,可用于较长时间段的测量。
● 2个分离的时钟:用于apb1接口的pclk1和rtc时钟(rtc时钟的频率必须小于pclk1时钟
频率的四分之一以上)。
● 可以选择以下三种rtc的时钟源:
— hse时钟除以128;
— lse振荡器时钟;
— lsi振荡器时钟(详见6.2.8节rtc时钟)。
● 2个独立的复位类型:
— apb1接口由系统复位;
— rtc核心(预分频器、闹钟、计数器和分频器)只能由后备域复位(详见6.1.3节)。
● 3个专门的可屏蔽中断:
— 闹钟中断,用来产生一个软件可编程的闹钟中断。
— 秒中断,用来产生一个可编程的周期性中断信号(最长可达1秒)。
— 溢出中断,指示内部可编程计数器溢出并回转为0的状态。
二、rtc由两部分组成
**apb1接口:**用来和apb1总线相连。通过apb1接口可以访问rtc的相关寄存器(预分频值,计数器值,闹钟值)。
**rtc核心:**由一组可编程计数器组成。分两个主要模块。
第一个是rtc预分频模块,它可以编程产生最长1秒的rtc时间基tr_clk。如果设置了秒中断允许位,可以产生秒中断。
第二个是32位的可编程计数器,可被初始化为当前时间。系统时间按tr_clk周期累加并与存储在rtc_alr寄存器中的可编程时间相比,当匹配时候如果设置了闹钟中断允许位,可以产生闹钟中断。
rtc内核完全独立于apb1接口,软件通过apb1接口对rtc相关寄存器访问。但是相关寄存器只在rtc apb1时钟进行重新同步的rtc时钟的上升沿被更新。所以软件必须先等待寄存器同步标志位(rtc_crl的rsf位)被硬件置1才读。
三、rtc时钟源
首先讲一下时钟源:
三种不同的时钟源可被用来驱动系统时钟(sysclk):
● hsi振荡器时钟
● hse振荡器时钟
● pll时钟
这些设备有以下2种二级时钟源:
● 40khz低速内部rc,可以用于驱动独立看门狗和通过程序选择驱动rtc。rtc用于从停机/待机模式下自动唤醒系统。
● 32.768khz低速外部晶体也可用来通过程序选择驱动rtc(rtcclk)。
参看:stm32时钟系统
当不被使用时,任一个时钟源都可被独立地启动或关闭,由此优化系统功耗。
用户可通过多个预分频器配置ahb、高速apb(apb2)和低速apb(apb1)域的频率。ahb和apb2域的最大频率是72mhz。apb1域的最大允许频率是36mhz。sdio接口的时钟频率固定为hclk/2。
rcc通过ahb时钟(hclk)8分频后作为cortex系统定时器(systick)的外部时钟。通过对systick控制与状态寄存器的设置,可选择上述时钟或cortex(hclk)时钟作为systick时钟。adc时钟由高速apb2时钟经2、 4、 6或8分频后获得。
定时器时钟频率分配由硬件按以下2种情况自动设置:
如果相应的apb预分频系数是1,定时器的时钟频率与所在apb总线频率一致。
否则,定时器的时钟频率被设为与其相连的apb总线频率的2倍。
如上图,有五个时钟源,为hsi、hse、lsi、lse、pll。
接下来我们一一看一下:
hse时钟
**高速外部时钟信号(hse)**由以下两种时钟源产生:
● hse外部晶体/陶瓷谐振器
● hse用户外部时钟
为了减少时钟输出的失真和缩短启动稳定时间,晶体/陶瓷谐振器和负载电容器必须尽可能地靠
近振荡器引脚。负载电容值必须根据所选择的振荡器来调整。
外部时钟源(hse旁路)
在这个模式里,必须提供外部时钟。它的频率最高可达25mhz。用户可通过设置在时钟控制寄存器中的hsebyp和hseon位来选择这一模式。外部时钟信号(50%占空比的方波、正弦波或三角波)必须连到soc_in引脚,同时保证osc_out引脚悬空。见图9。
外部晶体/陶瓷谐振器(hse晶体)
**4~16mz外部振荡器可为系统提供更为精确的主时钟。**相关的硬件配置可参考图9,进一步信息可参考数据手册的电气特性部分。
在时钟控制寄存器rcc_cr中的hserdy位用来指示高速外部振荡器是否稳定。在启动时,直到这一位被硬件置’1’,时钟才被释放出来。如果在时钟中断寄存器rcc_cir中允许产生中断,将会产生相应中断。
hse晶体可以通过设置时钟控制寄存器里rcc_cr中的hseon位被启动和关闭。
hsi时钟
hsi时钟信号由内部8mhz的rc振荡器产生,可直接作为系统时钟或在2分频后作为pll输入。
hsi rc振荡器能够在不需要任何外部器件的条件下提供系统时钟。它的启动时间比hse晶体振荡器短。然而,即使在校准之后它的时钟频率精度仍较差。
校准
制造工艺决定了不同芯片的rc振荡器频率会不同,这就是为什么每个芯片的hsi时钟频率在出厂前已经被st校准到1%(25°c)的原因。系统复位时,工厂校准值被装载到时钟控制寄存器的hsical[7:0]位。
如果用户的应用基于不同的电压或环境温度,这将会影响rc振荡器的精度。可以通过时钟控制寄存器里的hsitrim[4:0]位来调整hsi频率。
时钟控制寄存器中的hsirdy位用来指示hsi rc振荡器是否稳定。在时钟启动过程中,直到这一位被硬件置’1’, hsi rc输出时钟才被释放。hsi rc可由时钟控制寄存器中的hsion位来启动和关闭。
如果hse晶体振荡器失效, hsi时钟会被作为备用时钟源。
pll
内部pll可以用来倍频hsi rc的输出时钟或hse晶体输出时钟。
pll的设置(选择his振荡器除2或hse振荡器为pll的输入时钟,和选择倍频因子)必须在其被激活前完成。一旦pll被激活,这些参数就不能被改动。
如果pll中断在时钟中断寄存器里被允许,当pll准备就绪时,可产生中断申请。
如果需要在应用中使用usb接口, pll必须被设置为输出48或72mhz时钟,用于提供48mhz的usbclk时钟。
lse时钟
**lse晶体是一个32.768khz的低速外部晶体或陶瓷谐振器。**它为实时时钟或者其他定时功能提供一个低功耗且精确的时钟源。
lse晶体通过在备份域控制寄存器(rcc_bdcr)里的lseon位启动和关闭。
在备份域控制寄存器(rcc_bdcr)里的lserdy指示lse晶体振荡是否稳定。在启动阶段,直到这个位被硬件置’1’后, lse时钟信号才被释放出来。如果在时钟中断寄存器里被允许,可产生中断申请。
外部时钟源(lse旁路)
在这个模式里必须提供一个32.768khz频率的外部时钟源。你可以通过设置在备份域控制寄存器(rcc_bdcr)里的lsebyp和lseon位来选择这个模式。具有50%占空比的外部时钟信号(方波、正弦波或三角波)必须连到osc32_in引脚,同时保证osc32_out引脚悬空,见图9。
lsi时钟
lsi rc担当一个低功耗时钟源的角色,它可以在停机和待机模式下保持运行,为独立看门狗和自动唤醒单元提供时钟。**lsi时钟频率大约40khz(在30khz和60khz之间)。**进一步信息请参考数据手册中有关电气特性部分。
lsi rc可以通过控制/状态寄存器(rcc_csr)里的lsion位来启动或关闭。
在控制/状态寄存器(rcc_csr)里的lsirdy位指示低速内部振荡器是否稳定。在启动阶段,直到这个位被硬件设置为’1’后,此时钟才被释放。如果在时钟中断寄存器(rcc_cir)里被允许,将产生lsi中断申请。
注意:只有大容量和互联型产品可以进行lsi校准
lsi校准
可以通过校准内部低速振荡器lsi来补偿其频率偏移,从而获得精度可接受的rtc时间基数,以及独立看门狗(iwdg)的超时时间(当这些外设以lsi为时钟源)。
**校准可以通过使用tim5的输入时钟(tim5_clk)测量lsi时钟频率实现。**测量以hse的精度为保证,软件可以通过调整rtc的20位预分频器来获得精确的rtc时钟基数,以及通过计算得到精确的独立看门狗(iwdg)的超时时间。
lsi校准步骤如下:
打开tim5,设置通道4为输入捕获模式;
设置afio_mapr的tim5_ch4_iremap位为’1’,在内部把lsi连接到tim5的通道4;
通过tim5的捕获/比较4事件或者中断来测量lsi时钟频率;
根据测量结果和期望的rtc时间基数和独立看门狗的超时时间,设置20位预分频器。
四、rtc时钟
**通 过 设 置 备 份 域 控 制 寄 存 器 (rcc_bdcr) 里 的 rtcsel[1:0] 位 , rtcclk 时钟源可以由hse/128、lse或lsi时钟提供。**除非备份域复位,此选择不能被改变。
lse时钟在备份域里,但hse和lsi时钟不是。因此:
● 如果lse被选为rtc时钟:
— 只要vbat维持供电,尽管vdd供电被切断, rtc仍继续工作。
● 如果lsi被选为自动唤醒单元(awu)时钟:
— 如果vdd供电被切断, awu状态不能被保证。有关lsi校准,详见6.2.5节lsi时钟。
● 如果hse时钟128分频后作为rtc时钟:
— 如果vdd供电被切断或内部电压调压器被关闭(1.8v域的供电被切断),则rtc状态不确定。
— 必须设置电源控制寄存器(见4.4.1节)的dpb位(取消后备区域的写保护)为’1’。
五、rtc寄存器
上面都是从stm32中文手册里摘取的。大概了解一下rtc和时钟。
不过讲的有点扯,里面有多好寄存器,不知道是干啥的。接下来重点看一下这些寄存器。
rtc控制寄存器高位(rtc_crh)
rtc控制寄存器低位(rtc_crl)
①修改crh/crl寄存器,必须先判断rsf位,确定已经同步。
②修改cnt,alr,prl的时候,必须先配置cnf位进入配置模式,修改完之后,设置cnf位为0退出配置模式
③同时在对rtc相关寄存器写操作之前,必须判断上一次写操作已经结束,也就是判断rtoff位是否置位。
rtc预分频装载寄存器(rtc_prlh/rtc_prll)
预分频装载寄存器用来保存rtc预分频器的周期计数值。它们受rtc_cr寄存器的rtoff位保护,仅当rtoff值为’1’时允许进行写操作。
配置rtc寄存器
必须设置rtc_crl 寄 存 器 中 的cnf位 , 使rtc进入配置模式后 , 才能写 入rtc_prl、rtc_cnt、 rtc_alr寄存器。
另外,对rtc任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询rtc_cr寄存器中的rtoff状态位,判断rtc寄存器是否处于更新中。仅当rtoff状态位是’1’时,才可以写入rtc寄存器。
配置过程:
查询rtoff位,直到rtoff的值变为’1’
置cnf值为1,进入配置模式
对一个或多个rtc寄存器进行写操作
清除cnf标志位,退出配置模式
查询rtoff,直至rtoff位变为’1’以确认写操作已经完成。
仅当cnf标志位被清除时,写操作才能进行,这个过程至少需要3个rtcclk周期。
读rtc寄存器
rtc核完全独立于rtc apb1接口。
软件通过apb1接口访问rtc的预分频值、 计数器值和闹钟值。但是,相关的可读寄存器只在与rtc apb1时钟进行重新同步的rtc时钟的上升沿被更新。rtc标志也是如此的。
这意味着,如果apb1接口曾经被关闭,而读操作又是在刚刚重新开启apb1之后,则在第一次的内部寄存器更新之前,从apb1上读出的rtc寄存器数值可能被破坏了(通常读到0)。下述几种
情况下能够发生这种情形:
● 发生系统复位或电源复位
● 系统刚从待机模式唤醒(参见第4.3节:低功耗模式)。
● 系统刚从停机模式唤醒(参见第4.3节:低功耗模式)。
所有以上情况中, apb1接口被禁止时(复位、无时钟或断电)rtc核仍保持运行状态。
因此,若在读取rtc寄存器时, rtc的apb1接口曾经处于禁止状态,则软件首先必须等待rtc_crl寄存器中的rsf位(寄存器同步标志)被硬件置’1’。
注:rtc的 apb1接口不受wfi和wfe等低功耗模式的影响
六、rtc相关库函数讲解
库函数所在文件:stm32f10x_rtc.c / stm32f10x_rtc.h
rtc时钟源和时钟操作函数:
void rcc_rtcclkconfig(uint32_t clksource);//时钟源选择 void rcc_rtcclkcmd(functionalstate newstate)//时钟使能rtc配置函数(预分频,计数值:
void rtc_setprescaler(uint32_t prescalervalue);//预分频配置:prlh/prllvoid rtc_setcounter(uint32_t countervalue);//设置计数器值:cnth/cntlvoid rtc_setalarm(uint32_t alarmvalue);//闹钟设置:alrh/alrlrtc中断设置函数:
void rtc_itconfig(uint16_t rtc_it, functionalstate newstate);//crh
rtc允许配置和退出配置函数:
void rtc_itconfig(uint16_t rtc_it, functionalstate newstate);//crh
同步函数:
void rtc_waitforlasttask(void);//等待上次操作完成:crl位rtoffvoid rtc_waitforsynchro(void);//等待时钟同步:crl位rsf相关状态位获取清除函数:
flagstatus rtc_getflagstatus(uint16_t rtc_flag);void rtc_clearflag(uint16_t rtc_flag);itstatus rtc_getitstatus(uint16_t rtc_it);void rtc_clearitpendingbit(uint16_t rtc_it);其他相关函数(bkp等)
pwr_backupaccesscmd();//bkp后备区域访问使能rcc_apb1periphclockcmd();//使能pwr和bkp时钟rcc_lseconfig();//开启lse,rtc选择lse作为时钟源pwr_backupaccesscmd();//bkp后备区域访问使能uint16_t bkp_readbackupregister(uint16_t bkp_dr);//读bkp寄存器void bkp_writebackupregister(uint16_t bkp_dr, uint16_t data);//写bkp 七、rtc配置一般步骤 1) 使能电源时钟和备份区域时钟。 前面已经介绍了,我们要访问 rtc 和备份区域就必须先使能电源时钟和备份区域时钟。
rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_bkp, enable);
2) 取消备份区写保护。
要向备份区域写入数据,就要先取消备份区域写保护(写保护在每次硬复位之后被使能),否则是无法向备份区域写入数据的。我们需要用到向备份区域写入一个字节,来标记时钟已经配置过了,这样避免每次复位之后重新配置时钟。取消备份区域写保护的库函数实现方法是:
pwr_backupaccesscmd(enable); //使能 rtc 和后备寄存器访问
3) 复位备份区域,开启外部低速振荡器。
在取消备份区域写保护之后,我们可以先对这个区域复位,以清除前面的设置,当然这个操作不要每次都执行,因为备份区域的复位将导致之前存在的数据丢失,所以要不要复位,要看情况而定。然后我们使能外部低速振荡器,注意这里一般要先判断 rcc_bdcr 的 lserdy位来确定低速振荡器已经就绪了才开始下面的操作。
备份区域复位的函数是:
bkp_deinit();//复位备份区域
开启外部低速振荡器的函数是:
rcc_lseconfig(rcc_lse_on);// 开启外部低速振荡器
4) 选择 rtc 时钟,并使能。
这里我们将通过 rcc_bdcr 的 rtcsel 来选择选择外部 lsi 作为 rtc 的时钟。然后通过rtcen 位使能 rtc 时钟。库函数中,选择 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_init
//实时时钟配置//初始化 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); //向指定的后备寄存器中//写入用户程序数据 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_set
//设置时钟//把输入的时钟转换为秒钟//以 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;}rtc_get
//得到当前的时间,结果保存在 calendar 结构体里面//返回值:0,成功;其他:错误代码.u8 rtc_get(void){ static u16 daycnt=0;u32 timecount=0;u32 temp=0;u16 temp1=0;timecount=rtc-》cnth; //得到计数器中的值(秒钟数)timecount《《=16;timecount+=rtc-》cntl;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 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;}rtc_nvic_config
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_irqhandler
//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_get(); //更新时间printf(“alarm time:%d-%d-%d %d:%d:%d ”,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();}九、项目代码
void bsp_rtc_init(void){u32 i = 0;#if(info_out_rtc_init_en 》 0)u8tmpbuf[60]=“”;#endif
/* clear reset flags */rcc_clearflag();
// 这里标志必须跟测试程序一致否则时间被复位成默认if (bkp_readbackupregister(bkp_dr1) != rtc_save_flag){rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_bkp, enable);/* allow access to bkp domain */pwr_backupaccesscmd(enable);
/* backup data register value is not correct or not yet programmed (whenthe first time the program is executed) */
/* rtc configuration */bsp_rtc_config();
#if(def_rtcinfo_outputen 》 0)if(dbginfoswt & dbg_info_rtc)myprintf(“[rtc]: rtc finish configured.。。。 ”);#endif
/* set default time */sys_rtc.year=default_year;sys_rtc.month=default_month;sys_rtc.day=default_day;sys_rtc.hour=default_hour;sys_rtc.minute =default_minute;sys_rtc.second =default_second;
/* adjust time by values entred by the user on the hyperterminal */bsp_rtc_set_current(&sys_rtc);
bkp_writebackupregister(bkp_dr1, rtc_save_flag);}else{/* enable pwr and bkp clocks */rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_bkp, enable);
/* allow access to bkp domain */pwr_backupaccesscmd(enable);
/* wait for rtc registers synchronization */rtc_waitforsynchro();
/* wait until last write operation on rtc registers has finished */rtc_waitforlasttask();
/* enable the rtc second *///rtc_itconfig(rtc_it_sec, enable);// 不能在系统运行前使能中断//rtc_itconfig(rtc_it_alr, enable);// 系统闹钟中断/* wait until last write operation on rtc registers has finished *///rtc_waitforlasttask();// 不能在系统运行前使能中断
/* initialize date structure */sys_rtc.year = bkp_readbackupregister(bkp_dr4);sys_rtc.month= bkp_readbackupregister(bkp_dr3);sys_rtc.day = bkp_readbackupregister(bkp_dr2);
if(rtc_getcounter() / 86399 != 0){for(i = 0; i 《 (rtc_getcounter() / 86399); i++){bsp_date_update(&sys_rtc);}
/* wait until last write operation on rtc registers has finished */rtc_waitforlasttask();rtc_setcounter(rtc_getcounter() % 86399);/* wait until last write operation on rtc registers has finished */rtc_waitforlasttask();
bkp_writebackupregister(bkp_dr4, sys_rtc.year);bkp_writebackupregister(bkp_dr3, sys_rtc.month);bkp_writebackupregister(bkp_dr2, sys_rtc.day);}}/* clear the rtc second interrupt pending bit */rtc_clearitpendingbit(rtc_it_sec);// 防止系统初始化未完成前进入中断程序rtc_clearflag(rtc_it_sec);
/* enable one second interrupe *///rtc_itconfig(rtc_it_sec,enable);// 不能在系统运行前使能中断rtcinitfinish=1;// 设置初始化完成标志}
void bsp_rtc_config(void){//u32 counter = 0;uint32_t tmp = 0;rcc_clockstypedef rcc_clocks;tim_timebaseinittypedef tim_timebasestructure;tim_icinittypedef tim_icinitstructure;
/* enable pwr and bkp clocks */rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_bkp, enable);rcc_apb2periphclockcmd(rcc_apb2periph_afio, enable);/* allow access to bkp domain */pwr_backupaccesscmd(enable);/* reset backup domain */bkp_deinit();rcc_lsicmd(enable); //启用lsiwhile (rcc_getflagstatus(rcc_flag_lsirdy) == reset){}rcc_rtcclkconfig(rcc_rtcclksource_lsi);rcc_rtcclkcmd(enable); // enable rtc clockrtc_waitforsynchro();rtc_waitforlasttask();rtc_setprescaler(40000); // rtc period = rtcclk/rtc_pr = (4 khz)/(4000+1) lsirtc_waitforlasttask();bkp_tamperpincmd(disable);bkp_rtcoutputconfig(bkp_rtcoutputsource_second);rcc_getclocksfreq(&rcc_clocks);rcc_apb1periphclockcmd(rcc_apb1periph_tim5, enable);gpio_pinremapconfig(gpio_remap_tim5ch4_lsi, enable);tim_timebasestructure.tim_prescaler = 0;tim_timebasestructure.tim_countermode = tim_countermode_up;tim_timebasestructure.tim_period = 0xffff;tim_timebasestructure.tim_clockdivision = tim_ckd_div1;tim_timebaseinit(tim5, &tim_timebasestructure);tim_icinitstructure.tim_channel = tim_channel_4;tim_icinitstructure.tim_icpolarity = tim_icpolarity_rising;tim_icinitstructure.tim_icselection = tim_icselection_directti;tim_icinitstructure.tim_icprescaler = tim_icpsc_div1;tim_icinitstructure.tim_icfilter = 0;tim_icinit(tim5, &tim_icinitstructure);operationcomplete = 0;tim_cmd(tim5, enable);tim5-》sr = 0;//tim_itconfig(tim5, tim_it_cc4, enable);
while (operationcomplete != 2){if (tim_getflagstatus(tim5, tim_flag_cc4) == set){tmpcc4[incrementvar_operationcomplete()] = (uint16_t)(tim5-》ccr4);tim_clearflag(tim5, tim_flag_cc4);if (getvar_operationcomplete() 》= 2){tmp = (uint16_t)(tmpcc4[1] - tmpcc4[0] + 1);setvar_periodvalue(tmp);}}}if (periodvalue != 0){#if defined (stm32f10x_ld_vl) || defined (stm32f10x_md_vl) || defined (stm32f10x_hd_vl)lsifreq = (uint32_t)((uint32_t)(rcc_clocks.pclk1_frequency) / (uint32_t)periodvalue);#elselsifreq = (uint32_t)((uint32_t)(rcc_clocks.pclk1_frequency * 2) / (uint32_t)periodvalue);#endif}rtc_setprescaler(lsifreq - 1);rtc_waitforlasttask();
tim_deinit( tim5 );}十、hse作为rtc时钟源
void rtc_configuration(void){rcc_apb1periphclockcmd(rcc_apb1periph_pwr | rcc_apb1periph_bkp, enable);pwr_backupaccesscmd(enable);/* reset backup domain */bkp_deinit();//使用外部高速晶振8m/128 = 62.5krcc_rtcclkconfig(rcc_rtcclksource_hse_div128);//允许rtcrcc_rtcclkcmd(enable);//等待rtc寄存器同步rtc_waitforsynchro();
rtc_waitforlasttask();//允许rtc的秒中断(还有闹钟中断和溢出中断可设置)rtc_itconfig(rtc_it_sec, enable);
黑莓完成转型,股价飙升66%
说到颜值机,能想到了只有华为荣耀8这款手机,颜值绝对逆天!
惊!一北京小伙用人工智能,让老照片里的李焕英“笑了”
AirPods Pro与iPhone 3GS完美配对 降噪功能运行正常
基于Labview的光伏发电数据监测系统的设计[图]
浅谈RTC实时时钟特征与原理
华为P10Plus强大的通信实力将引领4G到4.5G的转变,完成实质性飞跃
电力载波智能家居系统的工作原理和注意事项
PS-1305S小型荷重试验机的优势所在
电机多大才需要降压启动?
小米汽车对标车型是什么?保时捷和特斯拉?
5G技术助力医疗发展将步入高速发展期
高通加入Thread Group 助力物联网标准统一
什么是投入式液位传感器,它的作用是什么
51单片机最小系统的构成与绘制
CAN总线的隔离地与屏蔽双绞线的屏蔽层怎么接
小米MAX2怎么样?小米MAX2评测:小米MX2外观、配置、续航、相机、价格较小米MAX升级了吗?
宝马:不可避免地会推出纯电动M系性能车
一种简易智能家居监控系统的设计方案
2021入耳式蓝牙耳机口碑品牌,降噪蓝牙耳机销量榜推荐