• 【STM32】RTC(实时时钟)


    1.RTC简介 

    本质:计数器

    RTC中断是外部中断(EXTI)

    当VDD掉电的时候,Vbat可以通过电源--->实时计时

    STM32的RTC外设(Real Time Clock),实质是一个 掉电 后还继续运行的定时器。从定时器的角度来说,相对于通用定时器TIM外设,它十分简单, 只有很纯粹的计时和触发中断的功能;但从 掉电还继续运行 的角度来说,它却是STM32中唯一一个具有如此强大功能的外设。 所以RTC外设的复杂之处并不在于它的定时功能,而在于它掉电还继续运行的特性。

    常用的RTC方案

    1)一般都需要设计RTC外围电路(例如晶振,电源等)

    2)一般都可以给RTC设置独立的电源(当内部Soc的内部电源VDD失效时,可以使用外部电源进行供电,实现掉电还能运行)

    3)多数RTC的寄存器采用BCD码此处时间信息

    RTC使用场景

    2.STM32RTC框图介绍

    一般分频后的时钟频率为1HZ

    浅灰色的部分都是属于备份域的,在VDD掉电时可 在VBAT的驱动下继续运行。这部分仅包括RTC的分频器,计数器, 和闹钟控制器。

    时钟源的选择

    它使用的时钟源有三种,分别为高速外部时钟 的128分频(HSE/128)、低速内部时钟LSI以及低速外部时钟LSE;使HSE分频时钟或LSI的话,在主电源VDD掉电 的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作。因此RTC一般使用低速外部时钟LSE,在设计中, 频率通常为实时时钟模块中常用的32.768KHz,这是因为32768 = 2^15,分频容易实现,所以它被广泛应用到RTC模块。

    HSE(高速外部时钟)/128---》因为是需要使用到Soc内部的电源,所以不能使用

    LSI(低速内部时钟)40KHZ---》因为容易受到温度影响,使用不使用

    LSE32KHZ----》低速外设接口(32.768KHZ)

    1.RTC预分频器

    在配置RTC模块的时钟时,通常把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR_CLK = RTCCLK/32768= 1 Hz,计时周期为1秒,计时器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值加1。

    2.32位可编程计数器

    1)从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数

    2)在备份域中所有寄存器都是16位的, RTC控制相关的寄存器也不例外。它的计数器RTC_CNT的32位由RTC_CNTLRTC_CNTH两个寄存器组成,分别保存定时计数值的低16位和高16位。

    3.待机唤醒

    有2种唤醒方式

    4.RTC控制寄存器与APB1接口

    3.后备寄存器和RTC寄存器特性

    1)当主电源VDD有效时,由VDD给RTC外设供电; 而当VDD掉电后,由VBAT给RTC外设供电。但无论由什么电源供电,RTC中的数据都保存在 属于RTC的备份域,若主电源VDD和VBAT都掉电,那么备份域中保存的所有数据 将丢失。

    2)后备寄存器可以用于保存掉电时的数据

    4.RTC相关寄存器介绍

    1.APB1 外设时钟使能寄存器(RCC_APB1ENR)

    使能PWR&&BKP时钟

    2.电源控制寄存器(PWR_CR)

    使能对后备寄存器和RTC的访问权限

    3.备份域控制寄存器 (RCC_BDCR)

    1.开启RTC时钟

    2.开启LSE时钟

    3.选择RTC计数时钟源

    4.RTC控制寄存器低位(RTC_CRL)

    5.RTC控制寄存器高位(RTC_CRH)

    6.RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)

    7.RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)

    将年月日时分秒---》转换为“秒”

    5.RTC基本配置步骤

    相关HAL库介绍

    6.时间设置和读取

    7.通过串口打印当前时间

    1.rtc初始化

    2.rtc_msp

    3.rtc_set_time

    将输入的时间转换形式写入寄存器

    1. //月份数据表
    2. u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
    3. //平年的月份日期表
    4. const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    5. /*******************************************************************************
    6. * 函 数 名 : RTC_Set
    7. * 函数功能 : RTC设置日期时间函数(以1970年1月1日为基准,把输入的时钟转换为秒钟)
    8. 1970~2099年为合法年份
    9. * 输 入 : syear:年 smon:月 sday:日
    10. hour:时 min:分 sec:秒
    11. * 输 出 : 0,成功
    12. 1,失败
    13. *******************************************************************************/
    14. u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
    15. {
    16. u16 t;
    17. u32 seccount=0;
    18. if(syear<1970||syear>2099)return 1;
    19. for(t=1970;t//把所有年份的秒钟相加
    20. {
    21. if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
    22. else seccount+=31536000; //平年的秒钟数
    23. }
    24. smon-=1;
    25. for(t=0;t//把前面月份的秒钟数相加
    26. {
    27. seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
    28. if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
    29. }
    30. seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
    31. seccount+=(u32)hour*3600;//小时秒钟数
    32. seccount+=(u32)min*60; //分钟秒钟数
    33. seccount+=sec;//最后的秒钟加上去
    34. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
    35. PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
    36. RTC_SetCounter(seccount); //设置RTC计数器的值
    37. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
    38. return 0;
    39. }

    4.rtc_get_time

    将从寄存器获得的值进行展示

    1. //得到当前的时间
    2. //返回值:0,成功;其他:错误代码.
    3. u8 RTC_Get(void)
    4. {
    5. static u16 daycnt=0;
    6. u32 timecount=0;
    7. u32 temp=0;
    8. u16 temp1=0;
    9. timecount=RTC_GetCounter();
    10. temp=timecount/86400; //得到天数(秒钟数对应的)
    11. if(daycnt!=temp)//超过一天了
    12. {
    13. daycnt=temp;
    14. temp1=1970; //从1970年开始
    15. while(temp>=365)
    16. {
    17. if(Is_Leap_Year(temp1))//是闰年
    18. {
    19. if(temp>=366)temp-=366;//闰年的秒钟数
    20. else {temp1++;break;}
    21. }
    22. else temp-=365; //平年
    23. temp1++;
    24. }
    25. calendar.w_year=temp1;//得到年份
    26. temp1=0;
    27. while(temp>=28)//超过了一个月
    28. {
    29. if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
    30. {
    31. if(temp>=29)temp-=29;//闰年的秒钟数
    32. else break;
    33. }
    34. else
    35. {
    36. if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
    37. else break;
    38. }
    39. temp1++;
    40. }
    41. calendar.w_month=temp1+1; //得到月份
    42. calendar.w_date=temp+1; //得到日期
    43. }
    44. temp=timecount%86400; //得到秒钟数
    45. calendar.hour=temp/3600; //小时
    46. calendar.min=(temp%3600)/60; //分钟
    47. calendar.sec=(temp%3600)%60; //秒钟
    48. calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期
    49. return 0;
    50. }
    51. //获得现在是星期几
    52. //功能描述:输入公历日期得到星期(只允许1901-2099年)
    53. //输入参数:公历年月日
    54. //返回值:星期号
    55. u8 RTC_Get_Week(u16 year,u8 month,u8 day)
    56. {
    57. u16 temp2;
    58. u8 yearH,yearL;
    59. yearH=year/100; yearL=year%100;
    60. // 如果为21世纪,年份数加100
    61. if (yearH>19)yearL+=100;
    62. // 所过闰年数只算1900年之后的
    63. temp2=yearL+yearL/4;
    64. temp2=temp2%7;
    65. temp2=temp2+day+table_week[month-1];
    66. if (yearL%4==0&&month<3)temp2--;
    67. return(temp2%7);
    68. }

    8.使用CubeMX生成代码

    【精选】STM32CubeMX学习笔记(14)——RTC实时时钟使用_bcd data format-CSDN博客

    1.开启RTC

    RTC是在时钟部分的

    2.使能时钟RCC

    由于我们使用32.768MHZ(外部低速晶振),所以我们要使能低速时钟(LSE)

    主时钟还是按照原来的时钟频率去设置(主时钟和RTC没有关系)

    3.RTC相关的设置

    勾选 Activate Clock Source 激活时钟源,勾选 Activate Calendar 激活万年历

    1.开启Activate Calendar

    只有开启后才可以进行设置时间

    2.RTC OUT Disable

    3.中断设置

    4.Calendar Time

    5.Calendar Day

  • 相关阅读:
    MySQL性能优化实践:SQL查询优化之使用只读索引、IN方法和临时表分批查询(附加:索引的创建删除命令)
    线程池在业务中的实践
    模式识别与机器学习复习 | 第4讲
    关于GB 2312 编码顺序的研究
    Docker镜像与容器的亲密对话:深度剖析两者内在关联与实战演绎
    32-k8s项目实战-01-集群节点扩缩容
    [极客大挑战 2019]FinalSQL
    [语音识别] 基于Python构建简易的音频录制与语音识别应用
    微信小程序 java健康评估系统springboot
    作为主播如何打造个人IP的7个技巧
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/134447815