概述
本示例,只要介绍,RTC 如何实现 年/月/日 - 时/分/秒,又离成功进了一步 ^_^。
一、环境
1、硬件 (RTL8762DK + 128M Bits Falsh)
2、软件(keil IDE)
二、RTC的使用
1、创建examples_ble_rtc_clock工程,基于《examples_ble_led工程》来创建,创建步骤请参考第二篇文章。
2、新建rtc_clock文件夹,分别存放hub_clock.c与hub_clock.h这两个文件
2、打开examples_ble_rtc_clock工程
1)、hub_clock.h
#ifndef _WRISTBNAD_CLOCK_H_
#define _WRISTBNAD_CLOCK_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "board.h"
#define IsLeapYear(yr) (!((yr) % 400) || (((yr) % 100) && !((yr) % 4)))
#define YearLength(yr) (IsLeapYear(yr) ? 366 : 365)
#define TIME_VALUE_HANDLER(v, max_v) ((v + max_v) % (max_v))
#define TIME_HOUR_HANDLER(hour) TIME_VALUE_HANDLER(hour, 24)
#define TIME_MINUTES_HANDLER(minutes) TIME_VALUE_HANDLER(minutes, 60)
#define TIME_SECONDS_HANDLER(seconds) TIME_VALUE_HANDLER(seconds, 60)
#define BEGYEAR 2000 // UTC started at 00:00:00 January 1, 2000
#define DAY 86400UL // 24 hours * 60 minutes * 60 seconds
#define SYSTEM_ORIGIN_DAY_OF_WEEK (Sat) //2000-01-01 is sat
#define LENGTH_OF_WEEK (7)
#define RTC_CNT_MAX_VALUE (1024*1024*16UL -1) //RTC->CNT: [23:0]
#define RTC_PRESCALER_VALUE 0
extern uint32_t seconds_diff;
extern bool seconds_cali_enable;
typedef enum
{
MOn = 0,
Tues = 1,
Wed = 2,
Thur = 3,
Fri = 4,
Sat = 5,
Sun = 6
} DAY_OF_WEEK;
// To be used with
typedef struct
{
volatile uint16_t year; // 2000+
volatile uint8_t month; // 0-11
volatile uint8_t day; // 0-30
volatile uint8_t seconds; // 0-59
volatile uint8_t minutes; // 0-59
volatile uint8_t hour; // 0-23
volatile DAY_OF_WEEK week;
} T_UTC_TIME;
/* time bit field */
typedef struct
{
uint32_t seconds : 6;
uint32_t minute : 6;
uint32_t hours : 5;
uint32_t day : 5;
uint32_t month : 4;
uint32_t year : 6;
}
time_bit_field_type_t;
typedef union
{
uint32_t data;
time_bit_field_type_t time;
} time_union_t;
/* time bit field */
typedef struct
{
volatile uint16_t current_stationary_time;
volatile uint32_t SecondCountRTC;
volatile uint32_t pre_rtc_tick_count;
volatile T_UTC_TIME Global_Time;
} __attribute__((packed)) SystemClock_t;
extern SystemClock_t SysClock;
#define ABS_PARAMS(a,b) ((a>b) ? (a-b):(b-a))
typedef void (* pSystemClockCB)(void);
extern pSystemClockCB SystemClockCallBack;
void system_clock_init(uint32_t second);
void clock_driver_init(void);
DAY_OF_WEEK get_day_of_week(uint32_t secTime);//used to calculate day of week
void convert_to_utc_time(uint32_t sec_time);
uint32_t convert_time_to_second(time_union_t time);
void minute_system_clock_message_handle(void);
uint8_t get_system_clock_second(void);
uint16_t get_system_clock_millisecond(void);
time_union_t to_utc_time(uint32_t sec_time);
void set_sys_clock(time_union_t time);
bool get_system_work_state(void);
void clock_only_setting_date(uint16_t year, uint16_t month, uint16_t days);
void clock_only_setting_time(uint16_t hour, uint16_t minutes, uint16_t seconds);
uint32_t clock_get_timestamp(void);
#ifdef __cplusplus
}
#endif
#endif //_WRISTBNAD_CLOCK_H_
2)、hub_clock.c
/*********************************************************************************************************
* Copyright(c) 2015, Realtek Semiconductor Corporation. All rights reserved.
**********************************************************************************************************
* @file wristband_clock.c
* @brief
* @details
* @author
* @date 2017-10-31
* @version v0.1
*********************************************************************************************************
*/
#include "trace.h"
#include "string.h"
#include "ftl.h"
#include "app_task.h"
#include "rtl876x_rcc.h"
#include "rtl876x_rtc.h"
#include "rtl876x_nvic.h"
#include "hub_clock.h"
#include "otp.h"
#include "data_uart.h"
SystemClock_t SysClock __attribute__((aligned(4)));
#define ClOCK_RTC_COMPARATOR 0
#define RTC_CLOCK_SOURCE_FREQ (OTP->bw_rf_low_clk_frac ? 32000:32768)
static uint32_t rtcTime = 2; //1s interrupt
static void wall_clock_start(void);
static void update_system_clock(void);
static uint8_t month_length_calc(uint8_t lpyr, uint8_t mon);
pSystemClockCB SystemClockCallBack = NULL;
void clock_driver_init(void)
{
system_clock_init(0);
}
/**
* @brief system clock init
* @param init offset secod, sometimes is zero
* @return void
*/
void system_clock_init(uint32_t second)
{
RTC_DeInit();
RTC_SetPrescaler(RTC_PRESCALER_VALUE);
RTC_MaskINTConfig(RTC_INT_COMP0, ENABLE);
RTC_INTConfig(RTC_INT_COMP0, DISABLE);
/* Config RTC interrupt */
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
RTC_SystemWakeupConfig(ENABLE);
convert_to_utc_time(second);
/* Init ticks from RTC */
SysClock.SecondCountRTC = second; /* This should read from flash */
SysClock.pre_rtc_tick_count = 0;
wall_clock_start();
}
/**
* @brief RTC interrupt handler
* @param No parameter.
* @return void
*/
void RTC_Handler(void)
{
uint32_t CompareValue = 0;
data_uart_print("RTC->INT_SR = 0x%x, RTC->CR0 = 0x%x \r\n", RTC->INT_SR, RTC->CR0);
if (RTC_GetINTStatus(RTC_INT_COMP0) == SET) {
// HUB_CLOCK_LOG("RTC_INT_CMP0");
update_system_clock();
CompareValue = RTC_GetCompValue(ClOCK_RTC_COMPARATOR) + (RTC_CLOCK_SOURCE_FREQ / (RTC_PRESCALER_VALUE + 1)) * rtcTime;
RTC_SetCompValue(ClOCK_RTC_COMPARATOR, CompareValue & 0xFFFFFF);
RTC_ClearCompINT(ClOCK_RTC_COMPARATOR);
}
if (RTC_GetINTStatus(RTC_INT_OVF) == SET) {
// HUB_CLOCK_LOG("RTC Overflow");
RTC_ClearOverFlowINT();
}
}
/**
* @brief minute system clock messafe handle
* @param void
* @return void
*/
void minute_system_clock_message_handle(void)
{
/* get UTCTime time */
convert_to_utc_time(SysClock.SecondCountRTC);
#if 1
data_uart_print("year:%d,month:%d,day:%d,hour:%d,minute:%d,second:%d\r\n",
SysClock.Global_Time.year,
SysClock.Global_Time.month, SysClock.Global_Time.day,
SysClock.Global_Time.hour, SysClock.Global_Time.minutes,
SysClock.Global_Time.seconds);
#endif
if (SystemClockCallBack != NULL) {
SystemClockCallBack();
}
if ((SysClock.SecondCountRTC / 60) % 60 == 0) {
}
if (SysClock.Global_Time.year > 2000 && ((SysClock.Global_Time.hour >= 18) || (SysClock.Global_Time.hour < 10))) {
} else {
}
if (SysClock.Global_Time.year > 2000 && SysClock.Global_Time.hour == 19 && SysClock.Global_Time.minutes == 0) {
}
if (SysClock.Global_Time.year > 2000 && SysClock.Global_Time.hour == 7 && SysClock.Global_Time.minutes == 0) {
}
}
static uint8_t month_length_calc(uint8_t lpyr, uint8_t mon)
{
uint8_t days = 31;
if (mon == 1) {
days = (28 + lpyr); // feb
} else {
// aug-dec
if (mon > 6) {
mon--;
}
if (mon & 1) {
days = 30;
}
}
return (days);
}
void convert_to_utc_time(uint32_t sec_time)
{
/* calculate the time less than a day - hours, minutes, seconds */
{
uint32_t day = sec_time % DAY;
SysClock.Global_Time.hour = day % 60UL;
SysClock.Global_Time.minutes = day / 3600UL;
SysClock.Global_Time.seconds = (day % 3600UL) / 60UL;
}
/* Fill in the calendar - day, month, year */
{
uint16_t numDays = sec_time / DAY;
SysClock.Global_Time.year = BEGYEAR;
while (numDays >= YearLength(SysClock.Global_Time.year)) {
numDays -= YearLength(SysClock.Global_Time.year);
SysClock.Global_Time.year++;
}
SysClock.Global_Time.month = 0;
while (numDays >= month_length_calc(IsLeapYear(SysClock.Global_Time.year), SysClock.Global_Time.month)) {
numDays -= month_length_calc(IsLeapYear(SysClock.Global_Time.year), SysClock.Global_Time.month);
SysClock.Global_Time.month++;
}
SysClock.Global_Time.day = numDays;
SysClock.Global_Time.month = SysClock.Global_Time.month + 1;
SysClock.Global_Time.day = SysClock.Global_Time.day + 1;
}
SysClock.Global_Time.week = get_day_of_week(SysClock.SecondCountRTC);
}
time_union_t to_utc_time(uint32_t sec_time)
{
time_union_t union_time = {0};
/* calculate the time less than a day - hours, minutes, seconds */
{
uint32_t day = sec_time % DAY;
union_time.time.seconds = day % 60UL;
union_time.time.minute = (day % 3600UL) / 60UL;
union_time.time.hours = day / 3600UL;
}
/* Fill in the calendar - day, month, year */
{
uint16_t numDays = sec_time / DAY;
union_time.time.year = 0;
while (numDays >= YearLength(union_time.time.year)) {
numDays -= YearLength(union_time.time.year);
union_time.time.year++;
}
union_time.time.month = 0;
while (numDays >= month_length_calc(IsLeapYear(union_time.time.year), union_time.time.month)) {
numDays -= month_length_calc(IsLeapYear(union_time.time.year), union_time.time.month);
union_time.time.month++;
}
union_time.time.day = numDays;
union_time.time.month = union_time.time.month + 1;
union_time.time.day = union_time.time.day + 1;
}
return union_time;
}
static void update_system_clock(void)
{
data_uart_print("update_system_clock \r\n");
SysClock.SecondCountRTC = SysClock.SecondCountRTC + (60 - SysClock.SecondCountRTC % 60);
SysClock.pre_rtc_tick_count = RTC_GetCounter();
T_IO_MSG clock_msg;
clock_msg.type = IO_MSG_TYPE_CLOCK;
app_send_msg_to_apptask(&clock_msg);
}
static void wall_clock_start(void)
{
uint32_t CompareValue;
data_uart_print("\r\n clock_start second_diff_value: %d\r\n", rtcTime);
CompareValue = RTC_GetCounter() + (RTC_CLOCK_SOURCE_FREQ / (RTC_PRESCALER_VALUE + 1)) * rtcTime;
data_uart_print("CompareValue: %d\r\n", CompareValue);
RTC_SetComp(ClOCK_RTC_COMPARATOR, CompareValue & 0xFFFFFF);
RTC_MaskINTConfig(RTC_INT_COMP0, DISABLE);
RTC_INTConfig(RTC_INT_COMP0, ENABLE);
RTC_NvCmd(ENABLE);
RTC_Cmd(ENABLE);
}
uint32_t convert_time_to_second(time_union_t time)
{
uint32_t i = 0;
uint32_t offset = 0;
/* day time */
offset += time.time.seconds;
offset += time.time.minute * 60;
offset += time.time.hours * 60 * 60;
uint8_t leapYear = IsLeapYear(time.time.year + 2000);
offset += DAY * (time.time.day - 1);
for (i = 0; i < time.time.month - 1; ++i) {
/* month start from 1 */
offset += month_length_calc(leapYear, i) * DAY;
}
for (i = 0; i < time.time.year ; ++i) {
if (IsLeapYear(i + 2000)) {
offset += DAY * 366;
} else {
offset += DAY * 365;
}
}
return offset;
}
void set_sys_clock(time_union_t time)
{
SysC