打印
[活动专区]

【杰发科技AC7802x测评】5. RTC应用之实现RTC日历

[复制链接]
275|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hehung|  楼主 | 2023-6-11 17:58 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
#技术资源# #申请原创#
1 RTC介绍

RTC是芯片内部自带的实时时钟芯片,用于精确的自动累计时间,用户字需要设置初始时间,RTC就会自动累计,减少了CPU参与,降低了CPU的负载率,而且RTC的精度很高。本帖将测试AC7802X的RTC。

关于RTC的介绍,可以参考用户手册《ATC_AC7802x_ReferenceManual_CH.pdf》18 实时计数器模块(RTC)

>  AC7802X的RTC支持功能较少,没有硬件上的年月日时分秒设置以及获取功能。只支持实时数以及低功耗唤醒功能。本帖将使用软件的方式实现年月日,星期,时分秒的设置,自动累计以及获取功能




本实验通过软件的方式实现了日期时间,星期的自动累计,星期会自动计算,不用手动设置。

2 软件实现

2.1 打开RTC功能

如需正常使用RTC功能,需要现在"Manage Run-Time Enviromment"中加载RTC功能,如下图。



2.2 初始化RTC

如下代码,实现了对RTC的初始化,并且设置了1s进入一次RTC中断,中断的回调函数为“Rtc_CallbackFunc”。

其中中断间隔时间设置为"(99+1)*(159999+1)/16000000 = 1s"。

Rtc_UserSetDateTime为软件实现的设置当前时间日期的函数,后面说明。

// 初始化Rtc,设置RTC超时时间为1s
void Rtc_UserInit(void)
{
    uint32_t tmpMod = 159999;                       /*!设置RTC模值 */
    uint32_t tmpPrescalerValue = 99;                /*!设置分频比 */
    RTC_ConfigType RTCConfig;                       /*! (99+1)*(159999+1)/16000000 = 1s, timeout = (pre+1)*(mod+1)/clk */
  
    memset(&RTCConfig,0,sizeof(RTCConfig));

    RTCConfig.clockSource = RTC_CLOCK_APB;          /*! 时钟源选择:RTC_CLOCK_APB,默认为16M */                                                
    RTCConfig.periodValue = tmpMod;                 /*! 模值赋给RTCConfig结构体 */
    RTCConfig.psrInterruptEn = DISABLE;             /*! 关闭实时预分频器中断 */
    RTCConfig.rtcInterruptEn = ENABLE;              /*! RTC中断使能 */
    RTCConfig.psrValue = tmpPrescalerValue;         /*! 预分频比赋值给RTCConfig结构体 */
    RTCConfig.rtcOutEn = DISABLE;                   /*! 禁能RTC输出 */
    RTCConfig.callBack = Rtc_CallbackFunc;           /*! RTC回调函数配置 */
  
    RTC_Init(&RTCConfig);                           /*! RTC初始化函数生效 */

    Rtc_UserSetDateTime();
}



2.3 时间累计函数

由于AC7802X没有提供硬件上的日期时间的自动累计,所以需要以来软件来实现。

>  硬件上没有实现日期时间的累计功能,RTC计时器就显得比较鸡肋,因为定时器可以实现同样的功能。

- Rtc_IsLeapYear函数用于判断指定年份是否为闰年
- Rtc_FindWeekDay为泰勒公式的软件实现,用于根据年月日计算星期。就算不设置星期,也可以自动计算出来。
- Rtc_DateTimeIncrease为日期时间累计函数,当初试时间设置之后,该函数会自动累计时间。该函数需要放在回调函数中,每一秒钟调用一次。

// 判断某一年是否为闰年
static uint8_t Rtc_IsLeapYear(uint8_t year)
{
    uint16_t full_year = 2000 + year;

    if (((full_year % 4 == 0 ) && (full_year % 100 != 0)) ||
         (year % 400 == 0))
    {
        return 1; // 闰年
    }
    else
    {
        return 0; // 非闰年
    }
}

// 泰勒公式求星期
static uint8_t Rtc_FindWeekDay(void)
{
    int m = current_dt.mon;
    int d = current_dt.mday;
    int year = current_dt.year + 2000;

    // 根据月份对年份和月份进行调整
    if(m <= 2)
    {
        year -= 1;
        m += 12;
    }

    int c = year / 100; // 取得年份前两位
    int y = year % 100; // 取得年份后两位
   
    // 根据泰勒公式计算星期
    int w = (int)(c/4) - 2*c + y + (int)(y/4)
        + (int)(13*(m+1)/5) + d - 1;

  
    return w%7; // 返回星期
}

