[MCU] [极海M3内核 APM32E103VET6S MINI开发板]03.RTC&日历功能

xld0932   2022-9-26 15:38 楼主

APM32E103VET6S微控制器带有一个RTC实时时钟单元,它可以应用在我们需要显示日期或者时间的应用场合,通过内部或者外部的时钟源实现精确走时,相比于定时器实现的RTC要准确得多。本方通过RTC的秒中断功能,再结合软件处理实现日期和时间的打印输出,具体步骤如下:

  • 定义一个日期、时间结构体及结构体全局变量,定义一个基础月份对应的天数数组:
typedef struct
{
    uint16_t year;
    uint8_t  month;
    uint8_t  day;
    uint8_t  week;
    uint8_t  hour;
    uint8_t  minute;
    uint8_t  second;
} CALENDAR_TypeDef;

const uint8_t    RTC_DayOfMonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
CALENDAR_TypeDef RTC_Calendar;
  • 闰年判断函数:
/*******************************************************************************
 * [url=home.php?mod=space&uid=159083]@brief[/url] * @param       
 * @retval      
 * [url=home.php?mod=space&uid=1020061]@attention[/url] *******************************************************************************/
uint8_t RTC_LeapYear(uint16_t Year)
{
    if(
        (((Year % 400) == 0)                     ) ||   /* Century Leap Year */
        (((Year % 100) != 0) && ((Year % 4) == 0))      /* Normal  Leay Year */
    )
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
  • 通过蔡勒公式获取当前日期对应的是星期几:
/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
uint8_t RTC_GetWeek(uint16_t Year, uint8_t Month, uint8_t Day)
{
    int w, c, y;

    /* Month 1 Or 2 of This Year Must Be As Last Month 13 Or 14 */
    if((Month == 1) || (Month == 2))
    {
        Month += 12;
        Year  -= 1;
    }

    w = 0;          /* Weekday */
    c = Year / 100; /* Century */
    y = Year % 100; /* Year    */

    w = y + (y / 4) + (c / 4) - (2 * c) + (26 * (Month + 1) / 10) + Day - 1;

    while(w < 0) w += 7;

    w %= 7;

    return w;
}
  • 通过读取当前RTC计数器的累加值,将其转换为对应的日期和时间信息:
/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RTC_UpdateCalendar(void)
{
    static uint32_t PreTotalDay = 0;

    uint32_t TotalSecond = 0;
    uint32_t TotalDay    = 0;

    uint16_t Year  = 1970;
    uint8_t  Month = 0;

    /* Get The RTC Counter Value */
    TotalSecond = RTC_ReadCounter();
    TotalDay    = TotalSecond / 86400;

    if(PreTotalDay != TotalDay)
    {
        PreTotalDay = TotalDay;

        while(TotalDay >= 365)
        {
            if(RTC_LeapYear(Year) == 1)
            {
                if(TotalDay >= 366)
                {
                    TotalDay -= 366;
                }
                else
                {
                    break;
                }
            }
            else
            {
                TotalDay -= 365;
            }

            Year++;
        }

        RTC_Calendar.year = Year;

        while(TotalDay >= 28)
        {
            if((Month == 1) && (RTC_LeapYear(RTC_Calendar.year) == 1))
            {
                if(TotalDay >= 29)
                {
                    TotalDay -= 29;
                }
                else
                {
                    break;
                }
            }
            else
            {
                if(TotalDay >= RTC_DayOfMonth[Month])
                {
                    TotalDay -= RTC_DayOfMonth[Month];
                }
                else
                {
                    break;
                }
            }

            Month++;
        }

        RTC_Calendar.month = Month    + 1;
        RTC_Calendar.day   = TotalDay + 1;

        RTC_Calendar.week  = RTC_GetWeek(RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);
    }

    RTC_Calendar.hour   =  (TotalSecond % 86400) / 3600;
    RTC_Calendar.minute = ((TotalSecond % 86400) % 3600) / 60;
    RTC_Calendar.second = ((TotalSecond % 86400) % 3600) % 60;
}
  • 通过日期和时间信息将其转换为RTC的计数值,并将计数值设置到RTC中:
/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RTC_SetDateTime(uint16_t Year, uint8_t Month, uint8_t Day,
                     uint8_t  Hour, uint8_t Min,   uint8_t Sec)
{
    uint32_t TotalSecond = 0;

    uint16_t y = 0;
    uint8_t  m = 0;

    if((Year >= 1970) && (Year <= 2099))
    {
        for(y = 1970;  y < Year; y++)
        {
            if(RTC_LeapYear(y) == 1)
            {
                TotalSecond += 31622400;    /* Total Seconds Of Leap   Year */
            }
            else
            {
                TotalSecond += 31536000;    /* Total Seconds Of Normal Year */
            }
        }

        for(m = 0; m < (Month - 1); m++)
        {
            TotalSecond += RTC_DayOfMonth[m] * 86400;   /* Total Seconds Of Month */

            if((RTC_LeapYear(Year) == 1) && (m == 1))
            {
                TotalSecond += 86400;
            }
        }

        TotalSecond += (uint32_t)(Day - 1) * 86400; /* Total Seconds Of Day    */
        TotalSecond += (uint32_t)Hour      * 3600;  /* Total Seconds Of Hour   */
        TotalSecond += (uint32_t)Min       * 60;    /* Total Seconds Of Minute */
        TotalSecond += Sec;

        RTC_ConfigCounter(TotalSecond);
        RTC_WaitForLastTask();
    }
    else
    {
        printf("\r\nError Date & Time!!!\r\n");
    }
}
  • 打印日期和时间信息:
/******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
******************************************************************************/
void RTC_PrintDateTime(void)
{
    printf("\r\n%04d-%02d-%02d", RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day);

    switch(RTC_Calendar.week)
    {
        case 0 : printf(" SUN "); break;
        case 1 : printf(" MON "); break;
        case 2 : printf(" TUE "); break;
        case 3 : printf(" WED "); break;
        case 4 : printf(" THU "); break;
        case 5 : printf(" FRI "); break;
        case 6 : printf(" SAT "); break;
        default: break;
    }

    printf("%02d:%02d:%02d\r\n", RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
}
  • 自动加载并提取当前工程编译生成时的日期和时间信息,用于后面初始化日期和时间用:
/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RTC_LoadDefault(void)
{
    char    date[20], time[20];
    char    text[6][5];
    uint8_t index = 0, month = 0;

    memset(date, 0, sizeof(date));
    memset(time, 0, sizeof(time));
    memset(text, 0, sizeof(text)); 

    memcpy(date, __DATE__, sizeof(__DATE__));
    memcpy(time, __TIME__, sizeof(__TIME__));

    char *str;

    str = strtok(date, " ");

    while(str != NULL)
    {
        memcpy(text[index], str, strlen(str));
        index++;

        str = strtok(NULL, " ");
    }

    str = strtok(time, ":");

    while(str != NULL)
    {
        memcpy(text[index], str, strlen(str));
        index++;

        str = strtok(NULL, ":");
    }

#if 0
    for(uint8_t i = 0; i < index; i++)
    {
        printf("\r\n->%s", text[i]);
    }
#endif

    char *strMonth[12] =
    {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    };

    for(uint8_t i = 0; i < 12; i++)
    {
        if(strcmp(text[0], strMonth[i]) == 0)
        {
            month = i + 1;
        }
    }

    RTC_Calendar.day    = atoi(text[1]);
    RTC_Calendar.month  = month;
    RTC_Calendar.year   = atoi(text[2]);
    RTC_Calendar.week   = RTC_GetWeek(RTC_Calendar.year,
                                      RTC_Calendar.month,
                                      RTC_Calendar.day);

    RTC_Calendar.hour   = atoi(text[3]);
    RTC_Calendar.minute = atoi(text[4]);
    RTC_Calendar.second = atoi(text[5]);
}
  • RTC初始化及中断处理实现:
/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RTC_Init(void)
{
    RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)RCM_APB1_PERIPH_PMU);
    PMU_EnableBackupAccess();

    RCM_EnableLSI();
    while(RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);

    RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
    RCM_EnableRTCCLK();

    RTC_WaitForSynchro();
    RTC_WaitForLastTask();

    RTC_EnableInterrupt(RTC_INT_SEC);
    RTC_WaitForLastTask();

    RTC_ConfigPrescaler(32767);
    RTC_WaitForLastTask();

    RTC_LoadDefault();
    RTC_SetDateTime(RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day,
                    RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
    RTC_WaitForLastTask();

    NVIC_EnableIRQRequest(RTC_IRQn, 0, 0);
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RTC_IRQHandler(void)
{
    if(RTC_ReadIntFlag(RTC_INT_SEC) != RESET) 
    {
        RTC_UpdateCalendar();

        RTC_PrintDateTime();
    }

    RTC_ClearIntFlag(RTC_INT_SEC | RTC_INT_OVR);
    RTC_WaitForLastTask();
}

 

 

运行结果:

1.png

 

软件工程源代码:

Project.zip (293.36 KB)
(下载次数: 10, 2022-9-26 15:38 上传)
We are a team and we work as a team !

回复评论 (2)

  • RTC使用使用LSI或者是LSE作为时钟源的配置:
/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RTC_Init(void)
{
#if 0
    RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)RCM_APB1_PERIPH_PMU);
    PMU_EnableBackupAccess();

    RCM_EnableLSI();
    while(RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);

    RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
    RCM_EnableRTCCLK();
#else
    RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)(RCM_APB1_PERIPH_PMU | RCM_APB1_PERIPH_BAKR));
    PMU_EnableBackupAccess();

    BAKPR_Reset();

    RCM_ConfigLSE(RCM_LSE_OPEN);
    while(RCM_ReadStatusFlag(RCM_FLAG_LSERDY) == RESET);

    RCM_ConfigRTCCLK(RCM_RTCCLK_LSE);
    RCM_EnableRTCCLK();
#endif

    RTC_WaitForSynchro();
    RTC_WaitForLastTask();

    RTC_EnableInterrupt(RTC_INT_SEC);
    RTC_WaitForLastTask();

    RTC_ConfigPrescaler(32767);
    RTC_WaitForLastTask();

    RTC_LoadDefault();
    RTC_SetDateTime(RTC_Calendar.year, RTC_Calendar.month,  RTC_Calendar.day,
                    RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
    RTC_WaitForLastTask();

    NVIC_EnableIRQRequest(RTC_IRQn, 0, 0);
}

 

本帖最后由 xld0932 于 2022-9-28 09:00 编辑
We are a team and we work as a team !
点赞  2022-9-27 13:48

楼主的RTC示例写得非常清晰明了,感分享!

点赞  2022-9-27 15:03
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复