cpu/esp32: add RTT counter implementation
fixup! cpu/esp32: add RTT counter implementation
This commit is contained in:
parent
6cd9896ac0
commit
f67cb48f6d
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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> [[TOC](#esp32_toc)]
|
||||
\anchor esp32_rtt_counter
|
||||
## <a name="esp32_rtt_counter"> RTT implementation </a> [[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> [[TOC](#esp32_toc)]
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
124
cpu/esp32/include/rtt_arch.h
Normal file
124
cpu/esp32/include/rtt_arch.h
Normal 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
282
cpu/esp32/periph/rtt.c
Normal 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;
|
||||
}
|
||||
192
cpu/esp32/periph/rtt_hw_rtc.c
Normal file
192
cpu/esp32/periph/rtt_hw_rtc.c
Normal 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,
|
||||
};
|
||||
219
cpu/esp32/periph/rtt_hw_sys.c
Normal file
219
cpu/esp32/periph/rtt_hw_sys.c
Normal 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,
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user