// 日期时间累计函数,硬件没有实现,需要软件实现
static void Rtc_DateTimeIncrease(void)
{
    // seconds increase
    if (current_dt.sec >= 59U)
    {
        current_dt.sec = 0U;
   
        if (current_dt.min >= 59U)
        {
            current_dt.min = 0;
        
            if (current_dt.hour >= 23)
            {
                current_dt.hour = 0U;
            
                if (current_dt.mday >= month_day_num[current_dt.mon-1])
                {
                    current_dt.mday = 1;
                    if (current_dt.mon >= 12)
                    {
                        current_dt.mon = 1;
                        current_dt.year++;
                        // 年变化时,求2月天数
                        month_day_num[1] = (Rtc_IsLeapYear(current_dt.year) == 1) ? (29U) : (28U);
                    }
                    else
                    {
                        current_dt.mon++;
                    }
                }
                else
                {
                    current_dt.mday++;
                }
            
                if (current_dt.weekday >= 7U)
                {
                    current_dt.weekday = 0U;
                }
                else
                {
                    current_dt.weekday++;
                }
            }
            else
            {
                current_dt.hour++;
            }
        }
        else
        {
            current_dt.min++;
        }
    }
    else
    {
        current_dt.sec++;
    }

}



2.4 全部代码

下面是全部代码,包括了RTC初始化,时间设置,打印等。

每一秒中打印一次当前时间,日期,星期。

设定的初始时间为2023年12月31日23点59分50秒,为了测试时间累计函数是否成功。

/*
@hehung
2023-6-11
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/

#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "ac780x_gpio.h"
#include "ac780x_rtc.h"
#include "ac7802x.h"
#include "app_rtc.h"

static rtc_time_t current_dt = {0, 0, 0, 0, 0, 0, 0};
static uint8_t month_day_num[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


static void Rtc_CallbackFunc(void *device, uint32_t wpara, uint32_t lpara);
static uint8_t Rtc_IsLeapYear(uint8_t year);
static void Rtc_DateTimeIncrease(void);


// Rtc 回调函数
static void Rtc_CallbackFunc(void *device, uint32_t wpara, uint32_t lpara)
{
    if (wpara & RTC_SC_RTIF_Msk)                                          /*! RTC溢出中断 */
    {
        Rtc_DateTimeIncrease();
        Rtc_ShowTime();
    }   
}


// 判断某一年是否为闰年
static uint8_t Rtc_IsLeapYear(uint8_t year)
{
    uint16_t full_year = 2000 + year;

    if (((full_year % 4 == 0 ) && (full_year % 100 != 0)) ||
         (year % 400 == 0))
    {
        return 1; // 闰年
    }
    else
    {
        return 0; // 非闰年
    }
}

// 泰勒公式求星期
static uint8_t Rtc_FindWeekDay(void)
{
    int m = current_dt.mon;
    int d = current_dt.mday;
    int year = current_dt.year + 2000;

    // 根据月份对年份和月份进行调整
    if(m <= 2)
    {
        year -= 1;
        m += 12;
    }

    int c = year / 100; // 取得年份前两位
    int y = year % 100; // 取得年份后两位
   
    // 根据泰勒公式计算星期
    int w = (int)(c/4) - 2*c + y + (int)(y/4)
        + (int)(13*(m+1)/5) + d - 1;

  
    return w%7; // 返回星期
}

// 日期时间累计函数,硬件没有实现,需要软件实现
static void Rtc_DateTimeIncrease(void)
{
    // seconds increase
    if (current_dt.sec >= 59U)
    {
        current_dt.sec = 0U;
   
        if (current_dt.min >= 59U)
        {
            current_dt.min = 0;
        
            if (current_dt.hour >= 23)
            {
                current_dt.hour = 0U;
            
                if (current_dt.mday >= month_day_num[current_dt.mon-1])
                {
                    current_dt.mday = 1;
                    if (current_dt.mon >= 12)
                    {
                        current_dt.mon = 1;
                        current_dt.year++;
                        // 年变化时,求2月天数
                        month_day_num[1] = (Rtc_IsLeapYear(current_dt.year) == 1) ? (29U) : (28U);
                    }
                    else
                    {
                        current_dt.mon++;
                    }
                }
                else
                {
                    current_dt.mday++;
                }
            
                if (current_dt.weekday >= 7U)
                {
                    current_dt.weekday = 0U;
                }
                else
                {
                    current_dt.weekday++;
                }
            }
            else
            {
                current_dt.hour++;
            }
        }
        else
        {
            current_dt.min++;
        }
    }
    else
    {
        current_dt.sec++;
    }

}


