首先,要明白的时,RTC要想显示一个日期和时间的话,必须要明白RTC的工作原理
RTC也是一个定时器,我们可以把RTC配置为定时1s的定时器
其次,我们需要一个基准时间,比如1970年1月1日,8:00:00分,这是我们的时间基准,然后我们在这个基准上让RTC定时,当RTC在基准时间上跑了60s时,那么此时的时间就是1970年1月1日,8:01:00分
接下来讲一下,什么时间戳,时间戳就是指一种记录时间的方式,简单的说,时间戳就是一串数字,表示从某个固定时间点开始到现在的时间长度,通常以秒为单位计算。如果以1970年1月1日,8:00:00分为基准时间的话,那么1970年1月1日,8:01:00分的时间戳就是60
配置好RTC的底层代码,让RTC跑起来
#include "bsp_rtc.h"
#ifndef __BSP_RTC_H__
#define __BSP_RTC_H__
#include "gd32f30x.h"
#include <stdio.h>
typedef struct _date_time{
uint16_t year;
uint16_t month;
uint16_t day;
uint16_t hour;
uint16_t min;
uint16_t second;
}date_time_t;
/*测试函数*/
void RTC_init(void);
void time_adjust(uint32_t current_senconds);
uint16_t fml_leap_year(uint16_t year);
uint32_t fml_time_to_stamp(date_time_t date);
uint32_t fml_stamp_to_time( uint32_t timep, date_time_t *date);
配置RTC
#include "bsp_rtc.h"
/* 秒中断标志 */
uint8_t time_interrupt_flag;
//当前日期时间
date_time_t date_time;
date_time_t setting_date_time;
/*
使能中断管理器并分配优先级
*/
/*
配置RTC
*/
void rtc_configuration(void)
{
/* 备份区域的时钟使能*/
rcu_periph_clock_enable(RCU_BKPI);
//电源管理时钟使能
rcu_periph_clock_enable(RCU_PMU);
/* 备份区允许访问 */
pmu_backup_write_enable();
/* 备份域复位 */
bkp_deinit();
/* 使能外部低速时钟 */
rcu_osci_on(RCU_LXTAL);
/* 等待低速晶体振荡器稳定 */
rcu_osci_stab_wait(RCU_LXTAL);
/* RTC 时钟源选择 */
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
/* 使能RTC时钟 */
rcu_periph_clock_enable(RCU_RTC);
/* 等待寄存器与APB1时钟同步 */
rtc_register_sync_wait();
/* 等待最后一次操作完成 */
rtc_lwoff_wait();
/* 使能RTC秒中断*/
rtc_interrupt_enable(RTC_INT_SECOND);
/* 等待最后一次操作完成*/
rtc_lwoff_wait();
/*设置预分频 外部时钟是32.768Hz 分频后就是 1S*/
rtc_prescaler_set(32767);
/* 等待最后一次操作完成 */
rtc_lwoff_wait();
}
void nvic_rtc_configuration(void)
{
nvic_irq_enable(RTC_IRQn, 1, 1);
}
初始化RTC
void RTC_init(void)
{
/* NVIC configure */
nvic_rtc_configuration();
printf( "This is a RTC demo...... \r\n" );
//读取备份区寄存器0的数据,也就是 查看时间是否已经配置
if (bkp_read_data(BKP_DATA_0) != 0xA5A5)
{
/* 备份寄存器的值不是0xa5a5,说明RTC没有配置过 */
printf("This is a RTC demo!\r\n");
printf("RTC not yet configured....\r\n");
/* 配置 */
rtc_configuration();
printf("RTC configured ok\r\n");
/* 首次调整时间 传入一个时间戳*/
time_adjust(946659660);
bkp_write_data(BKP_DATA_0, 0xA5A5);
}
else//不需要更新时间
{
/* 检查是否是电源复位标志 */
if (rcu_flag_get(RCU_FLAG_PORRST) != RESET)
{
printf("\r\n\n Power On Reset occurred....");
}
/* 检查是否是软件复位 */
else if (rcu_flag_get(RCU_FLAG_SWRST) != RESET)
{
printf("\r\n\n External Reset occurred....");
}
/* 检查复位是否是外部引脚复位 */
else if (rcu_flag_get(RCU_FLAG_EPRST) != RESET)
{
printf("\r\n\n External Reset Pin....");
}
/* 允许访问备份区域 */
//rcu_periph_clock_enable(RCU_PMU);
//pmu_backup_write_enable();
printf("\r\n No need to configure RTC....");
/* 等待寄存器与APB1时钟同步 */
rtc_register_sync_wait();
/* 使能秒中断 */
rtc_interrupt_enable(RTC_INT_SECOND);
/*等待配置完成 */
rtc_lwoff_wait();
}
#ifdef RTCCLOCKOUTPUT_ENABLE
/* enable PMU and BKPI clocks */
rcu_periph_clock_enable(RCU_BKPI);
rcu_periph_clock_enable(RCU_PMU);
/* 允许操作备份区*/
pmu_backup_write_enable();
/* 关闭侵入引脚 */
bkp_tamper_detection_disable();
/* 使能时钟输出 */
bkp_rtc_calibration_output_enable();
#endif
/* 清除所有标志 */
rcu_all_reset_flag_clear();
}
设置时间的函数
/*
设置时间
current_senconds:当前时间戳
*/
void time_adjust(uint32_t current_senconds)
{
rcu_periph_clock_enable(RCU_BKPI);
rcu_periph_clock_enable(RCU_PMU);
pmu_backup_write_enable();
/* 等待最后一次操作完成*/
rtc_lwoff_wait();
/* 改变当前时间 */
rtc_counter_set(current_senconds);
/*等待最后一次操作完成 */
rtc_lwoff_wait();
}
时间转换为时间戳
/*****时间日历转换时间戳*****/
const uint16_t month_days_table[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint32_t fml_time_to_stamp(date_time_t date)
{
static uint32_t dax = 0;
static uint32_t day_count = 0;
uint16_t leap_year_count = 0;
uint16_t i;
// 计算闰年数
for (i = 1970; i < date.year; i++)
{
if (fml_leap_year(i))
{
leap_year_count++;
}
}
// 计算年的总天数
day_count = leap_year_count * 366 + (date.year - 1970 - leap_year_count) * 365;
// 累加计算当年所有月的天数
for (i = 1; i < date.month; i++)
{
if ((2 == i) && (fml_leap_year(date.year)))
{
day_count += 29;
}
else
{
day_count += month_days_table[i];
}
}
// 累加计算当月的天数
day_count += (date.day - 1);
dax = (uint32_t)(day_count * 86400) + (uint32_t)((uint32_t)date.hour * 3600) + (uint32_t)((uint32_t)date.min * 60) + (uint32_t)date.second;
/* 北京时间补偿 */
dax = dax - 8*60*60;
return dax;
}
uint16_t fml_leap_year(uint16_t year)
{
return (((year % 4 == 0)&&(year % 100 != 0)) || (year % 400 == 0));
}
时间戳转换为时间
/*****************时间戳日历转换****************/
uint32_t fml_stamp_to_time( uint32_t timep, date_time_t *date)
{
uint32_t days = 0;
uint32_t rem = 0;
/* 北京时间补偿 */
timep = timep + 8*60*60;
// 计算天数
days = (uint32_t)(timep / 86400);
rem = (uint32_t)(timep % 86400);
// 计算年份
uint16_t year;
for (year = 1970; ; ++year)
{
uint16_t leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
uint16_t ydays = leap ? 366 : 365;
if (days < ydays)
{
break;
}
days -= ydays;
}
date->year = year;
// 计算月份
static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint16_t month;
for (month = 0; month < 12; month++)
{
uint16_t mdays = days_in_month[month];
if (month == 1 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
mdays = 29;
}
if (days < mdays)
{
break;
}
days -= mdays;
}
date->month = month;
date->month += 1;
// 计算日期
date->day = days + 1;
// 计算时间
date->hour = rem / 3600;
rem %= 3600;
date->min = rem / 60;
date->second = rem % 60;
return 0;
}
主函数
extern uint8_t time_interrupt_flag;
extern date_time_t date_time;
extern date_time_t setting_date_time;
void mian(void)
{
RTC_init();void
while(1)
{
/* 中断通知 */
if (time_interrupt_flag == 1)
{
/*获取rtc时间戳 */
int curent_s = rtc_counter_get();
printf("%d:%d:%d %d:%d:%d\r\n", date_time.year, date_time.month, date_time.day, date_time.hour, date_time.min, date_time.second);
fml_stamp_to_time( curent_s, &date_time);
time_interrupt_flag = 0;
}
// time_stamp = fml_time_to_stamp(date_time);
// printf ("time_stamp is %d",time_stamp);
vTaskDelay(1000);
}
}
修改当前的时间
//如果设置时间的话,修改setting_date_time结构体里面的值,再调用以下函数,把setting_date_time结构体里面的时间转换为时间戳
uint32_t seting_time_stamp = fml_time_to_stamp(setting_date_time); //得到时间戳
time_adjust(seting_time_stamp); //修改当前的时间戳
void time_adjust(uint32_t current_senconds)
{
rcu_periph_clock_enable(RCU_BKPI); //每次修改时间戳时,必须重新使能备份区域的时钟
rcu_periph_clock_enable(RCU_PMU); //每次修改时间戳时,必须重新使能电源管理时钟
pmu_backup_write_enable(); //每次修改时间戳时,必须使能备份区允许访问
/* 等待最后一次操作完成*/
rtc_lwoff_wait();
/* 改变当前时间 */
rtc_counter_set(current_senconds);
/*等待最后一次操作完成 */
rtc_lwoff_wait();
}
时间戳转换时间,时间转换为时间戳部分,转载:(34条消息) 通俗易懂----C语言时间日期与时间戳互相转化_骚气小飞猪的博客-CSDN博客