cpu/esp32: add RTT counter implementation

fixup! cpu/esp32: add RTT counter implementation
This commit is contained in:
Gunar Schorcht 2020-04-03 01:16:55 +02:00
parent 6cd9896ac0
commit f67cb48f6d
8 changed files with 867 additions and 31 deletions

View File

@ -95,6 +95,11 @@ ifneq (,$(filter pm_layered,$(USEMODULE)))
USEMODULE += periph_rtc
endif
ifneq (,$(filter periph_rtt,$(USEMODULE)))
USEMODULE += periph_rtt_hw_sys
USEMODULE += periph_rtt_hw_rtc
endif
ifneq (,$(filter shell,$(USEMODULE)))
USEMODULE += ps
endif

View File

@ -6,3 +6,4 @@ FEATURES_PROVIDED += arch_esp32
FEATURES_PROVIDED += esp_wifi_enterprise
FEATURES_PROVIDED += periph_adc_ctrl
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_rtt

View File

@ -38,7 +38,7 @@
5. [PWM Channels](#esp32_pwm_channels)
6. [SPI Interfaces](#esp32_spi_interfaces)
7. [Timers](#esp32_timers)
8. [RTC Timer](#esp32_rtc_timer)
8. [RTT Implementation](#esp32_rtt_counter)
9. [UART Interfaces](#esp32_uart_interfaces)
10. [CAN Interfaces](#esp32_can_interfaces)
11. [Power Management](#esp32_power_management)
@ -120,8 +120,7 @@ Module | Default | Short description
[esp_log_startup](#esp32_esp_log_module) | not used | enable additional startup information
[esp_log_tagged](#esp32_esp_log_module) | not used | add additional information to the log output
[esp_now](#esp32_esp_now_network_interface) | not used | enable the ESP-NOW network device
[esp_rtc_timer](#esp32_rtc_timer) | not used | enable RTC hardware timer with internal 150 kHz RC oscillator
[esp_rtc_timer_32k](#esp32_rtc_timer) | not used | enable RTC hardware timer with external 32.768 kHz crystal.
[esp_rtc_timer_32k](#esp32_rtt_counter) | not used | use RTC timer with external 32.768 kHz crystal as RTT
[esp_spi_ram](#esp32_spi_ram) | not used | enable SPI RAM
[esp_spiffs](#esp32_spiffs_device) | not used | enable SPIFFS for on-board flash memory
[esp_wifi](#esp32_wifi_network_interface) | not used | enable the Wifi network device in WPA2 personal mode
@ -422,7 +421,6 @@ esp_log_colored | Enable colored log output, see section [Log output](#esp32_esp
esp_log_startup | Enable additional startup information, see section [Log output](#esp32_esp_log_module).
esp_log_tagged | Add additional information to the log output, see section [Log output](#esp32_esp_log_module).
esp_now | Enable the built-in WiFi module with the ESP-NOW protocol as `netdev` network device, see section [ESP-NOW Network Interface](#esp32_esp_now_network_interface).
esp_rtc_timer | Enable RTC hardware timer with internal 150 kHz RC oscillator.
esp_rtc_timer_32k | Enable RTC hardware timer with external 32.768 kHz crystal.
esp_spiffs | Enable the optional SPIFFS drive in on-board flash memory, see section [SPIFFS Device](#esp32_spiffs_device).
esp_spi_ram | Enable the optional SPI RAM, see section [SPI RAM Modules](#esp32_spi_ram).
@ -839,37 +837,31 @@ to application's makefile.
Timers are MCU built-in features and not board-specific. There is nothing to be configured.
\anchor esp32_rtc_timer
## <a name="esp32_rtc_timer"> RTC Timer </a> &nbsp;[[TOC](#esp32_toc)]
\anchor esp32_rtt_counter
## <a name="esp32_rtt_counter"> RTT implementation </a> &nbsp;[[TOC](#esp32_toc)]
The RTC hardware timer of the ESP32 can be clocked with either an external
32.768 kHz crystal or the internal adjustable 150 kHz RC oscillator. If the
the external 32.768 kHz crystal is not available, the internal 150 kHz RC
oscillator is used automatically. However, since this internal 150 kHz RC
oscillator is not very accurate, the RTC low-level driver uses by default
the PLL-controlled 64-bit microsecond system timer to emulate the RTC timer.
The RTT peripheral low-level driver provides a RTT (Real Time Timer) with
a frequency of 32.768 kHz. It either uses the RTC hardware timer if an
external 32.768 kHz crystal is connected to the ESP32 or the PLL-controlled
64-bit microsecond system timer to emulate the RTC timer.
To allow the use of the RTC hardware timer for boards with an external
32 kHz crystal, the pseudomodules `esp_rtc_timer` and `esp_rtc_timer_32k`
can be used to control which timer is used by the RTC low-level driver
as following:
Whether an external 32.768 kHz crystal is connected to the ESP32 is
specified as a feature by the board definition using the pseudomodule
`esp_rtc_timer_32k`. If the feature `esp_rtc_timer_32k` is defined but the
external 32.768 kHz crystal is not recognized during startup, the
PLL controlled 64 bit microsecond system timer is used to emulate the
RTC timer.
- **esp_rtc_timer**:
Use always the RTC hardware timer with the **internal 150 kHz RC** oscillator.
The RTT is retained during light and deep sleep as well as during a restart.
The RTC hardware timer is used for this purpose, regardless of whether an
external 32.768 kHz crystal is connected to the ESP32 or the internal 150 kHz
RC oscillator is used. All current timer values are saved in the RTC memory
before entering a sleep mode or restart and are restored after when waking up
or restarting.
- **esp_rtc_timer_32k**:
Use the RTC hardware timer with the **external 32.768 kHz crystal**. If the
external 32.768 kHz crystal is not available, the RTC hardware timer
is used with the internal 150 kHz RC oscillator.
If none of the modules above is enabled, the PLL-driven **emulated RTC timer**
is used. In this case, the RTC hardware timer with the internal RC 150 kHz
oscillator is only used in deep sleep mode and during a reset.
@note The accuracy of the emulated RTC timer is better than the accuracy of the
RTC hardware timer with the internal 150 kHz RC oscillator. If you have not
connected an external 32.768 kHz crystal, you should use the default
configuration.
@note The RTT implementation is also used to implement a RTC (Real Time Clock)
peripheral. For this purpose the module `rt_rtc` is automatically enabled
when the feature `periph_rtc` is used.
\anchor esp32_uart_interfaces
## <a name="esp32_uart_interfaces"> UART Interfaces </a> &nbsp;[[TOC](#esp32_toc)]

View File

@ -409,6 +409,27 @@ typedef struct {
#define RNG_DATA_REG_ADDR (0x3ff75144)
/** @} */
/**
* @name RTT and RTC configuration
* @{
*/
/**
* @brief RTT frequency definition
*
* The RTT frequency is always 32.768 kHz even if no external crystal is
* connected. In this case the RTT value counted with the internal 150 kHz
* RC oscillator is converted to a value for an RTT with 32.768 kHz.
*/
#define RTT_FREQUENCY (32768UL)
/**
* @brief RTT is a 32-bit counter
*/
#define RTT_MAX_VALUE (0xFFFFFFFFUL)
/** @} */
/**
* @name SPI configuration
*

View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2020 Gunar Schorcht
*
* 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_esp32
* @{
*
* @file
* @brief Architecture specific RTT functions for ESP32
*
* The RTT peripheral driver implements a 32-bit RTT counter with a frequency
* of 32.768 kHz. It uses either
*
* - the 48-bit RTC counter if an external 32.678 kHz crystal is connected or
* - the 64-bit microsecond system timer.
*
* For this purpose, a hardware abstraction layer is defined by a driver
* interface of the type rtt_hw_driver_t, which generally provides a
* 48-bit RTC counter with a frequency of 32.678 kHz. This way the RTT
* implementation always sees a 48-bit counter with a frequency of 32.768 kHz
* regardless of which hardware implementation is actually used.
*
* If pseudomodule `esp_rtc_timer_32` is enabled by the board definition and
* the 32.768 kHz crystal is actually connected, the 48-bit RTC counter
* is used. Otherwise, the 64 bit microsecond system timer is used.
*
* Since the 64-bit microsecond system timer does not work during light/deep
* or during a reboot, the status of the 64-bit microsecond system timer is
* saved in RTC memory before entering a sleep mode or reboot. When leaving
* the sleep mode or after a reboot, it will be updated from the RTC counter.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef RTT_ARCH_H
#define RTT_ARCH_H
#include "periph/rtt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Maximum value of the 48-bit RTT hardware counter
*/
#define RTT_HW_COUNTER_MAX ((1ULL << 48) - 1)
/**
* @brief RTT hardware abstraction layer driver
*/
typedef struct {
/**
* @brief Init the current hardware counter
*/
void (*init)(void);
/**
* @brief Get the current hardware counter value
* @return 48-bit counter value with a frequency of 32.768 kHz
*/
uint64_t (*get_counter)(void);
/**
* @brief Set the hardware alarm
* @param[in] alarm alarm time as 32.768 kHz ticks
* @param[in] cb function called on alarm interrupt
* @param[in] arg argument used as parameter for the @p cb function
*/
void (*set_alarm)(uint32_t alarm, rtt_cb_t cb, void *arg);
/**
* @brief Clear the hardware alarm
*/
void (*clear_alarm)(void);
/**
* @brief Save the counter value before sleep or reboot if necessary
*/
void (*save_counter)(void);
/**
* @brief Restore the counter value before sleep or reboot
* @param[in] in_init true if function is called after deep sleep or
* reboot, false otherwise
*/
void (*restore_counter)(bool in_init);
/**
* @brief Enable the RTT hardware counter
*/
void (*poweron)(void);
/**
* @brief Disable the RTT hardware counter
*/
void (*poweroff)(void);
} rtt_hw_driver_t;
/**
* @brief Called before the power management enters a light or deep sleep mode
* @param mode sleep mode that is entered
* @return time to sleep in us
*/
uint64_t rtt_pm_sleep_enter(unsigned mode);
/**
* @brief Called after the power management left light sleep mode
* @param cause wake-up cause
*/
void rtt_pm_sleep_exit(uint32_t cause);
#ifdef __cplusplus
}
#endif
#endif /* RTT_ARCH_H */
/** @} */

282
cpu/esp32/periph/rtt.c Normal file
View File

@ -0,0 +1,282 @@
/*
* Copyright (C) 2020 Gunar Schorcht
*
* 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_esp32
* @ingroup drivers_periph_rtt
* @{
*
* @file
* @brief Low-level RTT driver implementation for ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include "cpu.h"
#include "esp_attr.h"
#include "esp_sleep.h"
#include "irq_arch.h"
#include "log.h"
#include "periph/rtt.h"
#include "rtt_arch.h"
#include "syscalls.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define RTC_CLK_CAL_FRACT 19 /* fractional bits of calibration value */
/* contains the values as given at the interface */
typedef struct {
uint32_t alarm; /**< alarm value as set at the interface */
rtt_cb_t alarm_cb; /**< alarm callback */
rtt_cb_t overflow_cb; /**< overflow callback */
void *alarm_arg; /**< argument for alarm callback */
void *overflow_arg; /**< argument for overflow callback */
uint32_t alarm_active; /**< alarm that is currently active */
bool alarm_set; /**< indicates whether an alarm is active */
bool wakeup; /**< indicates whether next alarm is a wake-up */
} rtt_counter_t;
static rtt_counter_t rtt_counter;
static uint32_t RTC_BSS_ATTR _rtt_offset;
/* we can't include soc/rtc.h because of rtc_init declaration conflicts */
extern uint32_t rtc_clk_slow_freq_get_hz(void);
/* forward declaration of functions */
void rtt_restore_counter(bool sys_time);
static void _rtt_update_hw_alarm(void);
static void IRAM_ATTR _rtt_isr(void *arg);
/* forward declarations of driver functions */
uint64_t _rtc_get_counter(void);
/* declaration of hardware counters */
extern const rtt_hw_driver_t _rtt_hw_sys_driver;
extern const rtt_hw_driver_t _rtt_hw_rtc_driver;
/* used hardware driver (default _rtt_hw_sys_driver) */
static const rtt_hw_driver_t *_rtt_hw = &_rtt_hw_sys_driver;
void rtt_init(void)
{
if (IS_USED(MODULE_ESP_RTC_TIMER_32K)) {
/* check whether the 32.678 kHz crystal is working */
if (rtc_clk_slow_freq_get_hz() == 32768) {
_rtt_hw = &_rtt_hw_rtc_driver;
}
else {
LOG_ERROR("[rtt] 32.768 kHz crystal not used!\n");
}
}
DEBUG("%s rtt_offset=%u @rtc=%llu rtc_active=%d @sys_time=%llu\n", __func__,
_rtt_offset, _rtc_get_counter(),
(_rtt_hw == &_rtt_hw_sys_driver) ? 1 : 0, system_get_time_64());
/* init the hardware counter if necessary */
_rtt_hw->init();
/* restore counter from RTC after deep sleep or reboot */
rtt_restore_counter(true);
/* clear alarm settings */
rtt_clear_alarm();
rtt_clear_overflow_cb();
/* power on the module and enable interrupts */
rtt_poweron();
}
void rtt_poweron(void)
{
_rtt_hw->poweron();
}
void rtt_poweroff(void)
{
_rtt_hw->poweroff();
}
void rtt_set_overflow_cb(rtt_cb_t cb, void *arg)
{
/* there is no overflow interrupt, we emulate */
rtt_counter.overflow_cb = cb;
rtt_counter.overflow_arg = arg;
_rtt_update_hw_alarm();
}
void rtt_clear_overflow_cb(void)
{
/* there is no overflow interrupt, we emulate */
rtt_counter.overflow_cb = NULL;
rtt_counter.overflow_arg = NULL;
_rtt_update_hw_alarm();
}
uint32_t rtt_get_counter(void)
{
/* we use only the lower 32 bit of the 48-bit RTC counter */
uint32_t counter = _rtt_hw->get_counter() + _rtt_offset;
DEBUG("%s counter=%u @sys_time=%u\n", __func__, counter, system_get_time());
return counter;
}
void rtt_set_counter(uint32_t counter)
{
uint32_t _rtt_current = _rtt_hw->get_counter();
_rtt_offset = counter - _rtt_current;
DEBUG("%s set=%u rtt_offset=%u @rtt=%u\n",
__func__, counter, _rtt_offset, _rtt_current);
_rtt_update_hw_alarm();
}
void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
{
uint32_t counter = rtt_get_counter();
rtt_counter.alarm = alarm;
rtt_counter.alarm_cb = cb;
rtt_counter.alarm_arg = arg;
DEBUG("%s alarm=%u @rtt=%u\n", __func__, alarm, counter);
_rtt_update_hw_alarm();
}
void rtt_clear_alarm(void)
{
/* clear the alarm */
rtt_counter.alarm = 0;
rtt_counter.alarm_cb = NULL;
rtt_counter.alarm_arg = NULL;
DEBUG("%s @rtt=%u\n", __func__, (uint32_t)_rtt_hw->get_counter());
_rtt_update_hw_alarm();
}
uint32_t rtt_get_alarm(void)
{
return rtt_counter.alarm;
}
void rtt_save_counter(void)
{
_rtt_hw->save_counter();
}
void rtt_restore_counter(bool in_init)
{
_rtt_hw->restore_counter(in_init);
}
uint64_t rtt_pm_sleep_enter(unsigned mode)
{
rtt_save_counter();
if (!rtt_counter.alarm_set) {
return 0;
}
uint32_t counter = rtt_get_counter();
uint64_t t_diff = RTT_TICKS_TO_US(rtt_counter.alarm_active - counter);
DEBUG("%s rtt_alarm=%u @rtt=%u t_diff=%llu\n", __func__,
rtt_counter.alarm_active, counter, t_diff);
if (t_diff) {
rtt_counter.wakeup = true;
esp_sleep_enable_timer_wakeup(t_diff);
}
else {
rtt_counter.wakeup = false;
}
return t_diff;
}
void rtt_pm_sleep_exit(uint32_t cause)
{
rtt_restore_counter(false);
if (cause == ESP_SLEEP_WAKEUP_TIMER) {
_rtt_isr(NULL);
}
}
static void _rtt_update_hw_alarm(void)
{
if (rtt_counter.alarm_cb && ((rtt_counter.alarm > rtt_get_counter()) ||
(rtt_counter.overflow_cb == NULL))) {
/* alarm is the next event if either the alarm is greater than the
current counter value or the overflow callback is not set. */
rtt_counter.alarm_active = rtt_counter.alarm;
rtt_counter.alarm_set = true;
_rtt_hw->set_alarm(rtt_counter.alarm - _rtt_offset, _rtt_isr, NULL);
}
else if (rtt_counter.overflow_cb) {
/* otherwise the overflow is the next event if its callback is set */
rtt_counter.alarm_active = 0;
rtt_counter.alarm_set = true;
_rtt_hw->set_alarm(0 - _rtt_offset, _rtt_isr, NULL);
}
else {
rtt_counter.alarm_set = false;
_rtt_hw->clear_alarm();
}
}
static void IRAM_ATTR _rtt_isr(void *arg)
{
DEBUG("%s\n", __func__);
uint32_t alarm = rtt_counter.alarm_active;
if (rtt_counter.wakeup) {
rtt_counter.wakeup = false;
DEBUG("%s wakeup alarm alarm=%u rtt_alarm=%u @rtt=%u\n",
__func__, alarm, rtt_counter.alarm_active, rtt_get_counter());
}
if ((alarm == rtt_counter.alarm) && rtt_counter.alarm_cb) {
DEBUG("%s alarm\n", __func__);
rtt_cb_t alarm_cb = rtt_counter.alarm_cb;
void * alarm_arg = rtt_counter.alarm_arg;
/* clear the alarm first, includes setting next alarm to overflow */
rtt_clear_alarm();
/* call the alarm handler afterwards if a callback is set */
if (alarm_cb) {
alarm_cb(alarm_arg);
}
}
if (alarm == 0) {
DEBUG("%s overflow\n", __func__);
/* set next alarm which is either an alarm if configured or overflow */
_rtt_update_hw_alarm();
/* call the overflow handler if set */
if (rtt_counter.overflow_cb) {
rtt_counter.overflow_cb(rtt_counter.overflow_arg);
}
}
DEBUG("%s next rtt=%u\n", __func__, rtt_counter.alarm_active);
}
uint32_t _rtt_hw_to_rtt_counter(uint32_t hw_counter)
{
return hw_counter + _rtt_offset;
}

View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2020 Gunar Schorcht
*
* 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_esp32
* @ingroup drivers_periph_rtt
* @{
*
* @file
* @brief Low-level RTT driver implementation for ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include "cpu.h"
#include "esp_attr.h"
#include "esp/common_macros.h"
#include "esp_common.h"
#include "esp_sleep.h"
#include "irq_arch.h"
#include "log.h"
#include "periph/rtt.h"
#include "rtt_arch.h"
#include "soc/dport_reg.h"
#include "soc/rtc_cntl_struct.h"
#include "syscalls.h"
#include "timex.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define RTC_CLK_CAL_FRACT 19 /* fractional bits of calibration value */
typedef struct {
uint32_t alarm_set; /**< alarm set at interface */
rtt_cb_t alarm_cb; /**< alarm callback */
void *alarm_arg; /**< argument for alarm callback */
} _rtc_alarm_t;
static _rtc_alarm_t _rtc_alarm;
/* we can't include soc/rtc.h because of rtc_init declaration conflicts */
extern uint32_t esp_clk_slowclk_cal_get(void);
/* convert hardware counter to 32-bit RTT counter */
uint32_t _rtt_hw_to_rtt_counter(uint32_t hw_counter);
static void IRAM _rtc_isr(void *arg);
/* converts a 48-bit RTC counter value to microseconds */
uint64_t _rtc_counter_to_us(uint64_t raw)
{
const uint32_t cal = esp_clk_slowclk_cal_get();
return ((((raw >> 32) * cal) << (32 - RTC_CLK_CAL_FRACT)) + /* high part */
(((raw & 0xffffffff) * cal) >> RTC_CLK_CAL_FRACT)); /* low part */
}
static void _rtc_init(void)
{
}
static void _rtc_poweron(void)
{
/* route all interrupt sources to the same RTT level type interrupt */
intr_matrix_set(PRO_CPU_NUM, ETS_RTC_CORE_INTR_SOURCE, CPU_INUM_RTC);
/* set interrupt handler and enable the CPU interrupt */
xt_set_interrupt_handler(CPU_INUM_RTC, _rtc_isr, NULL);
xt_ints_on(BIT(CPU_INUM_RTC));
}
static void _rtc_poweroff(void)
{
/* reset interrupt handler and disable the CPU interrupt */
xt_ints_off(BIT(CPU_INUM_RTC));
xt_set_interrupt_handler(CPU_INUM_RTC, NULL, NULL);
}
uint64_t _rtc_get_counter(void)
{
/* trigger timer register update */
RTCCNTL.time_update.update = 1;
/* wait until values in registers are valid */
while (!RTCCNTL.time_update.valid) {
ets_delay_us(1);
}
/* read the time from 48-bit counter and return */
return (((uint64_t)RTCCNTL.time1.val) << 32) + RTCCNTL.time0;
}
static void _rtc_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
{
/* compute the time difference for 32.768 kHz as 32-bit value */
uint64_t rtc_counter = _rtc_get_counter();
uint32_t rtt_diff = alarm - rtc_counter;
/* use computed time difference directly to set the RTC counter alarm */
uint64_t rtc_alarm = (rtc_counter + rtt_diff) & RTT_HW_COUNTER_MAX;
DEBUG("%s alarm=%u rtt_diff=%u rtc_alarm=%llu @rtc=%llu\n",
__func__, alarm, rtt_diff, rtc_alarm, rtc_counter);
/* save the alarm configuration for interrupt handling */
_rtc_alarm.alarm_set = alarm;
_rtc_alarm.alarm_cb = cb;
_rtc_alarm.alarm_arg = arg;
/* set the timer value */
RTCCNTL.slp_timer0 = rtc_alarm & 0xffffffff;
RTCCNTL.slp_timer1.slp_val_hi = rtc_alarm >> 32;
DEBUG("%s %08x%08x \n", __func__,
RTCCNTL.slp_timer1.slp_val_hi, RTCCNTL.slp_timer0);
/* enable RTC timer alarm */
RTCCNTL.slp_timer1.main_timer_alarm_en = 1;
/* clear and enable RTC timer interrupt */
RTCCNTL.int_clr.rtc_main_timer = 1;
RTCCNTL.int_ena.rtc_main_timer = 1;
}
static void _rtc_clear_alarm(void)
{
/* disable alarms first */
RTCCNTL.slp_timer1.main_timer_alarm_en = 0;
/* clear the bit in interrupt enable and status register */
RTCCNTL.int_clr.rtc_main_timer = 0;
RTCCNTL.int_ena.rtc_main_timer = 0;
/* reset the alarm configuration for interrupt handling */
_rtc_alarm.alarm_set = 0;
_rtc_alarm.alarm_cb = NULL;
_rtc_alarm.alarm_arg = NULL;
}
static void _rtc_save_counter(void)
{
}
static void _rtc_restore_counter(bool in_init)
{
(void)in_init;
}
static void IRAM _rtc_isr(void *arg)
{
/* disable alarms first */
RTCCNTL.slp_timer1.main_timer_alarm_en = 0;
/* clear the bit in interrupt enable and status register */
RTCCNTL.int_clr.rtc_main_timer = 0;
RTCCNTL.int_ena.rtc_main_timer = 0;
/* save the lower 32 bit of the current counter value */
uint32_t counter = _rtc_get_counter();
DEBUG("%s %u\n", __func__, counter);
if (_rtc_alarm.alarm_cb) {
DEBUG("%s alarm %u\n", __func__, counter);
rtt_cb_t alarm_cb = _rtc_alarm.alarm_cb;
void *alarm_arg = _rtc_alarm.alarm_arg;
/* clear the alarm first */
_rtc_alarm.alarm_cb = NULL;
_rtc_alarm.alarm_arg = NULL;
/* call the alarm handler afterwards if callback was defined */
alarm_cb(alarm_arg);
}
}
const rtt_hw_driver_t _rtt_hw_rtc_driver = {
.init = _rtc_init,
.get_counter = _rtc_get_counter,
.set_alarm = _rtc_set_alarm,
.clear_alarm = _rtc_clear_alarm,
.poweron = _rtc_poweron,
.poweroff = _rtc_poweroff,
.save_counter = _rtc_save_counter,
.restore_counter = _rtc_restore_counter,
};

View File

@ -0,0 +1,219 @@
/*
* Copyright (C) 2020 Gunar Schorcht
*
* 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_esp32
* @ingroup drivers_periph_rtt
* @{
*
* @file
* @brief Low-level RTT driver implementation for ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include "cpu.h"
#include "esp_attr.h"
#include "esp/common_macros.h"
#include "esp_sleep.h"
#include "irq_arch.h"
#include "log.h"
#include "periph/rtt.h"
#include "rtt_arch.h"
#include "soc/timer_group_struct.h"
#include "syscalls.h"
#include "timex.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define TIMER_SYSTEM_GROUP TIMERG0
#define TIMER_SYSTEM_INT_MASK BIT(0)
#define TIMER_SYSTEM_INT_SRC ETS_TG0_T0_LEVEL_INTR_SOURCE
#define SYS_US_TO_TICKS(us) ((((uint64_t)us) << 15) / US_PER_SEC)
#define SYS_TICKS_TO_US(cnt) (((uint64_t)cnt * US_PER_SEC) >> 15)
typedef struct {
uint32_t alarm_set; /**< alarm set at interface */
rtt_cb_t alarm_cb; /**< alarm callback */
void *alarm_arg; /**< argument for alarm callback */
} _sys_alarm_t;
static _sys_alarm_t _sys_alarm;
/* variables used to save counters during sleep or reboot */
static uint64_t RTC_BSS_ATTR _rtc_counter_saved;
static uint64_t RTC_BSS_ATTR _sys_counter_saved;
/* the offset of the system time to the RTC time in microseconds */
static uint64_t _sys_counter_offset;
/* forward declarations of functions required from RTC counter */
extern uint64_t _rtc_get_counter(void);
extern uint64_t _rtc_counter_to_us(uint64_t raw);
static void IRAM _sys_isr(void *arg);
static void _sys_init(void)
{
}
static void _sys_poweron(void)
{
/* route all interrupt sources to the same RTT level type interrupt */
intr_matrix_set(PRO_CPU_NUM, TIMER_SYSTEM_INT_SRC, CPU_INUM_RTC);
/* set interrupt handler and enable the CPU interrupt */
xt_set_interrupt_handler(CPU_INUM_RTC, _sys_isr, NULL);
xt_ints_on(BIT(CPU_INUM_RTC));
}
static void _sys_poweroff(void)
{
/* reset interrupt handler and disable the CPU interrupt */
xt_ints_off(BIT(CPU_INUM_RTC));
xt_set_interrupt_handler(CPU_INUM_RTC, NULL, NULL);
}
static uint64_t _sys_get_counter(void)
{
/* convert the 64-bit microsecond system time to 48-bit 32.768 kHz time */
return SYS_US_TO_TICKS(system_get_time_64() + _sys_counter_offset) & RTT_HW_COUNTER_MAX;
}
static void _sys_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg)
{
/* compute the time difference for 32.768 kHz as 32-bit value */
uint32_t rtt_diff = alarm - _sys_get_counter();
/*
* convert the computed time difference for 32.768 kHz to a 64-bit
* microsecond value and determine the alarm time for the 64-bit
* microsecond system timer
*/
uint64_t _sys_diff = SYS_TICKS_TO_US(rtt_diff);
uint64_t _sys_time = system_get_time_64();
uint64_t _sys_alarm_time = _sys_time + _sys_diff;
DEBUG("%s alarm=%u rtt_diff=%u "
"sys_diff=%llu sys_alarm=%llu @sys_time=%llu\n", __func__,
alarm, rtt_diff, _sys_diff, _sys_alarm_time, _sys_time);
/* save the alarm configuration for interrupt handling */
_sys_alarm.alarm_set = alarm;
_sys_alarm.alarm_cb = cb;
_sys_alarm.alarm_arg = arg;
/* set the timer value */
TIMER_SYSTEM.alarm_high = (uint32_t)(_sys_alarm_time >> 32);
TIMER_SYSTEM.alarm_low = (uint32_t)(_sys_alarm_time & 0xffffffff);
/* clear the bit in status and set the bit in interrupt enable */
TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
TIMER_SYSTEM_GROUP.int_ena.val |= TIMER_SYSTEM_INT_MASK;
/* enable the timer alarm */
TIMER_SYSTEM.config.level_int_en = 1;
TIMER_SYSTEM.config.alarm_en = 1;
}
static void _sys_clear_alarm(void)
{
/* disable alarms first */
TIMER_SYSTEM.config.level_int_en = 0;
TIMER_SYSTEM.config.alarm_en = 0;
/* clear the bit in interrupt enable and status register */
TIMER_SYSTEM_GROUP.int_ena.val &= ~TIMER_SYSTEM_INT_MASK;
TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
/* reset the alarm configuration for interrupt handling */
_sys_alarm.alarm_set = 0;
_sys_alarm.alarm_cb = NULL;
_sys_alarm.alarm_arg = NULL;
}
static void _sys_save_counter(void)
{
critical_enter();
/* save counters for synchronization after wakeup or reboot */
_rtc_counter_saved = _rtc_get_counter();
_sys_counter_saved = system_get_time_64() + _sys_counter_offset;
critical_exit();
DEBUG("%s rtc_time_saved=%llu sys_time_saved=%llu\n", __func__,
_rtc_counter_saved, _sys_counter_saved);
}
static void _sys_restore_counter(bool in_init)
{
critical_enter();
/* synchronize RTC counter and the 64-bit microsecond system timer */
uint64_t _rtc_time_diff = _rtc_get_counter() - _rtc_counter_saved;
_sys_counter_offset += _rtc_counter_to_us(_rtc_time_diff & RTT_HW_COUNTER_MAX);
_sys_counter_offset += (in_init) ? _sys_counter_saved : 0;
critical_exit();
DEBUG("%s rtc_time_saved=%llu rtc_time_diff=%llu "
"sys_time_saved=%llu sys_time_offset=%llu\n", __func__,
_rtc_counter_saved, _rtc_time_diff,
_sys_counter_saved, _sys_counter_offset);
}
static void IRAM _sys_isr(void *arg)
{
if (!(TIMER_SYSTEM_GROUP.int_st_timers.val & TIMER_SYSTEM_INT_MASK)) {
/* return in case of another timer interrupt */
return;
}
/* disable alarms first */
TIMER_SYSTEM.config.level_int_en = 0;
TIMER_SYSTEM.config.alarm_en = 0;
/* clear the bit in interrupt enable and status register */
TIMER_SYSTEM_GROUP.int_ena.val &= ~TIMER_SYSTEM_INT_MASK;
TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
/* save the lower 32 bit of the current counter value */
uint32_t counter = _sys_get_counter();
DEBUG("%s %u\n", __func__, counter);
if (_sys_alarm.alarm_cb) {
DEBUG("%s alarm %u\n", __func__, counter);
rtt_cb_t alarm_cb = _sys_alarm.alarm_cb;
void *alarm_arg = _sys_alarm.alarm_arg;
/* clear the alarm first */
_sys_alarm.alarm_cb = NULL;
_sys_alarm.alarm_arg = NULL;
/* call the alarm handler afterwards if callback was defined */
alarm_cb(alarm_arg);
}
}
const rtt_hw_driver_t _rtt_hw_sys_driver = {
.init = _sys_init,
.get_counter = _sys_get_counter,
.set_alarm = _sys_set_alarm,
.clear_alarm = _sys_clear_alarm,
.poweron = _sys_poweron,
.poweroff = _sys_poweroff,
.save_counter = _sys_save_counter,
.restore_counter = _sys_restore_counter,
};