摘要
在HT32F52352合泰单片机开发中,rtc在网上还是挺少人应用的,找了很久没什么资料,现在我根据手册和官方的代码进行配置理解。 RTC在嵌入式单片机中是一个很重要的应用资源。
-
记录事件时间戳:RTC可以记录事件发生的精确时间,方便后续分析和追溯。
-
定时操作:通过RTC可以实现定时操作,例如定时采集数据、定时发送信息等。
-
时间相关功能:RTC可以提供当前的日期和时间信息,方便系统中进行时间相关的业务逻辑。
-
实时显示:RTC可以在设备上实时显示当前的时间,方便用户查看。
-
节能功能:通过RTC可以实现设备定时开关机等节能功能,提高设备的能效。
介绍
我们在做项目的时候,要用到时间计时,我们刚开始用的定时器,这个比较简单,但是后来我们想到一个问题就是如果我们芯片如果卡死复位了,掉电了,那么我们整一个流程中计时功能又从头开始。基于严谨考虑我觉得使用芯片另一个芯片资源符合我要求的就是rtc时钟。怎么用呢,下面按照我们理解来实现。
硬件原理图
VBAT供电如图1,2;芯片内部图如图3,4
这里如果是画PCB可以应用如图1,2电路,如果直接用核心板那我们可以接一个3.3v电池,接到VCC-VBAT,然后接一个GND
图1
图2
图3
图4
从手册和图3可知,为了确保RTC在掉电情况下继续运行,可以将低速外部时钟(LSE)接到VBAT引脚上,这样即使系统掉电,RTC可以通过接入的外部时钟继续运行。同时,在系统上电后,可以通过开启低速外部时钟(LES)来确保RTC的正常运行和准确性。这样可以有效保证RTC在掉电情况下的稳定性和准确性,确保系统能够恢复正常工作。
配置寄存器流程如图5
图5
下面是对rtc进行配置
rtc时钟中断进行初始化
cs
void rtc_init()
{
//使能备份域时钟 等待可以被操作
CKCU_PeripClockConfig_TypeDef CKCUClock = {{0}};
CKCUClock.Bit.BKP = 1;
CKCU_PeripClockConfig(CKCUClock, ENABLE);
if (PWRCU_CheckReadyAccessed() != PWRCU_OK)
{
while (1);
}
NVIC_EnableIRQ(RTC_IRQn);
}
时间
对时间年份,月份进行处理,官方就是严谨。主要有两个函数,第一为判断是否为闰年,第二个函数数计算当前时间。
cs
//判断是否是闰年
bool IsLeapYear(u32 year)
{
if (((year % 4 == 0) && (year % 100 != 0) ) || (year % 400 == 0) )
return TRUE;
else
return FALSE;
}
//调节时间
u8 AP_Time_Adjust(Time_T* AdjustTime)
{
u32 i, temp, secsum = 0;
temp = AdjustTime->year - 1;
for (i = 0; i < (AdjustTime->year - 2014); i++)
{
if (IsLeapYear(temp--) == TRUE)
{
secsum += (366 * 86400);
}
else
{
secsum += (365 * 86400);
}
}
temp = 1;
for (i = 0; i < (AdjustTime->month - 1); i++)
{
if (temp == 2)
{
if (IsLeapYear(AdjustTime->year) == TRUE)
secsum += (29 * 86400);
else
secsum += (28 * 86400);
}
else
{
secsum += (Day_Per_Month[temp] * 86400);
}
temp++;
}
secsum += ((AdjustTime->day - 1) * 86400);
secsum += (AdjustTime->hour * 3600 );
secsum += (AdjustTime->minute * 60);
secsum += (AdjustTime->second);
PWRCU_WriteBackupRegister((PWRCU_BAKREG_Enum) PWRCU_BAKREG_1, secsum);
if (PWRCU_ReadBackupRegister((PWRCU_BAKREG_Enum) PWRCU_BAKREG_1) != secsum)
{
return 0;
}
return 1;
}
//计算当前时间
u8 AP_Time_Count(Time_T* CurrentTime)
{
u32 i, secsum = 0, temp = 0;
secsum = PWRCU_ReadBackupRegister((PWRCU_BAKREG_Enum) PWRCU_BAKREG_1);
secsum += RTC_GetCounter();
temp = 0;
while (secsum >= (365 * 86400))
{
if (IsLeapYear(2014 + temp))
{
if (secsum >= (366 * 86400))
{
temp++;
secsum -= (366 * 86400);
}
else
{
break;
}
}
else
{
temp++;
secsum -= (365 * 86400);
}
}
CurrentTime->year = 2014 + temp;
for (i = 1; i <= 12; i++)
{
if (secsum >= (Day_Per_Month[i] * 86400))
{
if (i == 2) // February
{
if (IsLeapYear(CurrentTime->year))
{
if (secsum >= (29 * 86400))
secsum -= (29 * 86400);
else
break;
}
else
{
secsum -= (28 * 86400);
}
}
else
{
secsum -= (Day_Per_Month[i] * 86400);
}
}
else
{
break;
}
}
CurrentTime->month = i;
CurrentTime->day = secsum / 86400 + 1;
secsum -= ((CurrentTime->day - 1) * 86400);
CurrentTime->hour = secsum / 3600;
CurrentTime->minute = (secsum % 3600) / 60;
CurrentTime->second = (secsum % 3600) % 60;
return 1;
}
RTC进行配置
这段代码是一个配置RTC(Real-Time Clock)的函数。函数包括以下步骤:
调用PWRCU_DeInit()函数,对电源控制单元进行复位操作,确保RTC处于初始状态。
调用RTC_LSESMConfig()函数配置外部32.768k振荡器为正常模式。
调用RTC_LSECmd()函数使能LSE(Low Speed External)时钟。
使用while循环等待LSE稳定就绪,通过CKCU_GetClockReadyStatus()函数判断外部时钟是否准备就绪。
调用RTC_ClockSourceConfig()函数配置RTC时钟源为LSE。
调用RTC_IntConfig()函数使能秒中断。
调用RTC_SetPrescaler()函数设置RTC的分频器分频系数为32768。
调用RTC_CMPCLRCmd()函数使能比较器清零功能。
cs
void RTC_Configuration(void)
{
PWRCU_DeInit();
//配置外部32.768k振荡器
RTC_LSESMConfig(RTC_LSESM_NORMAL);
RTC_LSECmd(ENABLE);
while (CKCU_GetClockReadyStatus(CKCU_FLAG_LSERDY) == RESET);
RTC_ClockSourceConfig(RTC_SRC_LSE);
RTC_IntConfig(RTC_INT_CSEC, ENABLE);
RTC_SetPrescaler(RTC_RPRE_32768);
RTC_CMPCLRCmd(ENABLE);
}
RTC中断函数
这里设置了一个标志位,当标志位值1是开启RTC中断。
cs
//RTC中断
void RTC_IRQHandler(void)
{
u8 bFlags;
bFlags = RTC_GetFlagStatus();
if (bFlags & 0x1)
{
//1s更新标志位
CK_SECOND_Flag = 1;
}
}
主函数
主函数主要对rtc的应用
cs
#include "ht32.h"
#include "ht32_board.h"
#include "led.h"
#include "delay.h"
#include "USART.h"
#include "IIC.h"
#include "SHT30.h"
//#include "modbus485.h"
#include "motor.h"
#include "DC_Motor.h"
//#include "Timer.h"
#include "Lock.h"
#include "UART.h"
#include "RTC.h"
Time_T DateTime, CurTime;
int main()
{
char datebuff[40];
rtc_init();
USART0_Configuration();
//读取备份域寄存器 判断RTC是否已经配置
if (PWRCU_ReadBackupRegister((PWRCU_BAKREG_Enum) PWRCU_BAKREG_0) != 0xAA55AA55)
{
// RTC 配置
RTC_Configuration();
//设置时间
DateTime.year = 2024;
DateTime.month = 4;
DateTime.day = 29;
DateTime.hour = 18;
DateTime.minute = 0;
DateTime.second = 0;
//使能 RTC
RTC_Cmd(ENABLE);
PWRCU_WriteBackupRegister((PWRCU_BAKREG_Enum) PWRCU_BAKREG_0, 0xAA55AA55);
}
while(1)
{
//标志位值1
if (CK_SECOND_Flag)
{
CK_SECOND_Flag = 0;
//显示当前时间
AP_Time_Count(&CurTime);
//sprintf函数将当前时间信息(年、月、日、时、分、秒)按指定的格式写入到datebuff字符数组中
sprintf(datebuff,"%04d-%02d-%02d %02d:%02d:%02d",CurTime.year,
CurTime.month,
CurTime.day,
CurTime.hour,
CurTime.minute,
CurTime.second);
printf("%s\r\n", datebuff);
}
}
}
演示
直接串口助手演示,3.3V接到VBAT,GND接地,rx-tx,tx-rx。串口助手按照rtc中断每1s打印数据
就算按复位或者烧录,设置T口拔电源,日期计数不变
验证:成功