#技术资源# #申请原创#
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的日历如下:
|