返回列表 发新帖我要提问本帖赏金: 40.00元(功能说明)

[MM32生态] 看过来,芯片也能当时钟?基于MM32L0130实现RTC实时时钟工程

[复制链接]
1944|8
 楼主| 春娇霹雳娃 发表于 2023-7-28 16:48 | 显示全部楼层 |阅读模式
本帖最后由 春娇霹雳娃 于 2023-7-31 10:52 编辑

#申请原创# @21小跑堂

1.简介
RTC 模块是用于提供时间(时、分、秒、亚秒)和日期(年、月、日)功能的定时计数器,日历以 BCD 码的格式显示。RTC内部存在实时日历计数器,默认情况下,每隔2个RTC时钟周期,会将实时的日历计数复制到影子寄存器。其设计框图如下图所示:

3185764c36f68691eb.png

MM32L0130使用高性能的 Arm® Cortex-M0+ 的 32 位微控制器,最高工作频率可达 48MHz,内置高速存储器,丰富的增强型 I/O 端口和多种外设。其开发板硬件外观如下图所示:
8843064c36fb528c75.png 9464564c37a723ca7d.png

本实验实现以下功能:
  • 可编程的日历功能,包括小时(12/24 小时制) 、分钟、秒钟、星期、日期、月份、年份
  • 可编程闹钟,任意日历字段的组合(日/星期、时、分、秒、亚秒)触发闹钟,支持闹钟中断,闹钟响应时间可配置
  • 支持周期性循环唤醒中断,周期性唤醒时间可配置
  • 支持入侵检测,当产生入侵事件,可通过时间戳获取入侵时间

1142164c370c8d0813.png

2.实验环境
硬件环境:
  • EV Board (MM32L0136C7P)
  • Jlink v11.0

软件环境:
  • KEIL V5.37
  • 终端软件Tera Term

9799964c3793956df9.png

3.软件配置
时钟配置
RTC可选择时钟源LSE、LSI、HSE128分频。RTC内部包含2个预分频器:7 位的异步预分频器与15位的同步预分频器,用于提供日历或其它功能的时钟。
  1. void CLOCK_RTCToLSE(void){
  2.     RCC->BDCR |= RCC_BDCR_DBP_MASK;
  3.     RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_RTC, true);
  4.     /* Reset RTC BKP. */
  5.     RCC_ResetRTCClock();
  6.     RCC->BDCR &= ~RCC_BDCR_RTCRST_MASK;
  7.     RCC_EnableBKPWriteProtect(false);
  8.     /* Enable LSE clock source. */
  9.     RCC->BDCR |= (RCC_BDCR_LSEON_MASK | RCC_BDCR_RTCSEL(1u));

  10.     while (0u == (RCC->BDCR & RCC_BDCR_LSERDY_MASK) ){}
  11.     RCC_EnableRTCClock(true);}
配置影子寄存器
  1. /* Get current time. */
  2. void RTC_GetTime(RTC_Type * RTCx, RTC_Datetime_Type * time){
  3.     uint32_t ss = RTCx->SSR;
  4.     uint32_t temp1 = RTCx->TR;
  5.     uint32_t temp2 = RTCx->DR;

  6.     time->SubSecs = ( ss & RTC_SSR_SS_MASK);
  7.     time->Secs   = ( (temp1 & ( RTC_TR_ST_MASK  |  RTC_TR_SU_MASK) ) >> RTC_TR_SU_SHIFT );
  8.     time->Mins   = ( (temp1 & ( RTC_TR_MNU_MASK |  RTC_TR_MNT_MASK) ) >> RTC_TR_MNU_SHIFT );
  9.     time->Hours  = ( (temp1 & ( RTC_TR_HU_MASK  |  RTC_TR_HT_MASK) ) >> RTC_TR_HU_SHIFT );
  10.     time->Week   = ( (temp2 & RTC_DR_WDU_MASK ) >> RTC_DR_WDU_SHIFT );
  11.     time->Days   = ( (temp2 & ( RTC_DR_DU_MASK  |  RTC_DR_DT_MASK) ) >> RTC_DR_DU_SHIFT );
  12.     time->Months = ( (temp2 & ( RTC_DR_MU_MASK  |  RTC_DR_MT_MASK) ) >> RTC_DR_MU_SHIFT );
  13.     time->Years  = ( (temp2 & ( RTC_DR_YU_MASK  |  RTC_DR_YT_MASK) ) >> RTC_DR_YU_SHIFT );}
