mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #15567 from iosabi/qn908x_rtc
cpu/qn908x: Add the RTC module.
This commit is contained in:
commit
e3558a4482
@ -13,6 +13,7 @@ config CPU_FAM_QN908X
|
||||
select HAS_PERIPH_CPUID
|
||||
select HAS_PERIPH_GPIO
|
||||
select HAS_PERIPH_GPIO_IRQ
|
||||
select HAS_PERIPH_RTC
|
||||
select HAS_PERIPH_WDT
|
||||
select HAS_PERIPH_WDT_CB
|
||||
|
||||
|
@ -4,6 +4,7 @@ CPU_FAM = qn908x
|
||||
FEATURES_PROVIDED += cortexm_mpu
|
||||
FEATURES_PROVIDED += periph_cpuid
|
||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||
FEATURES_PROVIDED += periph_rtc
|
||||
FEATURES_PROVIDED += periph_wdt periph_wdt_cb
|
||||
|
||||
include $(RIOTCPU)/cortexm_common/Makefile.features
|
||||
|
@ -50,6 +50,27 @@ SCT blocks between pwm and timer functions.
|
||||
#define TIMER_NUMOF 4
|
||||
|
||||
|
||||
@defgroup cpu_qn908x_rtc NXP QN908x Real-Time-Clock (RTC)
|
||||
@ingroup cpu_qn908x
|
||||
@brief NXP QN908x RTC driver
|
||||
|
||||
The RTC block in the QN908x can be driven by the external 32.768 kHz crystal or
|
||||
by the internal 32 kHz RCO oscillator clock, whichever is selected as the
|
||||
`CLK_32K` clock source. The RTC has an internal "second counter" calibrated
|
||||
depending on the frequency of the clock source selected at the time the RTC
|
||||
clock is initialized by calling @ref rtc_init.
|
||||
|
||||
The RTC function in this cpu doesn't have a match against a target value to
|
||||
generate an interrupt like the timer peripheral, instead, the alarm function in
|
||||
the rtc.h interface is implemented by an interrupt generated every second which
|
||||
checks the target value in software. Keep in mind that while the RTC can operate
|
||||
while the cpu is the power-down 0 mode, using the alarm functionality during
|
||||
that time means that the cpu will wake up every second for a brief moment,
|
||||
potentially impacting the power consumption.
|
||||
|
||||
No RTC-specific configuration is necessary.
|
||||
|
||||
|
||||
@defgroup cpu_qn908x_uart NXP QN908x UART
|
||||
@ingroup cpu_qn908x
|
||||
@brief NXP QN908x UART driver
|
||||
|
178
cpu/qn908x/periph/rtc.c
Normal file
178
cpu/qn908x/periph/rtc.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2020 iosabi
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_qn908x
|
||||
* @ingroup drivers_periph_rtc
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level Real-Time Clock (RTC) driver implementation
|
||||
*
|
||||
* @author iosabi <iosabi@protonmail.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "board.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/rtc.h"
|
||||
|
||||
#include "vendor/drivers/fsl_clock.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/* Callback context. */
|
||||
typedef struct {
|
||||
rtc_alarm_cb_t cb;
|
||||
void *arg;
|
||||
uint32_t alarm;
|
||||
} rtc_ctx_t;
|
||||
|
||||
static rtc_ctx_t rtc_ctx = { NULL, NULL, 0 };
|
||||
|
||||
void rtc_init(void)
|
||||
{
|
||||
DEBUG("rtc_init(), rtc=%" PRIu32 "\n", RTC->SEC);
|
||||
/* The RTC is really meant to be using the RCO32K at 32KHz. However, if the
|
||||
* 32K clock source is set to the external XTAL at 32.768 KHz we can use a
|
||||
* calibration parameter to correct for this:
|
||||
* ppm = (32000 / 32768 - 1) * (1 << 20) = -24576
|
||||
*/
|
||||
#if CONFIG_CPU_CLK_32K_RCO
|
||||
/* 32000 Hz, no need to use the calibration. */
|
||||
RTC->CTRL &= ~RTC_CTRL_CAL_EN_MASK;
|
||||
#elif CONFIG_CPU_CLK_32K_XTAL
|
||||
/* 32768 Hz, use -24576 ppm correction. */
|
||||
/* Positive ppm values would have the RTC_CAL_DIR_MASK set, but this is
|
||||
* negative. */
|
||||
RTC->CAL = RTC_CAL_PPM(24576);
|
||||
RTC->CTRL |= RTC_CTRL_CAL_EN_MASK;
|
||||
#else
|
||||
#error "One of the CONFIG_CPU_CLK_32K_* must be set."
|
||||
#endif
|
||||
/* RTC clock (BIV) starts enabled after reset anyway. */
|
||||
CLOCK_EnableClock(kCLOCK_Biv);
|
||||
|
||||
RTC->CTRL &= ~RTC_CTRL_SEC_INT_EN_MASK;
|
||||
|
||||
/* We only use the RTC_SEC_IRQ which triggers every second if enabled by the
|
||||
* alarm. */
|
||||
NVIC_EnableIRQ(RTC_SEC_IRQn);
|
||||
}
|
||||
|
||||
int rtc_set_time(struct tm *time)
|
||||
{
|
||||
uint32_t ts = rtc_mktime(time);
|
||||
|
||||
DEBUG("rtc_set_time(%" PRIu32 ")\n", ts);
|
||||
/* Writing 1 to CFG in CTRL register sets the CNT 0 timer to 0, resetting
|
||||
* the fractional part of the second, meaning that "SEC" is a round number
|
||||
* of second when this instruction executes. */
|
||||
RTC->CTRL |= RTC_CTRL_CFG_MASK;
|
||||
RTC->SEC = ts;
|
||||
while (RTC->STATUS &
|
||||
(RTC_STATUS_SEC_SYNC_MASK | RTC_STATUS_CTRL_SYNC_MASK)) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtc_get_time(struct tm *time)
|
||||
{
|
||||
uint32_t ts = RTC->SEC;
|
||||
|
||||
DEBUG("rtc_get_time() -> %" PRIu32 "\n", ts);
|
||||
rtc_localtime(ts, time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
|
||||
{
|
||||
uint32_t ts = rtc_mktime(time);
|
||||
|
||||
DEBUG("rtc_set_alarm(%" PRIu32 ", %p, %p)\n", ts, cb, arg);
|
||||
|
||||
if (ts <= RTC->SEC) {
|
||||
/* The requested time is in the past at the time of executing this
|
||||
* instruction, so we return invalid time. */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* If the requested time arrives (SEC_INT should have fired) before we get
|
||||
* to set the RTC_CTRL_SEC_INT_EN_MASK mask a few instruction below, the
|
||||
* alarm will be 1 second late. */
|
||||
rtc_ctx.cb = cb;
|
||||
rtc_ctx.arg = arg;
|
||||
rtc_ctx.alarm = ts;
|
||||
|
||||
RTC->CTRL |= RTC_CTRL_SEC_INT_EN_MASK;
|
||||
/* Wait until the CTRL_SEC is synced. */
|
||||
while (RTC->STATUS & RTC_STATUS_CTRL_SYNC_MASK) {}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtc_get_alarm(struct tm *time)
|
||||
{
|
||||
DEBUG("rtc_clear_alarm() -> %" PRIu32 "\n", rtc_ctx.alarm);
|
||||
rtc_localtime(rtc_ctx.alarm, time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtc_clear_alarm(void)
|
||||
{
|
||||
DEBUG("rtc_clear_alarm()\n");
|
||||
/* Disable the alarm flag before clearing out the callback. */
|
||||
RTC->CTRL &= ~RTC_CTRL_SEC_INT_EN_MASK;
|
||||
rtc_ctx.cb = NULL;
|
||||
rtc_ctx.alarm = 0;
|
||||
}
|
||||
|
||||
void rtc_poweron(void)
|
||||
{
|
||||
/* We don't power on/off the RTC since it is shared with the timer module.
|
||||
* Besides the CLK_32K clock source for every peripheral that might use it,
|
||||
* there isn't much to turn off here. */
|
||||
}
|
||||
|
||||
void rtc_poweroff(void)
|
||||
{
|
||||
/* TODO: Coordinate with the RTT module to turn off the RTC clock when
|
||||
* neither one is in use. */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Interrupt service declared in vectors_qn908x.h
|
||||
*
|
||||
* We can only generate an interrupt every second, so we check the alarm value
|
||||
* every time. For a hardware based comparison use the timer instead.
|
||||
*/
|
||||
void isr_rtc_sec(void)
|
||||
{
|
||||
if (RTC_STATUS_SEC_INT_MASK & RTC->STATUS) {
|
||||
DEBUG("isr_rtc_sec at %" PRIu32 "\n", RTC->SEC);
|
||||
/* Write 1 to clear the STATUS flag. */
|
||||
RTC->STATUS = RTC_STATUS_SEC_INT_MASK;
|
||||
if (rtc_ctx.cb != NULL && rtc_ctx.alarm <= RTC->SEC) {
|
||||
rtc_alarm_cb_t cb = rtc_ctx.cb;
|
||||
rtc_ctx.cb = NULL;
|
||||
/* Disable the interrupt. The cb may call rtc_set_alarm() again,
|
||||
* but otherwise we don't need the interrupt anymore. */
|
||||
RTC->CTRL &= ~RTC_CTRL_SEC_INT_EN_MASK;
|
||||
|
||||
cb(rtc_ctx.arg);
|
||||
}
|
||||
}
|
||||
cortexm_isr_end();
|
||||
}
|
Loading…
Reference in New Issue
Block a user