diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 2a117c8023..e992f545ba 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -121,6 +121,7 @@ Q(recv) Q(RTC) Q(info) Q(datetime) +Q(wakeup) // for Pin class Q(Pin) diff --git a/stmhal/rtc.c b/stmhal/rtc.c index 82bb61bbff..2bbe5b10cf 100644 --- a/stmhal/rtc.c +++ b/stmhal/rtc.c @@ -372,9 +372,123 @@ mp_obj_t pyb_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_datetime_obj, 1, 2, pyb_rtc_datetime); +// wakeup(None) +// wakeup(ms, callback=None) +// wakeup(wucksel, wut, callback) +mp_obj_t pyb_rtc_wakeup(mp_uint_t n_args, const mp_obj_t *args) { + // wut is wakeup counter start value, wucksel is clock source + // counter is decremented at wucksel rate, and wakes the MCU when it gets to 0 + // wucksel=0b000 is RTC/16 (RTC runs at 32768Hz) + // wucksel=0b001 is RTC/8 + // wucksel=0b010 is RTC/4 + // wucksel=0b011 is RTC/2 + // wucksel=0b100 is 1Hz clock + // wucksel=0b110 is 1Hz clock with 0x10000 added to wut + // so a 1 second wakeup could be wut=2047, wucksel=0b000, or wut=4095, wucksel=0b001, etc + + // disable wakeup IRQ while we configure it + HAL_NVIC_DisableIRQ(RTC_WKUP_IRQn); + + bool enable = false; + mp_int_t wucksel; + mp_int_t wut; + mp_obj_t callback = mp_const_none; + if (n_args <= 3) { + if (args[1] == mp_const_none) { + // disable wakeup + } else { + // time given in ms + mp_int_t ms = mp_obj_get_int(args[1]); + mp_int_t div = 2; + wucksel = 3; + while (div <= 16 && ms > 2000 * div) { + div *= 2; + wucksel -= 1; + } + if (div <= 16) { + wut = 32768 / div * ms / 1000; + } else { + wucksel = 4; + wut = ms / 1000; + if (ms > 0x10000) { + wucksel = 5; + ms -= 0x10000; + if (ms > 0x10000) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "wakeup value too large")); + } + } + } + wut -= 1; + enable = true; + } + if (n_args == 3) { + callback = args[2]; + } + } else { + // config values given directly + wucksel = mp_obj_get_int(args[1]); + wut = mp_obj_get_int(args[2]); + callback = args[3]; + enable = true; + } + + // set the callback + MP_STATE_PORT(pyb_extint_callback)[22] = callback; + + // disable register write protection + RTC->WPR = 0xca; + RTC->WPR = 0x53; + + // clear WUTE + RTC->CR &= ~(1 << 10); + + // wait until WUTWF is set + while (!(RTC->ISR & (1 << 2))) { + } + + if (enable) { + // program WUT + RTC->WUTR = wut; + + // set WUTIE to enable wakeup interrupts + // set WUTE to enable wakeup + // program WUCKSEL + RTC->CR |= (1 << 14) | (1 << 10) | (wucksel & 7); + + // enable register write protection + RTC->WPR = 0xff; + + // enable external interrupts on line 22 + EXTI->IMR |= 1 << 22; + EXTI->RTSR |= 1 << 22; + + // clear interrupt flags + RTC->ISR &= ~(1 << 10); + EXTI->PR = 1 << 22; + + HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 0x0f, 0x0f); + HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn); + + //printf("wut=%d wucksel=%d\n", wut, wucksel); + } else { + // clear WUTIE to disable interrupts + RTC->CR &= ~(1 << 14); + + // enable register write protection + RTC->WPR = 0xff; + + // disable external interrupts on line 22 + EXTI->IMR &= ~(1 << 22); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_wakeup_obj, 2, 4, pyb_rtc_wakeup); + STATIC const mp_map_elem_t pyb_rtc_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_info), (mp_obj_t)&pyb_rtc_info_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_datetime), (mp_obj_t)&pyb_rtc_datetime_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_wakeup), (mp_obj_t)&pyb_rtc_wakeup_obj }, }; STATIC MP_DEFINE_CONST_DICT(pyb_rtc_locals_dict, pyb_rtc_locals_dict_table); diff --git a/stmhal/stm32f4xx_it.c b/stmhal/stm32f4xx_it.c index f06c4081c3..9be180a051 100644 --- a/stmhal/stm32f4xx_it.c +++ b/stmhal/stm32f4xx_it.c @@ -341,7 +341,8 @@ void TAMP_STAMP_IRQHandler(void) { } void RTC_WKUP_IRQHandler(void) { - Handle_EXTI_Irq(EXTI_RTC_WAKEUP); + RTC->ISR &= ~(1 << 10); // clear wakeup interrupt flag + Handle_EXTI_Irq(EXTI_RTC_WAKEUP); // clear EXTI flag and execute optional callback } void TIM1_BRK_TIM9_IRQHandler(void) {