1712964c37c7a8f1b2.png
RTC初始化
  • 将RTC的同步预分频、异步预分频及时间为12/24小时制的配置放入RTC_Init()中。
  • 初始化前需要配置RTC_ISR寄存器的INIT位为1,等待INITF位硬件置1后才能进行数据写入。
  • 在进行完初始化及初始时间配置后,清除INIT位,退出初始化模式。

  1. /* Start initialization. */
  2.     RTCx->ISR |= RTC_ISR_INIT_MASK;
  3.     while ( ( 0u == (RTCx->ISR & RTC_ISR_INITF_MASK) ) && (init->TimeOut > 0u) )  /* Prevent the infinite loop of driver. */{
  4.         init->TimeOut--;}
时间配置
  • RTC_TR时间寄存器配置RTC的时分秒,按个位与十位分别配置
  • RTC_DR日期寄存器,年月日以个位与十位分开的形式配置
  • 数据以BCD码格式保存,即用4位二进制数来表示1位十进制数中的0~9这10个数
  • 时间计数分12小时制与24小时制,由RTC_CR寄存器的FMT位控制
  • RTC存在内部计数器,每2个RTC周期,将数值存放入时间寄存器与日期寄存器

  1. /* Get current time. */
  2. void RTC_GetTime(RTC_Type * RTCx, RTC_Datetime_Type * time){
  3.     uint32_t ss = RTCx->SSR;
  4.     uint32_t temp1 = RTCx->TR;
  5.     uint32_t temp2 = RTCx->DR;

  6.     time->SubSecs = ( ss & RTC_SSR_SS_MASK);
  7.     time->Secs   = ( (temp1 & ( RTC_TR_ST_MASK  |  RTC_TR_SU_MASK) ) >> RTC_TR_SU_SHIFT );
  8.     time->Mins   = ( (temp1 & ( RTC_TR_MNU_MASK |  RTC_TR_MNT_MASK) ) >> RTC_TR_MNU_SHIFT );
  9.     time->Hours  = ( (temp1 & ( RTC_TR_HU_MASK  |  RTC_TR_HT_MASK) ) >> RTC_TR_HU_SHIFT );
  10.     time->Week   = ( (temp2 & RTC_DR_WDU_MASK ) >> RTC_DR_WDU_SHIFT );
  11.     time->Days   = ( (temp2 & ( RTC_DR_DU_MASK  |  RTC_DR_DT_MASK) ) >> RTC_DR_DU_SHIFT );
  12.     time->Months = ( (temp2 & ( RTC_DR_MU_MASK  |  RTC_DR_MT_MASK) ) >> RTC_DR_MU_SHIFT );
  13.     time->Years  = ( (temp2 & ( RTC_DR_YU_MASK  |  RTC_DR_YT_MASK) ) >> RTC_DR_YU_SHIFT );}
读取时间
若要读取RTC_TR、RTC_DR、RTC_SSR寄存器,需等待RTC_ISR寄存器的RSF位置1后,再读取寄存器。(等待寄存器同步)
  1. /* Get current time. */
  2. void RTC_GetTime(RTC_Type * RTCx, RTC_Datetime_Type * time){
  3.     uint32_t ss = RTCx->SSR;
  4.     uint32_t temp1 = RTCx->TR;
  5.     uint32_t temp2 = RTCx->DR;

  6.     time->SubSecs = ( ss & RTC_SSR_SS_MASK);
  7.     time->Secs   = ( (temp1 & ( RTC_TR_ST_MASK  |  RTC_TR_SU_MASK) ) >> RTC_TR_SU_SHIFT );
  8.     time->Mins   = ( (temp1 & ( RTC_TR_MNU_MASK |  RTC_TR_MNT_MASK) ) >> RTC_TR_MNU_SHIFT );
  9.     time->Hours  = ( (temp1 & ( RTC_TR_HU_MASK  |  RTC_TR_HT_MASK) ) >> RTC_TR_HU_SHIFT );
  10.     time->Week   = ( (temp2 & RTC_DR_WDU_MASK ) >> RTC_DR_WDU_SHIFT );
  11.     time->Days   = ( (temp2 & ( RTC_DR_DU_MASK  |  RTC_DR_DT_MASK) ) >> RTC_DR_DU_SHIFT );
  12.     time->Months = ( (temp2 & ( RTC_DR_MU_MASK  |  RTC_DR_MT_MASK) ) >> RTC_DR_MU_SHIFT );
  13.     time->Years  = ( (temp2 & ( RTC_DR_YU_MASK  |  RTC_DR_YT_MASK) ) >> RTC_DR_YU_SHIFT );}
