闲聊:最近STM32价格真的是越涨越高,感觉STM32很快会被其他产品取代,到时候又要再学。最近大学生电子设计竞赛已经推荐使用TI的芯片了。但是同级别芯片里就32的资源最全,哪位大佬来指点指点这嵌入式的路该咋走,好难啊x_x。
切入正题:
RTC(Real Time Clock)实时时钟,主要作用就是使单片机可以得到准确的日历时间。
先祭出官方时钟图
建议下载个Snipaste截图工具,可以把截图钉在屏幕上,这样就可以边看文章边看框图,不用上下来回翻了^_^
1:得到日历与亚秒时间
第一步是选择时钟,RTC的时钟来源可以为三个来源,LSE,LSI,HSE(不知道这是啥的请移步我的另一篇文章,轻松搞懂STM32F4时钟树)。
日历的时间的实现:时钟发出的脉冲频率进入RTC,先校准一下得到RTCCLK。之后通过7位分频器进行分频。得到ck_apre,ck_apre再经过15位分频器得到ck_spre,经过两个分频器期望得到一个1Hz的ck_spre。也就是ck_spre的周期为1秒,这样就给日历寄存器(日历寄存器有两个,一个提供时间,一个提供日期)提供了时钟。这个日历寄存器是以ck_spre作为计数。当ck_spre每发送一次时钟脉冲,日历寄存器加一秒,并自动进位小时,日期,月份等,并且会自动算上润年润月等。
ck_spre的周期Fck_spre计算公式为:Fck_spre = FRTCCLK / (七位分频器 * 15位分频器);
亚秒的时间的实现:除了日历寄存器外还有一个亚秒寄存器,作用是得到亚秒的时间。亚秒就是次于秒的时间,通过定义亚秒,他和定时器的计数寄存器的向下计数是非常像的。首先从15位分频器的分频数作为一个初值放入亚秒寄存器,随后亚秒根据ck_apre递减。当递减到0时,15位分频器作为自动重载寄存器将其分频数重新放入亚秒寄存器。这样就得到了更精确的时间。亚秒的时钟也就是日历时钟Fck_spre*(1 / (15位分频器数的值+1) ),如Fck_spre = 1Hz,15位自动重装载寄存器的值为255,那么亚秒寄存器的时钟Fck_apre = 1 * (1/255+1) = 256分之1秒。
注:有童鞋可能会问,直接使用LSE的时钟不行么,非要搞这么复杂干啥。但是实际上这样做是有一定的考虑的,当时钟进来后,我们尽可能的在满足需求的同时使用小一些的频率,这样可以大大减少运行的功耗。那么为什么不把日历寄存器和亚秒寄存器做成一个寄存器,只不过是在日历寄存器的最小位后加上代表亚秒的几位就可以了,为什么要这样复杂呢?这一共有两个方面的考虑。一方面原因是如果不需要亚秒这样精确的时间的话,就可以节省一些功耗,一方面是如果我们需要亚秒这样的时间的话,就可以根据需求和功耗的限制,自由定义亚秒的单位时间。在实际工作或者比赛,如电子设计竞赛,在完成需求的情况下,越优的功耗和成本就可以打越高的分!
时间的存储:以BCD码为存储方式的计算日历的时钟。BCD就是把十进制数字用四个bit位来存储,就如存储十进制三二进制为0011,那么存储就是第一位1,二位1,三位0,四位0。也可以使用寄存器的两位存储一位和二位为1。(这里使用的是8421标准,其他标准请自行百度)
影子寄存器:无论是日历寄存器还是亚秒寄存器,都各自有一个影子寄存器,这样可以避免对本体寄存器的误操作。当然也可以直接对本体寄存器进行读写。每过两个RTCCLK周期本体寄存器就会自动更新到影子寄存器中去。每更新一次都会有相应的寄存器位置1,并由硬件自动置为或者软件自动置为。当处于停机和待机状态下不会更新。(停机和待机状态为STM32低功耗部分的内容,在这里不做过多介绍 (其实我TM也不会,慢慢来把,学完一样学一样) )。影子寄存器的复位为系统复位,按下复位按键就属于系统复位,在程序里也有系统复位的库函数。
除了日历外,RTC还具备可编程闹钟功能
两个闹钟,一个A一个B,当闹钟寄存器内的时间与日历寄存器以及亚秒定时器中的值相等,则触发某个寄存器位置为,如果使能闹钟中断的话还会触发闹钟中断,闹钟中断会发送电平信号到对应的引脚上。并可选择中断电平的极性(高低电平选择)。在比较日历寄存器与闹钟寄存器的值的时,使用掩码,这玩意就是那些需要相等,那些不需要,如日期需要小时不需要。那么就掩住小时。在库函数环境开发时有个固定函数设置那些位是有效的,那些位是无效的(即掩住);需要注意的是,当选择闹钟的秒为有效时,同步15位分配器分频系数必须大于3。
除了日历和闹钟外,RTC还具备了自动可唤醒定时器功能。
自动唤醒功能由一个16位计数器组成,由LSE或ck_spre提供时钟,当时钟为LSE(32.768 kHz)且四位预分配器分频系数为不分频时,定时范围为122微妙到32秒之间(正经人谁用LSE做自动唤醒?那不纯粹给自己找麻烦么)。当时钟为ck_spre(1Hz)时,定时范围为1秒到36小时,且定时分为两部分,一部分为1s到18h,另一部分为18h到36h。通过寄存器选择使用那个范围进行定时。其实也就是说2的16次方为65535,而65535 = 18小时。那么一个16位计数器在1Hz频率输出下最多计时18小时。而STM32为了放大这以时间,在硬件优化为了从0s计时到从18h计时。这挺牛逼的,非常方便!而且像手机一样,手机在设置定时开关机时,关机状态下也在计时否则就不知道何时开机。32也一样,自动唤醒定时器在系统复位以及关机,低功耗模式,等情况下,也依旧在计时!!!需要注意的是,唤醒标志位需要软件置位!自动唤醒功能也会产生中断,可以由RTC_ALARM输出。但是当中断被使能时,会使器件推出低功耗模式!!!
除了日历和闹钟以及自动唤醒外,还具备了时间戳功能以及入侵检测
这玩意就是当发生时间戳中断时记录当前的日历信息用的,可以设置入侵的极性(还可以设置采样等方式),当在对应的复用引脚上检测到对应的极性电平,则记录电平发生的时间。入侵检测的方式请自行翻阅手册(太多了,用到的时候翻手册吧,各个版本都不同没必要全记住)。
寄存器保护功能:
让我非常服气的是写入RTC寄存器居然还需要先输入密码,这你要是没注意到这点又没好好看手册,估计死活就卡这了。0XCA写入RTC_WPR,再写入0X53再次写入到RTC_WPR,切记配置寄存器的时候最好不要出错,如果一但出错就立马会被锁上,然后必须再次输入密码,而且这个保护机制免疫系统复位以及断电等操作!!!
RTC部分就到这里,因为一些缘故恕我不能公开代码。这篇文章从中午写到晚上10点左右,查阅了很多资料。如果作为一个初学者单去看野火或者正电的文章很多东西是不全的,比如野火的里面有时间戳但是没有自动唤醒,正电的里面没有时间戳但是有自动唤醒,而作为一个初学者直接去看官方手册又非常头疼。那么我便花出时间来,一方面为了让我的底子更加扎实,一方面为了记录我的学习路程,更重要的一方面是为了结合资料,结合出一份即通俗易懂,又比较全面的文章来。能让大家少走点坑。如有建议欢迎评论。
另外希望各位土豪们能打个赏啥的,让我买瓶护发素什么的。