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

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

 



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

 



本实验实现以下功能:

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


 



2.实验环境
硬件环境:

  • EV Board (MM32L0136C7P)
  • Jlink v11.0


软件环境:

  • KEIL V5.37
  • 终端软件Tera Term


 



3.软件配置
时钟配置
RTC可选择时钟源LSE、LSI、HSE128分频。RTC内部包含2个预分频器:7 位的异步预分频器与15位的同步预分频器,用于提供日历或其它功能的时钟。

复制
void CLOCK_RTCToLSE(void){

    RCC->BDCR |= RCC_BDCR_DBP_MASK;

    RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_RTC, true);

    /* Reset RTC BKP. */

    RCC_ResetRTCClock();

    RCC->BDCR &= ~RCC_BDCR_RTCRST_MASK;

    RCC_EnableBKPWriteProtect(false);

    /* Enable LSE clock source. */

    RCC->BDCR |= (RCC_BDCR_LSEON_MASK | RCC_BDCR_RTCSEL(1u));



    while (0u == (RCC->BDCR & RCC_BDCR_LSERDY_MASK) ){}

    RCC_EnableRTCClock(true);}

配置影子寄存器

复制
/* Get current time. */

void RTC_GetTime(RTC_Type * RTCx, RTC_Datetime_Type * time){

    uint32_t ss = RTCx->SSR;

    uint32_t temp1 = RTCx->TR;

    uint32_t temp2 = RTCx->DR;



    time->SubSecs = ( ss & RTC_SSR_SS_MASK);

    time->Secs   = ( (temp1 & ( RTC_TR_ST_MASK  |  RTC_TR_SU_MASK) ) >> RTC_TR_SU_SHIFT );

    time->Mins   = ( (temp1 & ( RTC_TR_MNU_MASK |  RTC_TR_MNT_MASK) ) >> RTC_TR_MNU_SHIFT );

    time->Hours  = ( (temp1 & ( RTC_TR_HU_MASK  |  RTC_TR_HT_MASK) ) >> RTC_TR_HU_SHIFT );

    time->Week   = ( (temp2 & RTC_DR_WDU_MASK ) >> RTC_DR_WDU_SHIFT );

    time->Days   = ( (temp2 & ( RTC_DR_DU_MASK  |  RTC_DR_DT_MASK) ) >> RTC_DR_DU_SHIFT );

    time->Months = ( (temp2 & ( RTC_DR_MU_MASK  |  RTC_DR_MT_MASK) ) >> RTC_DR_MU_SHIFT );

    time->Years  = ( (temp2 & ( RTC_DR_YU_MASK  |  RTC_DR_YT_MASK) ) >> RTC_DR_YU_SHIFT );}


RTC初始化

  • 将RTC的同步预分频、异步预分频及时间为12/24小时制的配置放入RTC_Init()中。
  • 初始化前需要配置RTC_ISR寄存器的INIT位为1,等待INITF位硬件置1后才能进行数据写入。
  • 在进行完初始化及初始时间配置后,清除INIT位,退出初始化模式。

复制
/* Start initialization. */

    RTCx->ISR |= RTC_ISR_INIT_MASK;

    while ( ( 0u == (RTCx->ISR & RTC_ISR_INITF_MASK) ) && (init->TimeOut > 0u) )  /* Prevent the infinite loop of driver. */{

        init->TimeOut--;}

时间配置

  • RTC_TR时间寄存器配置RTC的时分秒,按个位与十位分别配置
  • RTC_DR日期寄存器,年月日以个位与十位分开的形式配置
  • 数据以BCD码格式保存,即用4位二进制数来表示1位十进制数中的0~9这10个数
  • 时间计数分12小时制与24小时制,由RTC_CR寄存器的FMT位控制
  • RTC存在内部计数器,每2个RTC周期,将数值存放入时间寄存器与日期寄存器

复制
/* Get current time. */

