Extract RTC, implement fake RTC deepsleep
This commit is contained in:
parent
f1792c8474
commit
6b2c9985ff
4
main.c
4
main.c
|
@ -157,7 +157,7 @@ STATIC void start_mp(supervisor_allocation* heap) {
|
|||
|
||||
#if CIRCUITPY_ALARM
|
||||
// Record which alarm woke us up, if any. An object may be created so the heap must be functional.
|
||||
common_hal_alarm_save_wake_alarm();
|
||||
shared_alarm_save_wake_alarm();
|
||||
// Reset alarm module only after we retrieved the wakeup alarm.
|
||||
common_hal_alarm_reset();
|
||||
#endif
|
||||
|
@ -301,8 +301,6 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// TODO: on deep sleep, make sure display is refreshed before sleeping (for e-ink).
|
||||
|
||||
cleanup_after_vm(heap);
|
||||
|
||||
if (result.return_code & PYEXEC_FORCED_EXIT) {
|
||||
|
|
|
@ -126,7 +126,7 @@ STATIC void _idle_until_alarm(void) {
|
|||
// Allow ctrl-C interrupt.
|
||||
if (common_hal_alarm_woken_from_sleep()) {
|
||||
// This saves the return of common_hal_alarm_get_wake_alarm through Shared Bindings
|
||||
common_hal_alarm_save_wake_alarm();
|
||||
shared_alarm_save_wake_alarm();
|
||||
return;
|
||||
}
|
||||
port_idle_until_interrupt();
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
void common_hal_alarm_reset(void) {
|
||||
// alarm_sleep_memory_reset();
|
||||
alarm_pin_pinalarm_reset();
|
||||
// alarm_time_timealarm_reset();
|
||||
alarm_time_timealarm_reset();
|
||||
// alarm_touch_touchalarm_reset();
|
||||
// esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ mp_obj_t common_hal_alarm_get_wake_alarm(void) {
|
|||
// Set up light sleep or deep sleep alarms.
|
||||
STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
|
||||
alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms);
|
||||
// alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms);
|
||||
alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms);
|
||||
// alarm_touch_touchalarm_set_alarm(deep_sleep, n_alarms, alarms);
|
||||
}
|
||||
|
||||
|
@ -111,23 +111,20 @@ STATIC void _idle_until_alarm(void) {
|
|||
RUN_BACKGROUND_TASKS;
|
||||
// Allow ctrl-C interrupt.
|
||||
if (common_hal_alarm_woken_from_sleep()) {
|
||||
common_hal_alarm_save_wake_alarm();
|
||||
shared_alarm_save_wake_alarm();
|
||||
return;
|
||||
}
|
||||
// port_idle_until_interrupt();
|
||||
port_idle_until_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) {
|
||||
// if (supervisor_workflow_active()) {
|
||||
// mp_raise_NotImplementedError(translate("Cannot sleep with USB connected"));
|
||||
// }
|
||||
_setup_sleep_alarms(false, n_alarms, alarms);
|
||||
|
||||
// If USB is active, only pretend to sleep. Otherwise, light sleep
|
||||
if (supervisor_workflow_active()) {
|
||||
_setup_sleep_alarms(false, n_alarms, alarms);
|
||||
_idle_until_alarm();
|
||||
} else {
|
||||
_setup_sleep_alarms(false, n_alarms, alarms);
|
||||
port_disable_tick();
|
||||
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
|
||||
port_enable_tick();
|
||||
|
@ -144,13 +141,17 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala
|
|||
|
||||
//#define NORETURN __attribute__((noreturn))
|
||||
void NORETURN common_hal_alarm_enter_deep_sleep(void) {
|
||||
// alarm_pin_pinalarm_prepare_for_deep_sleep();
|
||||
alarm_pin_pinalarm_prepare_for_deep_sleep();
|
||||
//port_disable_tick();
|
||||
// alarm_touch_touchalarm_prepare_for_deep_sleep();
|
||||
while(1);
|
||||
// HAL_PWR_EnableBkUpAccess();
|
||||
// __HAL_RCC_BACKUPRESET_FORCE();
|
||||
// __HAL_RCC_BACKUPRESET_RELEASE();
|
||||
|
||||
// The ESP-IDF caches the deep sleep settings and applies them before sleep.
|
||||
// We don't need to worry about resetting them in the interim.
|
||||
// esp_deep_sleep_start();
|
||||
HAL_PWR_EnterSTANDBYMode();
|
||||
|
||||
// Should never hit this
|
||||
while(1);
|
||||
}
|
||||
|
||||
void common_hal_alarm_gc_collect(void) {
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
STATIC bool woke_up;
|
||||
|
||||
STATIC uint16_t alarm_pin_triggered;
|
||||
STATIC bool deep_wkup_enabled;
|
||||
|
||||
STATIC void pin_alarm_callback(uint8_t num) {
|
||||
alarm_pin_triggered |= (1 << num);
|
||||
|
@ -112,17 +113,32 @@ void alarm_pin_pinalarm_reset(void) {
|
|||
woke_up = false;
|
||||
}
|
||||
|
||||
// Deep sleep alarms don't actually make use of EXTI, but we pretend they're the same.
|
||||
void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
|
||||
for (size_t i = 0; i < n_alarms; i++) {
|
||||
if (MP_OBJ_IS_TYPE(alarms[i], &alarm_pin_pinalarm_type)) {
|
||||
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
|
||||
stm_peripherals_exti_enable(alarm->pin->number);
|
||||
|
||||
if (deep_sleep) {
|
||||
// Deep sleep only wakes on a rising edge from one pin, WKUP (PA00)
|
||||
if (alarm->pin != &pin_PA00) {
|
||||
mp_raise_ValueError(translate("Only the WKUP pin can be used to wake from Deep Sleep"));
|
||||
}
|
||||
HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1);
|
||||
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
|
||||
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
|
||||
|
||||
deep_wkup_enabled = true;
|
||||
} else {
|
||||
stm_peripherals_exti_enable(alarm->pin->number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alarm_pin_pinalarm_reset_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
|
||||
alarm_pin_triggered = 0;
|
||||
deep_wkup_enabled = false;
|
||||
for (size_t i = 0; i < n_alarms; i++) {
|
||||
if (MP_OBJ_IS_TYPE(alarms[i], &alarm_pin_pinalarm_type)) {
|
||||
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
|
||||
|
@ -131,6 +147,10 @@ void alarm_pin_pinalarm_reset_alarms(bool deep_sleep, size_t n_alarms, const mp_
|
|||
}
|
||||
}
|
||||
|
||||
// If we don't have WKUP enabled, ensure it's disabled
|
||||
// TODO; is this really required?
|
||||
void alarm_pin_pinalarm_prepare_for_deep_sleep(void) {
|
||||
|
||||
if (!deep_wkup_enabled) {
|
||||
HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
|
||||
#include "shared-bindings/alarm/time/TimeAlarm.h"
|
||||
#include "shared-bindings/time/__init__.h"
|
||||
#include "supervisor/port.h"
|
||||
#include "peripherals/rtc.h"
|
||||
|
||||
STATIC volatile bool woke_up;
|
||||
|
||||
void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time) {
|
||||
self->monotonic_time = monotonic_time;
|
||||
|
@ -37,42 +41,59 @@ mp_float_t common_hal_alarm_time_timealarm_get_monotonic_time(alarm_time_timeala
|
|||
return self->monotonic_time;
|
||||
}
|
||||
|
||||
// mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms) {
|
||||
// // First, check to see if we match
|
||||
// for (size_t i = 0; i < n_alarms; i++) {
|
||||
// if (MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) {
|
||||
// return alarms[i];
|
||||
// }
|
||||
// }
|
||||
// alarm_time_timealarm_obj_t *timer = m_new_obj(alarm_time_timealarm_obj_t);
|
||||
// timer->base.type = &alarm_time_timealarm_type;
|
||||
// // TODO: Set monotonic_time based on the RTC state.
|
||||
// timer->monotonic_time = 0.0f;
|
||||
// return timer;
|
||||
// }
|
||||
mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms) {
|
||||
// First, check to see if we match
|
||||
for (size_t i = 0; i < n_alarms; i++) {
|
||||
if (MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) {
|
||||
return alarms[i];
|
||||
}
|
||||
}
|
||||
alarm_time_timealarm_obj_t *timer = m_new_obj(alarm_time_timealarm_obj_t);
|
||||
timer->base.type = &alarm_time_timealarm_type;
|
||||
// TODO: Set monotonic_time based on the RTC state.
|
||||
timer->monotonic_time = 0.0f;
|
||||
return timer;
|
||||
}
|
||||
|
||||
// This is run in the timer task. We use it to wake the main CircuitPython task.
|
||||
// void timer_callback(void *arg) {
|
||||
// (void) arg;
|
||||
// woke_up = true;
|
||||
// xTaskNotifyGive(circuitpython_task);
|
||||
// }
|
||||
STATIC void timer_callback(void) {
|
||||
woke_up = true;
|
||||
}
|
||||
|
||||
bool alarm_time_timealarm_woke_us_up(void) {
|
||||
// return woke_up;
|
||||
return false;
|
||||
//mp_printf(&mp_plat_print,"Woke Up:%d\n",woke_up);
|
||||
return woke_up;
|
||||
}
|
||||
|
||||
// void alarm_time_timealarm_reset(void) {
|
||||
// esp_timer_stop(pretend_sleep_timer);
|
||||
// esp_timer_delete(pretend_sleep_timer);
|
||||
// pretend_sleep_timer = NULL;
|
||||
// woke_up = false;
|
||||
// }
|
||||
void alarm_time_timealarm_reset(void) {
|
||||
// mp_printf(&mp_plat_print,"timealarm reset");
|
||||
woke_up = false;
|
||||
}
|
||||
|
||||
void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
|
||||
// HAL_RTCEx_SetWakeUpTimer_IT(&RTCHandle, 0xA017, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
|
||||
// HAL_RTCEx_SetWakeUpTimer_IT(&_hrtc, rtc_clock_frequency / 1024 / 2, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
|
||||
// HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 1, 0U);
|
||||
// HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
|
||||
// Search through alarms for TimeAlarm instances, and check that there's only one
|
||||
bool timealarm_set = false;
|
||||
alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL;
|
||||
for (size_t i = 0; i < n_alarms; i++) {
|
||||
if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) {
|
||||
continue;
|
||||
}
|
||||
if (timealarm_set) {
|
||||
mp_raise_ValueError(translate("Only one alarm.time alarm can be set."));
|
||||
}
|
||||
timealarm = MP_OBJ_TO_PTR(alarms[i]);
|
||||
timealarm_set = true;
|
||||
}
|
||||
if (!timealarm_set) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute how long to actually sleep, considering the time now.
|
||||
mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f;
|
||||
uint32_t wakeup_in_secs = MAX(0.0f, timealarm->monotonic_time - now_secs);
|
||||
uint32_t wakeup_in_ticks = wakeup_in_secs * 1024;
|
||||
|
||||
// Use alarm B, since port reserves A
|
||||
stm32_peripherals_rtc_assign_alarm_callback(PERIPHERALS_ALARM_B,timer_callback);
|
||||
stm32_peripherals_rtc_set_alarm(PERIPHERALS_ALARM_B,wakeup_in_ticks);
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ typedef struct {
|
|||
mp_float_t monotonic_time; // values compatible with time.monotonic_time()
|
||||
} alarm_time_timealarm_obj_t;
|
||||
|
||||
// // Find the alarm object that caused us to wake up or create an equivalent one.
|
||||
// mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms);
|
||||
// // Check for the wake up alarm from pretend deep sleep.
|
||||
// Find the alarm object that caused us to wake up or create an equivalent one.
|
||||
mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms);
|
||||
// Check for the wake up alarm from pretend deep sleep.
|
||||
bool alarm_time_timealarm_woke_us_up(void);
|
||||
// void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
|
||||
// void alarm_time_timealarm_reset(void);
|
||||
void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
|
||||
void alarm_time_timealarm_reset(void);
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
#include "peripherals/exti.h"
|
||||
|
||||
|
||||
STATIC bool stm_exti_reserved[STM32_GPIO_PORT_SIZE];
|
||||
STATIC void (*stm_exti_callback[STM32_GPIO_PORT_SIZE])(uint8_t num);
|
||||
|
||||
|
|
|
@ -22,4 +22,203 @@
|
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
*/
|
||||
|
||||
#include "peripherals/rtc.h"
|
||||
#include STM32_HAL_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "lib/timeutils/timeutils.h"
|
||||
|
||||
// Default period for ticks is 1/1024 second
|
||||
#define TICK_DIVISOR 1024
|
||||
|
||||
STATIC RTC_HandleTypeDef hrtc;
|
||||
|
||||
#if BOARD_HAS_LOW_SPEED_CRYSTAL
|
||||
STATIC uint32_t rtc_clock_frequency = LSE_VALUE;
|
||||
#else
|
||||
STATIC uint32_t rtc_clock_frequency = LSI_VALUE;
|
||||
#endif
|
||||
|
||||
volatile uint32_t seconds_to_date = 0;
|
||||
volatile uint32_t cached_date = 0;
|
||||
volatile uint32_t seconds_to_minute = 0;
|
||||
volatile uint32_t cached_hours_minutes = 0;
|
||||
|
||||
volatile bool alarmed_already[2];
|
||||
|
||||
bool peripherals_wkup_on = false;
|
||||
|
||||
static void (*wkup_callback)(void);
|
||||
static void(*alarm_callbacks[2])(void);
|
||||
|
||||
uint32_t stm32_peripherals_get_rtc_freq(void) {
|
||||
return rtc_clock_frequency;
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_init(void) {
|
||||
// RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
|
||||
__HAL_RCC_RTC_ENABLE();
|
||||
hrtc.Instance = RTC;
|
||||
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
|
||||
// Divide async as little as possible so that we have rtc_clock_frequency count in subseconds.
|
||||
// This ensures our timing > 1 second is correct.
|
||||
hrtc.Init.AsynchPrediv = 0x0;
|
||||
hrtc.Init.SynchPrediv = rtc_clock_frequency - 1;
|
||||
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
|
||||
|
||||
HAL_RTC_Init(&hrtc);
|
||||
HAL_RTCEx_EnableBypassShadow(&hrtc);
|
||||
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
|
||||
}
|
||||
|
||||
// This function is called often for timing so we cache the seconds elapsed computation based on the
|
||||
// register value. The STM HAL always does shifts and conversion if we use it directly.
|
||||
uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t* subticks) {
|
||||
// Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
|
||||
// SSR twice to make sure we didn't read across a tick.
|
||||
__disable_irq();
|
||||
uint32_t first_ssr = (uint32_t)(RTC->SSR);
|
||||
uint32_t time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK);
|
||||
uint32_t date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK);
|
||||
uint32_t ssr = (uint32_t)(RTC->SSR);
|
||||
while (ssr != first_ssr) {
|
||||
first_ssr = ssr;
|
||||
time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK);
|
||||
date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK);
|
||||
ssr = (uint32_t)(RTC->SSR);
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
uint32_t subseconds = rtc_clock_frequency - 1 - ssr;
|
||||
|
||||
if (date != cached_date) {
|
||||
uint32_t year = (uint8_t)((date & (RTC_DR_YT | RTC_DR_YU)) >> 16U);
|
||||
uint8_t month = (uint8_t)((date & (RTC_DR_MT | RTC_DR_MU)) >> 8U);
|
||||
uint8_t day = (uint8_t)(date & (RTC_DR_DT | RTC_DR_DU));
|
||||
// Add 2000 since the year is only the last two digits.
|
||||
year = 2000 + (uint32_t)RTC_Bcd2ToByte(year);
|
||||
month = (uint8_t)RTC_Bcd2ToByte(month);
|
||||
day = (uint8_t)RTC_Bcd2ToByte(day);
|
||||
seconds_to_date = timeutils_seconds_since_2000(year, month, day, 0, 0, 0);
|
||||
cached_date = date;
|
||||
}
|
||||
uint32_t hours_minutes = time & (RTC_TR_HT | RTC_TR_HU | RTC_TR_MNT | RTC_TR_MNU);
|
||||
if (hours_minutes != cached_hours_minutes) {
|
||||
uint8_t hours = (uint8_t)((time & (RTC_TR_HT | RTC_TR_HU)) >> 16U);
|
||||
uint8_t minutes = (uint8_t)((time & (RTC_TR_MNT | RTC_TR_MNU)) >> 8U);
|
||||
hours = (uint8_t)RTC_Bcd2ToByte(hours);
|
||||
minutes = (uint8_t)RTC_Bcd2ToByte(minutes);
|
||||
seconds_to_minute = 60 * (60 * hours + minutes);
|
||||
cached_hours_minutes = hours_minutes;
|
||||
}
|
||||
uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU));
|
||||
seconds = (uint8_t)RTC_Bcd2ToByte(seconds);
|
||||
if (subticks != NULL) {
|
||||
*subticks = subseconds % 32;
|
||||
}
|
||||
|
||||
uint64_t raw_ticks = ((uint64_t) TICK_DIVISOR) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / 32;
|
||||
return raw_ticks;
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_assign_wkup_callback(void(*callback)(void)) {
|
||||
wkup_callback = callback;
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds) {
|
||||
//prep stuff from CubeMX
|
||||
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
|
||||
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
|
||||
|
||||
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) * seconds, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_set_wakeup_mode_tick(void) {
|
||||
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) / TICK_DIVISOR, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_enable_wakeup_timer(void) {
|
||||
peripherals_wkup_on = true;
|
||||
HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 1, 0U);
|
||||
HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_disable_wakeup_timer(void) {
|
||||
peripherals_wkup_on = false;
|
||||
HAL_NVIC_DisableIRQ(RTC_WKUP_IRQn);
|
||||
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_assign_alarm_callback(uint8_t alarm_idx, void(*callback)(void)) {
|
||||
alarm_callbacks[alarm_idx] = callback;
|
||||
}
|
||||
|
||||
void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
|
||||
uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks(NULL) + ticks;
|
||||
|
||||
RTC_AlarmTypeDef alarm;
|
||||
if (ticks > TICK_DIVISOR) {
|
||||
timeutils_struct_time_t tm;
|
||||
timeutils_seconds_since_2000_to_struct_time(raw_ticks / TICK_DIVISOR, &tm);
|
||||
alarm.AlarmTime.Hours = tm.tm_hour;
|
||||
alarm.AlarmTime.Minutes = tm.tm_min;
|
||||
alarm.AlarmTime.Seconds = tm.tm_sec;
|
||||
alarm.AlarmDateWeekDay = tm.tm_mday;
|
||||
// Masking here means that the value is ignored so we set none.
|
||||
alarm.AlarmMask = RTC_ALARMMASK_NONE;
|
||||
} else {
|
||||
// Masking here means that the value is ignored so we set them all. Only the subseconds
|
||||
// value matters.
|
||||
alarm.AlarmMask = RTC_ALARMMASK_ALL;
|
||||
}
|
||||
|
||||
alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 -
|
||||
((raw_ticks % TICK_DIVISOR) * 32);
|
||||
alarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
|
||||
alarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_SET;
|
||||
// Masking here means that the bits are ignored so we set none of them.
|
||||
alarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
|
||||
alarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
|
||||
alarm.Alarm = (alarm_idx == PERIPHERALS_ALARM_A) ? RTC_ALARM_A : RTC_ALARM_B;
|
||||
|
||||
HAL_RTC_SetAlarm_IT(&hrtc, &alarm, RTC_FORMAT_BIN);
|
||||
alarmed_already[alarm_idx] = false;
|
||||
}
|
||||
|
||||
bool stm32_peripherals_rtc_alarm_triggered(uint8_t alarm_idx) {
|
||||
return alarmed_already[alarm_idx];
|
||||
}
|
||||
|
||||
void RTC_WKUP_IRQHandler(void) {
|
||||
if (wkup_callback) {
|
||||
wkup_callback();
|
||||
}
|
||||
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
|
||||
__HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();
|
||||
hrtc.State = HAL_RTC_STATE_READY;
|
||||
}
|
||||
|
||||
void RTC_Alarm_IRQHandler(void) {
|
||||
HAL_RTC_AlarmIRQHandler(&hrtc);
|
||||
}
|
||||
|
||||
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *_hrtc) {
|
||||
if (alarm_callbacks[PERIPHERALS_ALARM_A]) {
|
||||
alarm_callbacks[PERIPHERALS_ALARM_A]();
|
||||
}
|
||||
HAL_RTC_DeactivateAlarm(_hrtc, RTC_ALARM_A);
|
||||
alarmed_already[PERIPHERALS_ALARM_A] = true;
|
||||
}
|
||||
|
||||
void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *_hrtc) {
|
||||
if (alarm_callbacks[PERIPHERALS_ALARM_B]) {
|
||||
alarm_callbacks[PERIPHERALS_ALARM_B]();
|
||||
}
|
||||
HAL_RTC_DeactivateAlarm(_hrtc, RTC_ALARM_B);
|
||||
alarmed_already[PERIPHERALS_ALARM_B] = true;
|
||||
}
|
||||
|
|
|
@ -22,4 +22,29 @@
|
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
*/
|
||||
|
||||
#ifndef __MICROPY_INCLUDED_STM32_PERIPHERALS_RTC_H__
|
||||
#define __MICROPY_INCLUDED_STM32_PERIPHERALS_RTC_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define PERIPHERALS_ALARM_A 0
|
||||
#define PERIPHERALS_ALARM_B 1
|
||||
|
||||
uint32_t stm32_peripherals_get_rtc_freq(void);
|
||||
void stm32_peripherals_rtc_init(void);
|
||||
uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t* subticks);
|
||||
|
||||
void stm32_peripherals_rtc_assign_wkup_callback(void(*callback)(void));
|
||||
void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds);
|
||||
void stm32_peripherals_rtc_set_wakeup_mode_tick(void);
|
||||
void stm32_peripherals_rtc_enable_wakeup_timer(void);
|
||||
void stm32_peripherals_rtc_disable_wakeup_timer(void);
|
||||
|
||||
void stm32_peripherals_rtc_assign_alarm_callback(uint8_t alarm_idx, void(*callback)(void)) ;
|
||||
void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks);
|
||||
bool stm32_peripherals_rtc_alarm_triggered(uint8_t alarm_idx);
|
||||
|
||||
#endif // __MICROPY_INCLUDED_STM32_PERIPHERALS_RTC_H__
|
||||
|
|
|
@ -45,17 +45,18 @@
|
|||
#include "common-hal/pwmio/PWMOut.h"
|
||||
#endif
|
||||
#if CIRCUITPY_PULSEIO || CIRCUITPY_PWMIO
|
||||
#include "timers.h"
|
||||
#include "peripherals/timers.h"
|
||||
#endif
|
||||
#if CIRCUITPY_SDIOIO
|
||||
#include "common-hal/sdioio/SDCard.h"
|
||||
#endif
|
||||
#if CIRCUITPY_PULSEIO || CIRCUITPY_ALARM
|
||||
#include "exti.h"
|
||||
#include "peripherals/exti.h"
|
||||
#endif
|
||||
|
||||
#include "clocks.h"
|
||||
#include "gpio.h"
|
||||
#include "peripherals/clocks.h"
|
||||
#include "peripherals/gpio.h"
|
||||
#include "peripherals/rtc.h"
|
||||
|
||||
#include STM32_HAL_H
|
||||
|
||||
|
@ -163,13 +164,6 @@ __attribute__((used, naked)) void Reset_Handler(void) {
|
|||
|
||||
// Low power clock variables
|
||||
static volatile uint32_t systick_ms;
|
||||
static RTC_HandleTypeDef _hrtc;
|
||||
|
||||
#if BOARD_HAS_LOW_SPEED_CRYSTAL
|
||||
static uint32_t rtc_clock_frequency = LSE_VALUE;
|
||||
#else
|
||||
static uint32_t rtc_clock_frequency = LSI_VALUE;
|
||||
#endif
|
||||
|
||||
safe_mode_t port_init(void) {
|
||||
HAL_Init(); // Turns on SysTick
|
||||
|
@ -181,20 +175,7 @@ safe_mode_t port_init(void) {
|
|||
|
||||
stm32_peripherals_clocks_init();
|
||||
stm32_peripherals_gpio_init();
|
||||
|
||||
// RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
|
||||
__HAL_RCC_RTC_ENABLE();
|
||||
_hrtc.Instance = RTC;
|
||||
_hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
|
||||
// Divide async as little as possible so that we have rtc_clock_frequency count in subseconds.
|
||||
// This ensures our timing > 1 second is correct.
|
||||
_hrtc.Init.AsynchPrediv = 0x0;
|
||||
_hrtc.Init.SynchPrediv = rtc_clock_frequency - 1;
|
||||
_hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
|
||||
|
||||
HAL_RTC_Init(&_hrtc);
|
||||
HAL_RTCEx_EnableBypassShadow(&_hrtc);
|
||||
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
|
||||
stm32_peripherals_rtc_init();
|
||||
|
||||
// Turn off SysTick
|
||||
SysTick->CTRL = 0;
|
||||
|
@ -223,7 +204,6 @@ uint32_t HAL_GetTick() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void SysTick_Handler(void) {
|
||||
systick_ms += 1;
|
||||
// Read the CTRL register to clear the SysTick interrupt.
|
||||
|
@ -328,119 +308,26 @@ __attribute__((used)) void HardFault_Handler(void)
|
|||
}
|
||||
}
|
||||
|
||||
// This function is called often for timing so we cache the seconds elapsed computation based on the
|
||||
// register value. The STM HAL always does shifts and conversion if we use it directly.
|
||||
volatile uint32_t seconds_to_date = 0;
|
||||
volatile uint32_t cached_date = 0;
|
||||
volatile uint32_t seconds_to_minute = 0;
|
||||
volatile uint32_t cached_hours_minutes = 0;
|
||||
uint64_t port_get_raw_ticks(uint8_t* subticks) {
|
||||
// Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
|
||||
// SSR twice to make sure we didn't read across a tick.
|
||||
__disable_irq();
|
||||
uint32_t first_ssr = (uint32_t)(RTC->SSR);
|
||||
uint32_t time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK);
|
||||
uint32_t date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK);
|
||||
uint32_t ssr = (uint32_t)(RTC->SSR);
|
||||
while (ssr != first_ssr) {
|
||||
first_ssr = ssr;
|
||||
time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK);
|
||||
date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK);
|
||||
ssr = (uint32_t)(RTC->SSR);
|
||||
}
|
||||
__enable_irq();
|
||||
|
||||
uint32_t subseconds = rtc_clock_frequency - 1 - ssr;
|
||||
|
||||
if (date != cached_date) {
|
||||
uint32_t year = (uint8_t)((date & (RTC_DR_YT | RTC_DR_YU)) >> 16U);
|
||||
uint8_t month = (uint8_t)((date & (RTC_DR_MT | RTC_DR_MU)) >> 8U);
|
||||
uint8_t day = (uint8_t)(date & (RTC_DR_DT | RTC_DR_DU));
|
||||
// Add 2000 since the year is only the last two digits.
|
||||
year = 2000 + (uint32_t)RTC_Bcd2ToByte(year);
|
||||
month = (uint8_t)RTC_Bcd2ToByte(month);
|
||||
day = (uint8_t)RTC_Bcd2ToByte(day);
|
||||
seconds_to_date = timeutils_seconds_since_2000(year, month, day, 0, 0, 0);
|
||||
cached_date = date;
|
||||
}
|
||||
uint32_t hours_minutes = time & (RTC_TR_HT | RTC_TR_HU | RTC_TR_MNT | RTC_TR_MNU);
|
||||
if (hours_minutes != cached_hours_minutes) {
|
||||
uint8_t hours = (uint8_t)((time & (RTC_TR_HT | RTC_TR_HU)) >> 16U);
|
||||
uint8_t minutes = (uint8_t)((time & (RTC_TR_MNT | RTC_TR_MNU)) >> 8U);
|
||||
hours = (uint8_t)RTC_Bcd2ToByte(hours);
|
||||
minutes = (uint8_t)RTC_Bcd2ToByte(minutes);
|
||||
seconds_to_minute = 60 * (60 * hours + minutes);
|
||||
cached_hours_minutes = hours_minutes;
|
||||
}
|
||||
uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU));
|
||||
seconds = (uint8_t)RTC_Bcd2ToByte(seconds);
|
||||
if (subticks != NULL) {
|
||||
*subticks = subseconds % 32;
|
||||
}
|
||||
|
||||
uint64_t raw_ticks = ((uint64_t) 1024) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / 32;
|
||||
return raw_ticks;
|
||||
}
|
||||
|
||||
void RTC_WKUP_IRQHandler(void) {
|
||||
supervisor_tick();
|
||||
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&_hrtc, RTC_FLAG_WUTF);
|
||||
__HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();
|
||||
_hrtc.State = HAL_RTC_STATE_READY;
|
||||
}
|
||||
|
||||
volatile bool alarmed_already = false;
|
||||
void RTC_Alarm_IRQHandler(void) {
|
||||
HAL_RTC_DeactivateAlarm(&_hrtc, RTC_ALARM_A);
|
||||
__HAL_RTC_ALARM_EXTI_CLEAR_FLAG();
|
||||
__HAL_RTC_ALARM_CLEAR_FLAG(&_hrtc, RTC_FLAG_ALRAF);
|
||||
alarmed_already = true;
|
||||
return stm32_peripherals_rtc_raw_ticks(subticks);
|
||||
}
|
||||
|
||||
// Enable 1/1024 second tick.
|
||||
void port_enable_tick(void) {
|
||||
HAL_RTCEx_SetWakeUpTimer_IT(&_hrtc, rtc_clock_frequency / 1024 / 2, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
|
||||
HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 1, 0U);
|
||||
HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
|
||||
stm32_peripherals_rtc_set_wakeup_mode_tick();
|
||||
stm32_peripherals_rtc_assign_wkup_callback(supervisor_tick);
|
||||
stm32_peripherals_rtc_enable_wakeup_timer();
|
||||
}
|
||||
// TODO: what is this? can I get rid of it?
|
||||
extern volatile uint32_t autoreload_delay_ms;
|
||||
|
||||
// Disable 1/1024 second tick.
|
||||
void port_disable_tick(void) {
|
||||
HAL_NVIC_DisableIRQ(RTC_WKUP_IRQn);
|
||||
HAL_RTCEx_DeactivateWakeUpTimer(&_hrtc);
|
||||
stm32_peripherals_rtc_disable_wakeup_timer();
|
||||
}
|
||||
|
||||
void port_interrupt_after_ticks(uint32_t ticks) {
|
||||
uint64_t raw_ticks = port_get_raw_ticks(NULL) + ticks;
|
||||
|
||||
RTC_AlarmTypeDef alarm;
|
||||
if (ticks > 1024) {
|
||||
timeutils_struct_time_t tm;
|
||||
timeutils_seconds_since_2000_to_struct_time(raw_ticks / 1024, &tm);
|
||||
alarm.AlarmTime.Hours = tm.tm_hour;
|
||||
alarm.AlarmTime.Minutes = tm.tm_min;
|
||||
alarm.AlarmTime.Seconds = tm.tm_sec;
|
||||
alarm.AlarmDateWeekDay = tm.tm_mday;
|
||||
// Masking here means that the value is ignored so we set none.
|
||||
alarm.AlarmMask = RTC_ALARMMASK_NONE;
|
||||
} else {
|
||||
// Masking here means that the value is ignored so we set them all. Only the subseconds
|
||||
// value matters.
|
||||
alarm.AlarmMask = RTC_ALARMMASK_ALL;
|
||||
}
|
||||
|
||||
alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 -
|
||||
((raw_ticks % 1024) * 32);
|
||||
alarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
|
||||
alarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_SET;
|
||||
// Masking here means that the bits are ignored so we set none of them.
|
||||
alarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
|
||||
alarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
|
||||
alarm.Alarm = RTC_ALARM_A;
|
||||
|
||||
HAL_RTC_SetAlarm_IT(&_hrtc, &alarm, RTC_FORMAT_BIN);
|
||||
alarmed_already = false;
|
||||
stm32_peripherals_rtc_set_alarm(PERIPHERALS_ALARM_A, ticks);
|
||||
}
|
||||
|
||||
void port_idle_until_interrupt(void) {
|
||||
|
@ -449,7 +336,8 @@ void port_idle_until_interrupt(void) {
|
|||
__set_FPSCR(__get_FPSCR() & ~(0x9f));
|
||||
(void) __get_FPSCR();
|
||||
}
|
||||
if (alarmed_already) {
|
||||
// The alarm might have triggered before we even reach the WFI
|
||||
if (stm32_peripherals_rtc_alarm_triggered(PERIPHERALS_ALARM_A)) {
|
||||
return;
|
||||
}
|
||||
__WFI();
|
||||
|
|
|
@ -228,7 +228,7 @@ mp_obj_t alarm_get_wake_alarm(void) {
|
|||
}
|
||||
|
||||
// Initialize .wake_alarm value.
|
||||
void common_hal_alarm_save_wake_alarm(void) {
|
||||
void shared_alarm_save_wake_alarm(void) {
|
||||
// Equivalent of:
|
||||
// alarm.wake_alarm = alarm
|
||||
mp_map_elem_t *elem =
|
||||
|
|
|
@ -50,7 +50,7 @@ extern void common_hal_alarm_gc_collect(void);
|
|||
extern mp_obj_t common_hal_alarm_get_wake_alarm(void);
|
||||
|
||||
// Used by wake-up code.
|
||||
void common_hal_alarm_save_wake_alarm(void);
|
||||
void shared_alarm_save_wake_alarm(void);
|
||||
|
||||
|
||||
// True if an alarm is alerting. This is most useful for pretend deep sleep.
|
||||
|
|
Loading…
Reference in New Issue