闹钟功能:
  • RTC支持闹钟响应功能,当闹钟寄存器中的值与计数器的值相同时,闹钟标志会置位,闹钟可匹配:日期/星期、小时、分钟、秒钟、亚秒
  • 闹钟在配置时需选择是匹配日期还是匹配星期
  • 闹钟可屏蔽不需要匹配的部分时间,例如:若屏蔽日期/星期、小时、分钟,则每分钟的当前闹钟秒数都会产生闹钟响应标志
  • 支持闹钟中断响应。

  1. /* Set alarm time. */
  2.     RTCx->ALARMAR = 0u;
  3.     RTCx->ALARMASSR = 0u;
  4.     uint32_t Alarm = ( RTC_ALARMAR_SU(time->Secs % 10u) | RTC_ALARMAR_ST(time->Secs / 10u) | RTC_ALARMAR_MNU(time->Mins % 10u)
  5.                   | RTC_ALARMAR_MNT(time->Mins / 10u) | RTC_ALARMAR_HU(time->Hours % 10u) | RTC_ALARMAR_HT(time->Hours / 10u)
  6.                   | RTC_ALARMAR_DU(time->Days % 10u) | RTC_ALARMAR_DT(time->Days / 10u) | RTC_ALARMAR_WDSEL(alarm->WeekDaysel) );
闹钟中断
闹钟中断使能&闹钟标志产生 ->闹钟中断
  1. /* RTC enable interrupt. */
  2. void RTC_EnableInterrupt(RTC_Type * RTCx, uint32_t interrupts, bool enable)
周期性唤醒
对RTC_WUTR(唤醒定时器寄存器)中的数据递减,直到递减为0,唤醒标志位置位,寄存器重装载数据,再次递减到0,不断循环。
1157564c37eb92a1c7.png
入侵检测
RTC支持入侵检测,通过给入侵引脚一个触发信号,使入侵标志置起,备份域寄存器会在发生入侵事件时复位。

3257064c37ed12fd24.png
  1. /* Configurate tamp and enable tamp. */
  2. void RTC_EnableTampConfig(RTC_Type * RTCx, RTC_Tamp_Type * tamp){
  3.     RTCx->TAMPCR &= ~(RTC_TAMP_TAMP1 | RTC_TAMP_TAMP2);
  4.     RTCx->BKPXR[tamp->BKP] = tamp->BKPData;
  5.     RTCx->TAMPCR = RTC_TAMPCR_TAMPTS(tamp->SaveTime);
  6.     if (tamp->Tamp == RTC_TAMP_TAMP1){
  7.         RTCx->TAMPCR |= RTC_TAMPCR_TAMP1TRG(tamp->Trigger);
  8.         RTC_EnableTamp(RTCx, RTC_TAMPCR_TAMP1E_MASK, true);
  9.     }if (tamp->Tamp == RTC_TAMP_TAMP2){
  10.         RTCx->TAMPCR |= RTC_TAMPCR_TAMP2TRG(tamp->Trigger);
  11.         RTC_EnableTamp(RTCx, RTC_TAMPCR_TAMP2E_MASK, true);}}

4.实验样例
rtc_basic
按下”a”初始化RTC并配置初始时间,按下”b”获取当前时间,串口输出年、月、日、时、分、秒、亚秒,按下”c”配置闹钟响应时间,并在闹钟响应后输出当前时间。
  1. int main(void){
  2.     uint8_t ch;

  3.     BOARD_Init();
  4.     printf("rtc_basic example.\r\n");
  5.     printf("Press key 'a' to initialize rtc, 'b' to get current time, 'c' to alarm time.\r\n");

  6.     while (1){
  7.         ch = getchar();
  8.         switch(ch){
  9.             case 'a':
  10.                 app_rtc_init();
  11.                 printf("a: app_rtc_init().\r\n");
  12.             break;
  13.             case 'b':
  14.                 app_rtc_get_current_time();
  15.                 printf("b: app_rtc_get_current_time().\r\n");
  16.             break;
  17.             case 'c':
  18.                 app_rtc_setalalrm();
  19.                 printf("c: app_rtc_setalalrm().\r\n");
  20.             break;
  21.             default:
  22.                 printf("a: app_rtc_init().\r\n");
  23.                 printf("b: app_rtc_get_current_time().\r\n");
  24.                 printf("c: app_rtc_setalalrm().\r\n");
  25.             break;}
  26.         printf("%02x-", (unsigned int)(long)rtc_time.Years);
  27.         printf("%02x-", (unsigned int)(long)rtc_time.Months);
  28.         printf("%02x ", (unsigned int)(long)rtc_time.Days);
  29.         printf("%02x ", (unsigned int)(long)rtc_time.Week);
  30.         printf("%02x-", (unsigned int)(long)rtc_time.Hours);
  31.         printf("%02x",  (unsigned int)(long)rtc_time.Mins);
  32.         printf("-%02x", (unsigned int)(long)rtc_time.Secs);
  33.         printf(" %02x", (unsigned int)(long)rtc_time.SubSecs);
  34.         printf("\r\n");}}