void RTC_GetTime(RTC_Type * RTCx, RTC_Datetime_Type * time){

    uint32_t ss = RTCx->SSR;

    uint32_t temp1 = RTCx->TR;

    uint32_t temp2 = RTCx->DR;



    time->SubSecs = ( ss & RTC_SSR_SS_MASK);

    time->Secs   = ( (temp1 & ( RTC_TR_ST_MASK  |  RTC_TR_SU_MASK) ) >> RTC_TR_SU_SHIFT );

    time->Mins   = ( (temp1 & ( RTC_TR_MNU_MASK |  RTC_TR_MNT_MASK) ) >> RTC_TR_MNU_SHIFT );

    time->Hours  = ( (temp1 & ( RTC_TR_HU_MASK  |  RTC_TR_HT_MASK) ) >> RTC_TR_HU_SHIFT );

    time->Week   = ( (temp2 & RTC_DR_WDU_MASK ) >> RTC_DR_WDU_SHIFT );

    time->Days   = ( (temp2 & ( RTC_DR_DU_MASK  |  RTC_DR_DT_MASK) ) >> RTC_DR_DU_SHIFT );

    time->Months = ( (temp2 & ( RTC_DR_MU_MASK  |  RTC_DR_MT_MASK) ) >> RTC_DR_MU_SHIFT );

    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后,再读取寄存器。(等待寄存器同步)

复制
/* Get current time. */

void RTC_GetTime(RTC_Type * RTCx, RTC_Datetime_Type * time){

    uint32_t ss = RTCx->SSR;

    uint32_t temp1 = RTCx->TR;

    uint32_t temp2 = RTCx->DR;



    time->SubSecs = ( ss & RTC_SSR_SS_MASK);

    time->Secs   = ( (temp1 & ( RTC_TR_ST_MASK  |  RTC_TR_SU_MASK) ) >> RTC_TR_SU_SHIFT );

    time->Mins   = ( (temp1 & ( RTC_TR_MNU_MASK |  RTC_TR_MNT_MASK) ) >> RTC_TR_MNU_SHIFT );

    time->Hours  = ( (temp1 & ( RTC_TR_HU_MASK  |  RTC_TR_HT_MASK) ) >> RTC_TR_HU_SHIFT );

    time->Week   = ( (temp2 & RTC_DR_WDU_MASK ) >> RTC_DR_WDU_SHIFT );

    time->Days   = ( (temp2 & ( RTC_DR_DU_MASK  |  RTC_DR_DT_MASK) ) >> RTC_DR_DU_SHIFT );

    time->Months = ( (temp2 & ( RTC_DR_MU_MASK  |  RTC_DR_MT_MASK) ) >> RTC_DR_MU_SHIFT );

    time->Years  = ( (temp2 & ( RTC_DR_YU_MASK  |  RTC_DR_YT_MASK) ) >> RTC_DR_YU_SHIFT );}

闹钟功能:

  • RTC支持闹钟响应功能,当闹钟寄存器中的值与计数器的值相同时,闹钟标志会置位,闹钟可匹配:日期/星期、小时、分钟、秒钟、亚秒
  • 闹钟在配置时需选择是匹配日期还是匹配星期
  • 闹钟可屏蔽不需要匹配的部分时间,例如:若屏蔽日期/星期、小时、分钟,则每分钟的当前闹钟秒数都会产生闹钟响应标志
  • 支持闹钟中断响应。

复制
/* Set alarm time. */

    RTCx->ALARMAR = 0u;

    RTCx->ALARMASSR = 0u;

    uint32_t Alarm = ( RTC_ALARMAR_SU(time->Secs % 10u) | RTC_ALARMAR_ST(time->Secs / 10u) | RTC_ALARMAR_MNU(time->Mins % 10u)

                  | RTC_ALARMAR_MNT(time->Mins / 10u) | RTC_ALARMAR_HU(time->Hours % 10u) | RTC_ALARMAR_HT(time->Hours / 10u)

                  | RTC_ALARMAR_DU(time->Days % 10u) | RTC_ALARMAR_DT(time->Days / 10u) | RTC_ALARMAR_WDSEL(alarm->WeekDaysel) );

闹钟中断
闹钟中断使能&闹钟标志产生 ->闹钟中断

复制
/* RTC enable interrupt. */

void RTC_EnableInterrupt(RTC_Type * RTCx, uint32_t interrupts, bool enable)

周期性唤醒
对RTC_WUTR(唤醒定时器寄存器)中的数据递减,直到递减为0,唤醒标志位置位,寄存器重装载数据,再次递减到0,不断循环。
 


入侵检测
RTC支持入侵检测,通过给入侵引脚一个触发信号,使入侵标志置起,备份域寄存器会在发生入侵事件时复位。

 

复制
/* Configurate tamp and enable tamp. */

void RTC_EnableTampConfig(RTC_Type * RTCx, RTC_Tamp_Type * tamp){

    RTCx->TAMPCR &= ~(RTC_TAMP_TAMP1 | RTC_TAMP_TAMP2);

    RTCx->BKPXR[tamp->BKP] = tamp->BKPData;

    RTCx->TAMPCR = RTC_TAMPCR_TAMPTS(tamp->SaveTime);

    if (tamp->Tamp == RTC_TAMP_TAMP1){

        RTCx->TAMPCR |= RTC_TAMPCR_TAMP1TRG(tamp->Trigger);

        RTC_EnableTamp(RTCx, RTC_TAMPCR_TAMP1E_MASK, true);

    }if (tamp->Tamp == RTC_TAMP_TAMP2){

        RTCx->TAMPCR |= RTC_TAMPCR_TAMP2TRG(tamp->Trigger);

        RTC_EnableTamp(RTCx, RTC_TAMPCR_TAMP2E_MASK, true);}}


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

复制
int main(void){

    uint8_t ch;



    BOARD_Init();

    printf("rtc_basic example.\r\n");

    printf("Press key 'a' to initialize rtc, 'b' to get current time, 'c' to alarm time.\r\n");



    while (1){

        ch = getchar();

        switch(ch){

            case 'a':

                app_rtc_init();

                printf("a: app_rtc_init().\r\n");

            break;

            case 'b':

                app_rtc_get_current_time();

                printf("b: app_rtc_get_current_time().\r\n");

            break;

            case 'c':

                app_rtc_setalalrm();

                printf("c: app_rtc_setalalrm().\r\n");

            break;

            default:

                printf("a: app_rtc_init().\r\n");

                printf("b: app_rtc_get_current_time().\r\n");

                printf("c: app_rtc_setalalrm().\r\n");

            break;}

        printf("%02x-", (unsigned int)(long)rtc_time.Years);

        printf("%02x-", (unsigned int)(long)rtc_time.Months);

        printf("%02x ", (unsigned int)(long)rtc_time.Days);

        printf("%02x ", (unsigned int)(long)rtc_time.Week);

        printf("%02x-", (unsigned int)(long)rtc_time.Hours);

        printf("%02x",  (unsigned int)(long)rtc_time.Mins);

        printf("-%02x", (unsigned int)(long)rtc_time.Secs);

        printf(" %02x", (unsigned int)(long)rtc_time.SubSecs);

        printf("\r\n");}}

rtc_interrupt
按下”a”初始化RTC并配置初始时间,按下”b”获取当前时间,串口输出年、月、日、时、分、秒、亚秒,按下”c”配置闹钟响应时间及闹钟中断,并在闹钟中断响应后输出当前时间。

复制
int main(void){

    uint8_t ch;



    BOARD_Init();

    printf("rtc_interrupt example.\r\n");

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



    while (1){

        ch = getchar();

        switch(ch){

            case 'a':

                app_rtc_init();

                printf("a: app_rtc_init().\r\n");

            break;

            case 'b':

                app_rtc_get_current_time();

                printf("b: app_rtc_get_current_time().\r\n");

            break;

            case 'c':

                app_rtc_setalalrm();

                printf("c: app_rtc_setalalrm().\r\n");

            break;

            default:

                printf("a: app_rtc_init().\r\n");

                printf("b: app_rtc_get_current_time().\r\n");

                printf("c: app_rtc_setalalrm().\r\n");

            break;}

        printf("%02x-", (unsigned int)(long)rtc_time.Years);

        printf("%02x-", (unsigned int)(long)rtc_time.Months);

        printf("%02x ", (unsigned int)(long)rtc_time.Days);

        printf("%02x ", (unsigned int)(long)rtc_time.Week);

        printf("%02x-", (unsigned int)(long)rtc_time.Hours);

        printf("%02x",  (unsigned int)(long)rtc_time.Mins);

        printf("-%02x", (unsigned int)(long)rtc_time.Secs);

        printf(" %02x", (unsigned int)(long)rtc_time.SubSecs);

        printf("\r\n");}}

rtc_wakeup
设置RTC周期性唤醒的时间为10s,当检测到唤醒标志,串口打印”wakeup”。

复制
int main(void){

    BOARD_Init();



    app_rtc_Init();

    app_rtc_setwakeup();



    while (1){

        if (true == app_rtc_wakeup_done_flag){

            app_rtc_wakeup_done_flag = false;

            printf("wakeup\r\n");}}}

rtc_tamper
向备份域寄存器中写入非0的数值,配置入侵触发为上升沿触发;当入侵引脚PC13获得一个上升沿,入侵标志置位,备份域寄存器复位。

复制
int main(void){

    BOARD_Init();



    app_rtc_init();

    printf("rtc_tamper.\r\n");



    while (1){

        app_rtc_tamper();}}

---------------------
作者:春娇霹雳娃
链接:https://bbs.21ic.com/icview-3317962-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值