// 设置当前时间
void Rtc_DateTimeInit(rtc_time_t dt)
{
    current_dt.year    = dt.year;
    current_dt.mon     = dt.mon;
    current_dt.mday    = dt.mday;
    current_dt.weekday = dt.weekday;
    current_dt.hour    = dt.hour;
    current_dt.min     = dt.min;
    current_dt.sec     = dt.sec;

    // 2月天数
    month_day_num[1] = (Rtc_IsLeapYear(current_dt.year) == 1) ? (29U) : (28U);
    // 根据泰勒公式计算真实星期
    current_dt.weekday = Rtc_FindWeekDay();
}

void Rtc_GetDateTime(rtc_time_t *date_time)
{
    *date_time = current_dt;
}

void Rtc_ShowTime(void)
{
    static const char *week[7] = {"Sun.", "Mon.", "Tue.", "Wed.", "Thu.", "Fri.", "Sat."};  
    rtc_time_t date_time;

    Rtc_GetDateTime(&date_time);


    printf ("DateTime:20%02d-%02d-%02d(%s) %02d:%02d:%02d\r\n", date_time.year,
                                          date_time.mon,
                                          date_time.mday,
                                          week[(uint8_t)date_time.weekday],
                                          date_time.hour,
                                          date_time.min,
                                          date_time.sec);
}



// 初始化Rtc,设置RTC超时时间为1s
void Rtc_UserInit(void)
{
    uint32_t tmpMod = 159999;                       /*!设置RTC模值 */
    uint32_t tmpPrescalerValue = 99;                /*!设置分频比 */
    RTC_ConfigType RTCConfig;                       /*! (99+1)*(159999+1)/16000000 = 1s, timeout = (pre+1)*(mod+1)/clk */
  
    memset(&RTCConfig,0,sizeof(RTCConfig));

    RTCConfig.clockSource = RTC_CLOCK_APB;          /*! 时钟源选择:RTC_CLOCK_APB,默认为16M */                                                
    RTCConfig.periodValue = tmpMod;                 /*! 模值赋给RTCConfig结构体 */
    RTCConfig.psrInterruptEn = DISABLE;             /*! 关闭实时预分频器中断 */
    RTCConfig.rtcInterruptEn = ENABLE;              /*! RTC中断使能 */
    RTCConfig.psrValue = tmpPrescalerValue;         /*! 预分频比赋值给RTCConfig结构体 */
    RTCConfig.rtcOutEn = DISABLE;                   /*! 禁能RTC输出 */
    RTCConfig.callBack = Rtc_CallbackFunc;           /*! RTC回调函数配置 */
  
    RTC_Init(&RTCConfig);                           /*! RTC初始化函数生效 */

    Rtc_UserSetDateTime();
}

void Rtc_UserSetDateTime(void)
{
    rtc_time_t dt;

    dt.year = 23;
    dt.mon = 12;
    dt.mday = 31;
    dt.hour = 23;
    dt.min = 59;
    dt.sec = 50;

    Rtc_DateTimeInit(dt);
}


```

2.5 主函数

主函数中只需要调用Rtc_UserInit即可。

```cpp
int main(void)
{
    InitDelay();
  
    UART_Cfg_Init();                          /*! 串口1初始化 */

    Rtc_UserInit();

    while(1)
    {

    }
}




3 实验效果

下面展示了实现效果。

初始时间为2023年12月31日23点59分50秒,目的是测试时间累计功能是否成功。

不用设置星期参数,就算设置了也不会识别,因为星期参数会通过泰勒公式自动计算出来,实验验证无误。



2023年12月31的日历如下:






使用特权

评论回复

相关帖子

沙发
xixi2017| | 2023-6-13 09:11 | 只看该作者
厂家做的好啊,外设驱动都集成到RTE里面了,方便配置和修改工程。

使用特权

评论回复
评论
hehung 2023-6-13 17:28 回复TA
是的,这点做的很好,集成开发起来很方便,都不需要花时间搭建开发环境 
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

66

帖子

1

粉丝