rtc_interrupt
按下”a”初始化RTC并配置初始时间,按下”b”获取当前时间,串口输出年、月、日、时、分、秒、亚秒,按下”c”配置闹钟响应时间及闹钟中断,并在闹钟中断响应后输出当前时间。
  1. int main(void){
  2.     uint8_t ch;

  3.     BOARD_Init();
  4.     printf("rtc_interrupt example.\r\n");
  5.     printf("Press key 'a' to initialize rtc, 'b' to get current time, 'c' to alarm interrupt time.\r\n");

  6.     while (1){
  7.         ch = getchar();
  8.         switch(ch){
  9.             case 'a':
  10.                 app_rtc_init();
  11.                 printf("a: app_rtc_init().\r\n");
  12.             break;
  13.             case 'b':
  14.                 app_rtc_get_current_time();
  15.                 printf("b: app_rtc_get_current_time().\r\n");
  16.             break;
  17.             case 'c':
  18.                 app_rtc_setalalrm();
  19.                 printf("c: app_rtc_setalalrm().\r\n");
  20.             break;
  21.             default:
  22.                 printf("a: app_rtc_init().\r\n");
  23.                 printf("b: app_rtc_get_current_time().\r\n");
  24.                 printf("c: app_rtc_setalalrm().\r\n");
  25.             break;}
  26.         printf("%02x-", (unsigned int)(long)rtc_time.Years);
  27.         printf("%02x-", (unsigned int)(long)rtc_time.Months);
  28.         printf("%02x ", (unsigned int)(long)rtc_time.Days);
  29.         printf("%02x ", (unsigned int)(long)rtc_time.Week);
  30.         printf("%02x-", (unsigned int)(long)rtc_time.Hours);
  31.         printf("%02x",  (unsigned int)(long)rtc_time.Mins);
  32.         printf("-%02x", (unsigned int)(long)rtc_time.Secs);
  33.         printf(" %02x", (unsigned int)(long)rtc_time.SubSecs);
  34.         printf("\r\n");}}
rtc_wakeup
设置RTC周期性唤醒的时间为10s,当检测到唤醒标志,串口打印”wakeup”。
  1. int main(void){
  2.     BOARD_Init();

  3.     app_rtc_Init();
  4.     app_rtc_setwakeup();

  5.     while (1){
  6.         if (true == app_rtc_wakeup_done_flag){
  7.             app_rtc_wakeup_done_flag = false;
  8.             printf("wakeup\r\n");}}}
rtc_tamper
向备份域寄存器中写入非0的数值,配置入侵触发为上升沿触发;当入侵引脚PC13获得一个上升沿,入侵标志置位,备份域寄存器复位。
  1. int main(void){
  2.     BOARD_Init();

  3.     app_rtc_init();
  4.     printf("rtc_tamper.\r\n");

  5.     while (1){
  6.         app_rtc_tamper();}}

5.附件
电路原理图: EVB-L0136_SCH.pdf (486.94 KB, 下载次数: 0)
工程文件 _workspace.zip (2.09 MB, 下载次数: 1)

打赏榜单

21小跑堂 打赏了 40.00 元 2023-08-15
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

单片机典型外设RTC应用实例,基于MM32L0实现RTC实时时钟  发表于 2023-8-15 10:47
 楼主| 春娇霹雳娃 发表于 2023-8-2 11:36 | 显示全部楼层
yszong 发表于 2023-8-15 21:29 | 显示全部楼层
2个预分频器呢
 楼主| 春娇霹雳娃 发表于 2023-8-16 09:14 | 显示全部楼层
单片小菜 发表于 2023-8-16 16:44 | 显示全部楼层
这个时钟的精度如何?
lajfda002 发表于 2023-8-16 16:58 | 显示全部楼层
感觉你的帖子都很厉害,你是大佬,高手。
 楼主| 春娇霹雳娃 发表于 2023-8-16 17:33 | 显示全部楼层
 楼主| 春娇霹雳娃 发表于 2023-8-16 17:33 | 显示全部楼层
lajfda002 发表于 2023-8-16 16:58
感觉你的帖子都很厉害,你是大佬,高手。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:灵动系统开发工程师
简介:none........

19

主题

154

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部