370 lines
8.9 KiB
C
370 lines
8.9 KiB
C
/*
|
|
* Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2022-04-28 CDT first version
|
|
* 2022-05-31 CDT delete this file
|
|
* 2022-06-10 xiaoxiaolisunny re-add this file for F460
|
|
* 2023-02-14 CDT add alarm(precision is 1 minute)
|
|
*/
|
|
|
|
#include <board.h>
|
|
#include <sys/time.h>
|
|
#include "board_config.h"
|
|
|
|
#if defined(BSP_USING_RTC)
|
|
|
|
//#define DRV_DEBUG
|
|
#define LOG_TAG "drv.rtc"
|
|
#include <drv_log.h>
|
|
|
|
#if defined(HC32F4A0)
|
|
/* BACKUP REG: 96~127 for RTC used */
|
|
#define RTC_BACKUP_DATA_SIZE (32U)
|
|
#define RTC_BACKUP_REG_OFFSET (128U - RTC_BACKUP_DATA_SIZE)
|
|
|
|
static const uint8_t m_au8BackupWriteData[RTC_BACKUP_DATA_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
|
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
|
31
|
|
};
|
|
static uint8_t m_au8BackupReadData[RTC_BACKUP_DATA_SIZE];
|
|
#endif
|
|
|
|
static rt_rtc_dev_t rtc_dev;
|
|
|
|
#ifdef RT_USING_ALARM
|
|
struct stc_hc32_alarm_irq
|
|
{
|
|
struct hc32_irq_config irq_config;
|
|
func_ptr_t irq_callback;
|
|
};
|
|
|
|
static void _rtc_alarm_irq_handler(void);
|
|
|
|
#define RTC_ALARM_IRQ_CONFIG \
|
|
{ \
|
|
.irq_num = BSP_RTC_ALARM_IRQ_NUM, \
|
|
.irq_prio = BSP_RTC_ALARM_IRQ_PRIO, \
|
|
.int_src = INT_SRC_RTC_ALM, \
|
|
}
|
|
|
|
static struct stc_hc32_alarm_irq hc32_alarm_irq =
|
|
{
|
|
.irq_config = RTC_ALARM_IRQ_CONFIG,
|
|
.irq_callback = _rtc_alarm_irq_handler,
|
|
};
|
|
#endif
|
|
|
|
#if defined(HC32F4A0)
|
|
static void _bakup_reg_write(void)
|
|
{
|
|
uint8_t u8Num;
|
|
for (u8Num = 0U; u8Num < RTC_BACKUP_DATA_SIZE; u8Num++)
|
|
{
|
|
PWC_BKR_Write(u8Num + RTC_BACKUP_REG_OFFSET, m_au8BackupWriteData[u8Num]);
|
|
}
|
|
}
|
|
|
|
static int32_t _bakup_reg_check(void)
|
|
{
|
|
uint8_t u8Num;
|
|
int32_t i32Ret = LL_OK;
|
|
|
|
for (u8Num = 0U; u8Num < RTC_BACKUP_DATA_SIZE; u8Num++)
|
|
{
|
|
m_au8BackupReadData[u8Num] = PWC_BKR_Read(u8Num + RTC_BACKUP_REG_OFFSET);
|
|
}
|
|
|
|
for (u8Num = 0U; u8Num < RTC_BACKUP_DATA_SIZE; u8Num++)
|
|
{
|
|
if (m_au8BackupWriteData[u8Num] != m_au8BackupReadData[u8Num])
|
|
{
|
|
i32Ret = LL_ERR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i32Ret;
|
|
}
|
|
|
|
static int32_t _hc32_rtc_rw_check(void)
|
|
{
|
|
int32_t i32Ret = LL_ERR;
|
|
|
|
/* Enter read/write mode */
|
|
if (LL_OK == RTC_EnterRwMode())
|
|
{
|
|
/* Exit read/write mode */
|
|
if (LL_OK == RTC_ExitRwMode())
|
|
{
|
|
i32Ret = LL_OK;
|
|
}
|
|
}
|
|
|
|
return i32Ret;
|
|
}
|
|
#endif
|
|
|
|
static rt_err_t _rtc_get_timeval(struct timeval *tv)
|
|
{
|
|
|
|
stc_rtc_time_t stcRtcTime = {0};
|
|
stc_rtc_date_t stcRtcDate = {0};
|
|
struct tm tm_new = {0};
|
|
|
|
if (LL_OK != RTC_GetTime(RTC_DATA_FMT_DEC, &stcRtcTime))
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
if (LL_OK != RTC_GetDate(RTC_DATA_FMT_DEC, &stcRtcDate))
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
tm_new.tm_sec = stcRtcTime.u8Second;
|
|
tm_new.tm_min = stcRtcTime.u8Minute;
|
|
tm_new.tm_hour = stcRtcTime.u8Hour;
|
|
tm_new.tm_mday = stcRtcDate.u8Day;
|
|
tm_new.tm_mon = stcRtcDate.u8Month - 1;
|
|
tm_new.tm_year = stcRtcDate.u8Year + 100;
|
|
|
|
tv->tv_sec = timegm(&tm_new);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t hc32_rtc_set_time_stamp(time_t time_stamp)
|
|
{
|
|
stc_rtc_time_t stcRtcTime = {0};
|
|
stc_rtc_date_t stcRtcDate = {0};
|
|
struct tm tm_set = {0};
|
|
|
|
gmtime_r(&time_stamp, &tm_set);
|
|
|
|
if (tm_set.tm_year < 100)
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
stcRtcTime.u8Second = tm_set.tm_sec ;
|
|
stcRtcTime.u8Minute = tm_set.tm_min ;
|
|
stcRtcTime.u8Hour = tm_set.tm_hour;
|
|
stcRtcDate.u8Day = tm_set.tm_mday;
|
|
stcRtcDate.u8Month = tm_set.tm_mon + 1;
|
|
stcRtcDate.u8Year = tm_set.tm_year - 100;
|
|
stcRtcDate.u8Weekday = tm_set.tm_wday;
|
|
|
|
if (LL_OK != RTC_SetTime(RTC_DATA_FMT_DEC, &stcRtcTime))
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
if (LL_OK != RTC_SetDate(RTC_DATA_FMT_DEC, &stcRtcDate))
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
LOG_D("set rtc time.");
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_init(void)
|
|
{
|
|
stc_rtc_init_t stcRtcInit;
|
|
|
|
#if defined(HC32F4A0)
|
|
if ((LL_OK != _bakup_reg_check()) || (LL_OK != _hc32_rtc_rw_check()))
|
|
#elif defined(HC32F460)
|
|
if (DISABLE == RTC_GetCounterState())
|
|
#endif
|
|
{
|
|
/* Reset RTC counter */
|
|
if (LL_ERR_TIMEOUT == RTC_DeInit())
|
|
{
|
|
LOG_E("Reset RTC failed!");
|
|
return -RT_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/* Stop RTC */
|
|
RTC_Cmd(DISABLE);
|
|
/* Configure structure initialization */
|
|
(void)RTC_StructInit(&stcRtcInit);
|
|
|
|
/* Configuration RTC structure */
|
|
#ifdef BSP_RTC_USING_XTAL32
|
|
stcRtcInit.u8ClockSrc = RTC_CLK_SRC_XTAL32;
|
|
#else
|
|
stcRtcInit.u8ClockSrc = RTC_CLK_SRC_LRC;
|
|
#endif
|
|
stcRtcInit.u8HourFormat = RTC_HOUR_FMT_24H;
|
|
(void)RTC_Init(&stcRtcInit);
|
|
|
|
/* Clear all status */
|
|
RTC_ClearStatus(RTC_FLAG_CLR_ALL);
|
|
/* Startup RTC count */
|
|
RTC_Cmd(ENABLE);
|
|
|
|
#if defined(HC32F4A0)
|
|
/* Write sequence flag to backup register */
|
|
_bakup_reg_write();
|
|
#endif
|
|
LOG_D("rtc init success");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_D("rtc does not need to init");
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_get_secs(time_t *sec)
|
|
{
|
|
struct timeval tv;
|
|
|
|
_rtc_get_timeval(&tv);
|
|
*(time_t *) sec = tv.tv_sec;
|
|
LOG_D("RTC: get rtc_time %d", *sec);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t _rtc_set_secs(time_t *sec)
|
|
{
|
|
rt_err_t result = RT_EOK;
|
|
|
|
if (hc32_rtc_set_time_stamp(*sec))
|
|
{
|
|
result = -RT_ERROR;
|
|
}
|
|
LOG_D("RTC: set rtc_time %d", *sec);
|
|
#ifdef RT_USING_ALARM
|
|
rt_alarm_update(&rtc_dev.parent, 1);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
#ifdef RT_USING_ALARM
|
|
|
|
static void _rtc_alarm_irq_handler(void)
|
|
{
|
|
rt_interrupt_enter();
|
|
RTC_ClearStatus(RTC_FLAG_ALARM);
|
|
rt_alarm_update(&rtc_dev.parent, 1);
|
|
rt_interrupt_leave();
|
|
}
|
|
|
|
static void hc32_rtc_alarm_enable(void)
|
|
{
|
|
NVIC_EnableIRQ(hc32_alarm_irq.irq_config.irq_num);
|
|
|
|
RTC_IntCmd(RTC_INT_ALARM, ENABLE);
|
|
RTC_AlarmCmd(ENABLE);
|
|
LOG_D("hc32 alarm enable");
|
|
}
|
|
|
|
static void hc32_rtc_alarm_disable(void)
|
|
{
|
|
RTC_AlarmCmd(DISABLE);
|
|
RTC_IntCmd(RTC_INT_ALARM, DISABLE);
|
|
|
|
NVIC_DisableIRQ(hc32_alarm_irq.irq_config.irq_num);
|
|
LOG_D("hc32 alarm disable");
|
|
}
|
|
#endif
|
|
|
|
static rt_err_t _rtc_get_alarm(struct rt_rtc_wkalarm *alarm)
|
|
{
|
|
#ifdef RT_USING_ALARM
|
|
stc_rtc_alarm_t stcRtcAlarm;
|
|
RTC_GetAlarm(RTC_DATA_FMT_DEC, &stcRtcAlarm);
|
|
alarm->tm_hour = stcRtcAlarm.u8AlarmHour;
|
|
alarm->tm_min = stcRtcAlarm.u8AlarmMinute;
|
|
alarm->tm_sec = 0; /* alarms precision is 1 minute */
|
|
|
|
LOG_D("GET_ALARM %d:%d:%d", alarm->tm_hour, alarm->tm_min, alarm->tm_sec);
|
|
return RT_EOK;
|
|
#else
|
|
return -RT_ERROR;
|
|
#endif
|
|
}
|
|
|
|
static rt_err_t _rtc_set_alarm(struct rt_rtc_wkalarm *alarm)
|
|
{
|
|
#ifdef RT_USING_ALARM
|
|
stc_rtc_alarm_t stcRtcAlarm;
|
|
|
|
LOG_D("RT_DEVICE_CTRL_RTC_SET_ALARM");
|
|
if (alarm != RT_NULL)
|
|
{
|
|
if (alarm->enable)
|
|
{
|
|
RTC_AlarmCmd(DISABLE);
|
|
/* Configuration alarm time: precision is 1 minute */
|
|
stcRtcAlarm.u8AlarmHour = alarm->tm_hour;
|
|
stcRtcAlarm.u8AlarmMinute = alarm->tm_min;
|
|
stcRtcAlarm.u8AlarmWeekday = RTC_ALARM_WEEKDAY_EVERYDAY;
|
|
stcRtcAlarm.u8AlarmAmPm = RTC_HOUR_24H;
|
|
RTC_ClearStatus(RTC_FLAG_ALARM);
|
|
(void)RTC_SetAlarm(RTC_DATA_FMT_DEC, &stcRtcAlarm);
|
|
hc32_rtc_alarm_enable();
|
|
LOG_D("SET_ALARM %d:%d:%d", alarm->tm_hour,
|
|
alarm->tm_min, 0);
|
|
}
|
|
else
|
|
{
|
|
hc32_rtc_alarm_disable();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
LOG_E("RT_DEVICE_CTRL_RTC_SET_ALARM error!!");
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
return RT_EOK;
|
|
#else
|
|
return -RT_ERROR;
|
|
#endif
|
|
}
|
|
|
|
const static struct rt_rtc_ops _ops =
|
|
{
|
|
_rtc_init,
|
|
_rtc_get_secs,
|
|
_rtc_set_secs,
|
|
_rtc_get_alarm,
|
|
_rtc_set_alarm,
|
|
_rtc_get_timeval,
|
|
RT_NULL
|
|
};
|
|
|
|
int rt_hw_rtc_init(void)
|
|
{
|
|
rt_err_t result;
|
|
|
|
#ifdef RT_USING_ALARM
|
|
/* register interrupt */
|
|
hc32_install_irq_handler(&hc32_alarm_irq.irq_config, hc32_alarm_irq.irq_callback, RT_FALSE);
|
|
#endif
|
|
|
|
rtc_dev.ops = &_ops;
|
|
result = rt_hw_rtc_register(&rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
|
|
if (result != RT_EOK)
|
|
{
|
|
LOG_E("rtc register err code: %d", result);
|
|
return result;
|
|
}
|
|
LOG_D("rtc register done");
|
|
return RT_EOK;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_rtc_init);
|
|
|
|
#endif /* BSP_USING_RTC */
|