From 86aa16bea61da8a8d156a7a928902a0bf4c85b73 Mon Sep 17 00:00:00 2001 From: T S Date: Sat, 7 Nov 2015 12:03:12 +0100 Subject: [PATCH] stmhal: Implement delayed RTC initialization with LSI fallback. If RTC is already running at boot then it's left alone. Otherwise, RTC is started at boot but startup function returns straight away. RTC startup is then finished the first time it is used. Fallback to LSI if LSE fails to start in a certain time. Also included: MICROPY_HW_CLK_LAST_FREQ hold pyb.freq() parameters in RTC backup reg MICROPY_HW_RTC_USE_US option to present datetime sub-seconds in microseconds MICROPY_HW_RTC_USE_CALOUT option to enable RTC calibration output CLK_LAST_FREQ and RTC_USE_CALOUT are enabled for PYBv1.0. --- stmhal/boards/PYBV10/mpconfigboard.h | 3 + stmhal/diskio.c | 1 + stmhal/main.c | 2 +- stmhal/modmachine.c | 23 ++ stmhal/modutime.c | 2 + stmhal/rtc.c | 415 +++++++++++++++++---------- stmhal/rtc.h | 3 +- stmhal/system_stm32.c | 77 +++-- 8 files changed, 358 insertions(+), 168 deletions(-) diff --git a/stmhal/boards/PYBV10/mpconfigboard.h b/stmhal/boards/PYBV10/mpconfigboard.h index 7aa9804986..fece733308 100644 --- a/stmhal/boards/PYBV10/mpconfigboard.h +++ b/stmhal/boards/PYBV10/mpconfigboard.h @@ -22,9 +22,12 @@ #define MICROPY_HW_CLK_PLLN (336) #define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) #define MICROPY_HW_CLK_PLLQ (7) +#define MICROPY_HW_CLK_LAST_FREQ (1) // The pyboard has a 32kHz crystal for the RTC #define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (0) +#define MICROPY_HW_RTC_USE_CALOUT (1) // UART config #define MICROPY_HW_UART1_NAME "XB" diff --git a/stmhal/diskio.c b/stmhal/diskio.c index a15dce1aa5..8c8c8ee15b 100644 --- a/stmhal/diskio.c +++ b/stmhal/diskio.c @@ -279,6 +279,7 @@ DWORD get_fattime ( void ) { + rtc_init_finalise(); RTC_TimeTypeDef time; RTC_DateTypeDef date; HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); diff --git a/stmhal/main.c b/stmhal/main.c index f6d6ada703..ffbd6cfa96 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -422,7 +422,7 @@ soft_reset: #if MICROPY_HW_ENABLE_RTC if (first_soft_reset) { - rtc_init(); + rtc_init_start(); } #endif diff --git a/stmhal/modmachine.c b/stmhal/modmachine.c index c495573fb1..25977e789c 100644 --- a/stmhal/modmachine.c +++ b/stmhal/modmachine.c @@ -39,6 +39,7 @@ #include "pin.h" #include "timer.h" #include "usb.h" +#include "rtc.h" #include "i2c.h" #include "spi.h" @@ -281,6 +282,9 @@ STATIC mp_obj_t machine_freq(mp_uint_t n_args, const mp_obj_t *args) { } else { RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; } + uint32_t h = RCC_ClkInitStruct.AHBCLKDivider >> 4; + uint32_t b1 = RCC_ClkInitStruct.APB1CLKDivider >> 10; + uint32_t b2 = RCC_ClkInitStruct.APB2CLKDivider >> 10; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) { goto fail; } @@ -312,6 +316,23 @@ STATIC mp_obj_t machine_freq(mp_uint_t n_args, const mp_obj_t *args) { // re-init TIM3 for USB CDC rate timer_tim3_init(); + #if defined(MICROPY_HW_CLK_LAST_FREQ) && MICROPY_HW_CLK_LAST_FREQ + #if defined(MCU_SERIES_F7) + #define FREQ_BKP BKP31R + #else + #define FREQ_BKP BKP19R + #endif + // qqqqqqqq pppppppp nnnnnnnn nnmmmmmm + // qqqqQQQQ ppppppPP nNNNNNNN NNMMMMMM + // 222111HH HHQQQQPP nNNNNNNN NNMMMMMM + p = (p / 2) - 1; + RTC->FREQ_BKP = m + | (n << 6) | (p << 16) | (q << 18) + | (h << 22) + | (b1 << 26) + | (b2 << 29); + #endif + return mp_const_none; fail:; @@ -349,6 +370,8 @@ STATIC mp_obj_t machine_sleep(void) { MP_DEFINE_CONST_FUN_OBJ_0(machine_sleep_obj, machine_sleep); STATIC mp_obj_t machine_deepsleep(void) { + rtc_init_finalise(); + #if defined(MCU_SERIES_F7) printf("machine.deepsleep not supported yet\n"); #else diff --git a/stmhal/modutime.c b/stmhal/modutime.c index 8892295582..8502e0fd62 100644 --- a/stmhal/modutime.c +++ b/stmhal/modutime.c @@ -57,6 +57,7 @@ STATIC mp_obj_t time_localtime(mp_uint_t n_args, const mp_obj_t *args) { if (n_args == 0 || args[0] == mp_const_none) { // get current date and time // note: need to call get time then get date to correctly access the registers + rtc_init_finalise(); RTC_DateTypeDef date; RTC_TimeTypeDef time; HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); @@ -119,6 +120,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); STATIC mp_obj_t time_time(void) { // get date and time // note: need to call get time then get date to correctly access the registers + rtc_init_finalise(); RTC_DateTypeDef date; RTC_TimeTypeDef time; HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); diff --git a/stmhal/rtc.c b/stmhal/rtc.c index 561548ee49..8fdb112504 100644 --- a/stmhal/rtc.c +++ b/stmhal/rtc.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "rtc.h" #include "irq.h" +#include "mphalport.h" /// \moduleref pyb /// \class RTC - real time clock @@ -52,112 +53,18 @@ static mp_uint_t rtc_info; // Note: LSI is around (32KHz), these dividers should work either way // ck_spre(1Hz) = RTCCLK(LSE) /(uwAsynchPrediv + 1)*(uwSynchPrediv + 1) +// modify RTC_ASYNCH_PREDIV & RTC_SYNCH_PREDIV in board//mpconfigport.h to change sub-second ticks +// default is 3906.25 µs, min is ~30.52 µs (will increas Ivbat by ~500nA) +#ifndef RTC_ASYNCH_PREDIV #define RTC_ASYNCH_PREDIV (0x7f) +#endif +#ifndef RTC_SYNCH_PREDIV #define RTC_SYNCH_PREDIV (0x00ff) - -#if 0 -#define RTC_INFO_USE_EXISTING (0) -#define RTC_INFO_USE_LSE (1) -#define RTC_INFO_USE_LSI (3) - -void rtc_init(void) { - // Enable the PWR clock - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); - - // Allow access to RTC - PWR_BackupAccessCmd(ENABLE); - - if (RTC_ReadBackupRegister(RTC_BKP_DR0) == 0x32F2) { - // RTC still alive, so don't re-init it - // wait for RTC APB register synchronisation - RTC_WaitForSynchro(); - rtc_info = RTC_INFO_USE_EXISTING; - return; - } - - uint32_t timeout = 10000000; - - // Enable the PWR clock - RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); - - // Allow access to RTC - PWR_BackupAccessCmd(ENABLE); - - // Enable the LSE OSC - RCC_LSEConfig(RCC_LSE_ON); - - // Wait till LSE is ready - mp_uint_t sys_tick = sys_tick_counter; - while((RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) && (--timeout > 0)) { - } - - // record how long it took for the RTC to start up - rtc_info = (sys_tick_counter - sys_tick) << 2; - - // If LSE timed out, use LSI instead - if (timeout == 0) { - // Disable the LSE OSC - RCC_LSEConfig(RCC_LSE_OFF); - - // Enable the LSI OSC - RCC_LSICmd(ENABLE); - - // Wait till LSI is ready - while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) { - } - - // Use LSI as the RTC Clock Source - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); - - // record that we are using the LSI - rtc_info |= RTC_INFO_USE_LSI; - } else { - // Use LSE as the RTC Clock Source - RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); - - // record that we are using the LSE - rtc_info |= RTC_INFO_USE_LSE; - } - - // Note: LSI is around (32KHz), these dividers should work either way - // ck_spre(1Hz) = RTCCLK(LSE) /(uwAsynchPrediv + 1)*(uwSynchPrediv + 1) - uint32_t uwSynchPrediv = 0xFF; - uint32_t uwAsynchPrediv = 0x7F; - - // Enable the RTC Clock - RCC_RTCCLKCmd(ENABLE); - - // Wait for RTC APB registers synchronisation - RTC_WaitForSynchro(); - - // Configure the RTC data register and RTC prescaler - RTC_InitTypeDef RTC_InitStructure; - RTC_InitStructure.RTC_AsynchPrediv = uwAsynchPrediv; - RTC_InitStructure.RTC_SynchPrediv = uwSynchPrediv; - RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; - RTC_Init(&RTC_InitStructure); - - // Set the date (BCD) - RTC_DateTypeDef RTC_DateStructure; - RTC_DateStructure.RTC_Year = 0x13; - RTC_DateStructure.RTC_Month = RTC_Month_October; - RTC_DateStructure.RTC_Date = 0x26; - RTC_DateStructure.RTC_WeekDay = RTC_Weekday_Saturday; - RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure); - - // Set the time (BCD) - RTC_TimeTypeDef RTC_TimeStructure; - RTC_TimeStructure.RTC_H12 = RTC_H12_AM; - RTC_TimeStructure.RTC_Hours = 0x01; - RTC_TimeStructure.RTC_Minutes = 0x53; - RTC_TimeStructure.RTC_Seconds = 0x00; - RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure); - - // Indicator for the RTC configuration - RTC_WriteBackupRegister(RTC_BKP_DR0, 0x32F2); -} #endif +STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc); +STATIC void PYB_RTC_MspInit_Kick(RTC_HandleTypeDef *hrtc, bool rtc_use_lse); +STATIC HAL_StatusTypeDef PYB_RTC_MspInit_Finalise(RTC_HandleTypeDef *hrtc); STATIC void RTC_CalendarConfig(void); #if defined(MICROPY_HW_RTC_USE_LSE) && MICROPY_HW_RTC_USE_LSE @@ -165,8 +72,40 @@ STATIC bool rtc_use_lse = true; #else STATIC bool rtc_use_lse = false; #endif +STATIC uint32_t rtc_startup_tick; +STATIC bool rtc_need_init_finalise = false; -void rtc_init(void) { +// check if LSE exists +// not well tested, should probably be removed +STATIC bool lse_magic(void) { +#if 0 + uint32_t mode_in = GPIOC->MODER & 0x3fffffff; + uint32_t mode_out = mode_in | 0x40000000; + GPIOC->MODER = mode_out; + GPIOC->OTYPER &= 0x7fff; + GPIOC->BSRRH = 0x8000; + GPIOC->OSPEEDR &= 0x3fffffff; + GPIOC->PUPDR &= 0x3fffffff; + int i = 0xff0; + __IO int d = 0; + uint32_t tc = 0; + __IO uint32_t j; + while (i) { + GPIOC->MODER = mode_out; + GPIOC->MODER = mode_in; + for (j = 0; j < d; j++) ; + i--; + if ((GPIOC->IDR & 0x8000) == 0) { + tc++; + } + } + return (tc < 0xff0)?true:false; +#else + return false; +#endif +} + +void rtc_init_start(void) { RTCHandle.Instance = RTC; /* Configure RTC prescaler and RTC data registers */ @@ -184,6 +123,7 @@ void rtc_init(void) { RTCHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; RTCHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; + rtc_need_init_finalise = false; if ((RCC->BDCR & (RCC_BDCR_LSEON | RCC_BDCR_LSERDY)) == (RCC_BDCR_LSEON | RCC_BDCR_LSERDY)) { // LSE is enabled & ready --> no need to (re-)init RTC // remove Backup Domain write protection @@ -193,8 +133,8 @@ void rtc_init(void) { // provide some status information rtc_info |= 0x40000 | (RCC->BDCR & 7) | (RCC->CSR & 3) << 8; return; - } else if ((RCC->BDCR & RCC_BDCR_RTCSEL) == RCC_BDCR_RTCSEL_1) { - // LSI is already active + } else if (((RCC->BDCR & RCC_BDCR_RTCSEL) == RCC_BDCR_RTCSEL_1) && ((RCC->CSR & 3) == 3)) { + // LSI configured & enabled & ready --> no need to (re-)init RTC // remove Backup Domain write protection HAL_PWR_EnableBkUpAccess(); // Clear source Reset Flag @@ -204,16 +144,33 @@ void rtc_init(void) { rtc_info |= 0x80000 | (RCC->BDCR & 7) | (RCC->CSR & 3) << 8; return; } + rtc_startup_tick = HAL_GetTick(); + rtc_info = 0x3f000000 | (rtc_startup_tick & 0xffffff); + if (rtc_use_lse) { + if (lse_magic()) { + // don't even try LSE + rtc_use_lse = false; + rtc_info &= ~0x01000000; + } + } + PYB_RTC_MspInit_Kick(&RTCHandle, rtc_use_lse); +} - mp_uint_t tick = HAL_GetTick(); +void rtc_init_finalise() { + if (!rtc_need_init_finalise) { + return; + } - if (HAL_RTC_Init(&RTCHandle) != HAL_OK) { + rtc_info = 0x20000000 | (rtc_use_lse << 28); + if (PYB_RTC_Init(&RTCHandle) != HAL_OK) { if (rtc_use_lse) { // fall back to LSI... rtc_use_lse = false; + rtc_startup_tick = HAL_GetTick(); + PYB_RTC_MspInit_Kick(&RTCHandle, rtc_use_lse); HAL_PWR_EnableBkUpAccess(); RTCHandle.State = HAL_RTC_STATE_RESET; - if (HAL_RTC_Init(&RTCHandle) != HAL_OK) { + if (PYB_RTC_Init(&RTCHandle) != HAL_OK) { rtc_info = 0x0100ffff; // indicate error return; } @@ -225,11 +182,10 @@ void rtc_init(void) { } // record how long it took for the RTC to start up - rtc_info = HAL_GetTick() - tick; + rtc_info |= (HAL_GetTick() - rtc_startup_tick) & 0xffff; // fresh reset; configure RTC Calendar RTC_CalendarConfig(); - if(__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET) { // power on reset occurred rtc_info |= 0x10000; @@ -240,46 +196,118 @@ void rtc_init(void) { } // Clear source Reset Flag __HAL_RCC_CLEAR_RESET_FLAGS(); + rtc_need_init_finalise = false; } -STATIC void RTC_CalendarConfig(void) { - // set the date to 1st Jan 2014 - RTC_DateTypeDef date; - date.Year = 14; - date.Month = 1; - date.Date = 1; - date.WeekDay = RTC_WEEKDAY_WEDNESDAY; - - if(HAL_RTC_SetDate(&RTCHandle, &date, FORMAT_BIN) != HAL_OK) { - // init error - return; +STATIC HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct) { + /*------------------------------ LSI Configuration -------------------------*/ + if ((RCC_OscInitStruct->OscillatorType & RCC_OSCILLATORTYPE_LSI) == RCC_OSCILLATORTYPE_LSI) { + // Check the LSI State + if (RCC_OscInitStruct->LSIState != RCC_LSI_OFF) { + // Enable the Internal Low Speed oscillator (LSI). + __HAL_RCC_LSI_ENABLE(); + } else { + // Disable the Internal Low Speed oscillator (LSI). + __HAL_RCC_LSI_DISABLE(); + } } - // set the time to 00:00:00 - RTC_TimeTypeDef time; - time.Hours = 0; - time.Minutes = 0; - time.Seconds = 0; - time.TimeFormat = RTC_HOURFORMAT12_AM; - time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; - time.StoreOperation = RTC_STOREOPERATION_RESET; + /*------------------------------ LSE Configuration -------------------------*/ + if ((RCC_OscInitStruct->OscillatorType & RCC_OSCILLATORTYPE_LSE) == RCC_OSCILLATORTYPE_LSE) { + // Enable Power Clock + __PWR_CLK_ENABLE(); + HAL_PWR_EnableBkUpAccess(); + uint32_t tickstart = HAL_GetTick(); + + #if defined(MCU_SERIES_F7) + //__HAL_RCC_PWR_CLK_ENABLE(); + // Enable write access to Backup domain + //PWR->CR1 |= PWR_CR1_DBP; + // Wait for Backup domain Write protection disable + while ((PWR->CR1 & PWR_CR1_DBP) == RESET) { + if (HAL_GetTick() - tickstart > RCC_DBP_TIMEOUT_VALUE) { + return HAL_TIMEOUT; + } + } + #else + // Enable write access to Backup domain + //PWR->CR |= PWR_CR_DBP; + // Wait for Backup domain Write protection disable + while ((PWR->CR & PWR_CR_DBP) == RESET) { + if (HAL_GetTick() - tickstart > DBP_TIMEOUT_VALUE) { + return HAL_TIMEOUT; + } + } + #endif - if (HAL_RTC_SetTime(&RTCHandle, &time, FORMAT_BIN) != HAL_OK) { - // init error - return; + // Set the new LSE configuration + __HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState); } + + return HAL_OK; } -/* - Note: Care must be taken when HAL_RCCEx_PeriphCLKConfig() is used to select - the RTC clock source; in this case the Backup domain will be reset in - order to modify the RTC Clock source, as consequence RTC registers (including - the backup registers) and RCC_BDCR register are set to their reset values. -*/ -void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc) { - RCC_OscInitTypeDef RCC_OscInitStruct; - RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; +STATIC HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { + // Check the RTC peripheral state + if (hrtc == NULL) { + return HAL_ERROR; + } + if (hrtc->State == HAL_RTC_STATE_RESET) { + // Allocate lock resource and initialize it + hrtc->Lock = HAL_UNLOCKED; + // Initialize RTC MSP + if (PYB_RTC_MspInit_Finalise(hrtc) != HAL_OK) { + return HAL_ERROR; + } + } + + // Set RTC state + hrtc->State = HAL_RTC_STATE_BUSY; + + // Disable the write protection for RTC registers + __HAL_RTC_WRITEPROTECTION_DISABLE(hrtc); + + // Set Initialization mode + if (RTC_EnterInitMode(hrtc) != HAL_OK) { + // Enable the write protection for RTC registers + __HAL_RTC_WRITEPROTECTION_ENABLE(hrtc); + + // Set RTC state + hrtc->State = HAL_RTC_STATE_ERROR; + + return HAL_ERROR; + } else { + // Clear RTC_CR FMT, OSEL and POL Bits + hrtc->Instance->CR &= ((uint32_t)~(RTC_CR_FMT | RTC_CR_OSEL | RTC_CR_POL)); + // Set RTC_CR register + hrtc->Instance->CR |= (uint32_t)(hrtc->Init.HourFormat | hrtc->Init.OutPut | hrtc->Init.OutPutPolarity); + + // Configure the RTC PRER + hrtc->Instance->PRER = (uint32_t)(hrtc->Init.SynchPrediv); + hrtc->Instance->PRER |= (uint32_t)(hrtc->Init.AsynchPrediv << 16); + + // Exit Initialization mode + hrtc->Instance->ISR &= (uint32_t)~RTC_ISR_INIT; + + #if defined(MCU_SERIES_F7) + hrtc->Instance->OR &= (uint32_t)~RTC_OR_ALARMTYPE; + hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType); + #else + hrtc->Instance->TAFCR &= (uint32_t)~RTC_TAFCR_ALARMOUTTYPE; + hrtc->Instance->TAFCR |= (uint32_t)(hrtc->Init.OutPutType); + #endif + + // Enable the write protection for RTC registers + __HAL_RTC_WRITEPROTECTION_ENABLE(hrtc); + + // Set RTC state + hrtc->State = HAL_RTC_STATE_READY; + + return HAL_OK; + } +} +STATIC void PYB_RTC_MspInit_Kick(RTC_HandleTypeDef *hrtc, bool rtc_use_lse) { /* To change the source clock of the RTC feature (LSE, LSI), You have to: - Enable the power clock using __PWR_CLK_ENABLE() - Enable write access using HAL_PWR_EnableBkUpAccess() function before to @@ -291,6 +319,7 @@ void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc) { // RTC clock source uses LSE (external crystal) only if relevant // configuration variable is set. Otherwise it uses LSI (internal osc). + RCC_OscInitTypeDef RCC_OscInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (rtc_use_lse) { @@ -300,11 +329,36 @@ void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc) { RCC_OscInitStruct.LSEState = RCC_LSE_OFF; RCC_OscInitStruct.LSIState = RCC_LSI_ON; } - if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { - //Error_Handler(); - return; + PYB_RCC_OscConfig(&RCC_OscInitStruct); + + // now ramp up osc. in background and flag calendear init needed + rtc_need_init_finalise = true; +} + +#define PYB_LSE_TIMEOUT_VALUE 1000 // ST docs spec 2000 ms LSE startup, seems to be too pessimistic +#define PYB_LSI_TIMEOUT_VALUE 500 // this is way too pessimistic, typ. < 1ms + +STATIC HAL_StatusTypeDef PYB_RTC_MspInit_Finalise(RTC_HandleTypeDef *hrtc) { + // we already had a kick so now wait for the corresponding ready state... + if (rtc_use_lse) { + // we now have to wait for LSE ready or timeout + uint32_t tickstart = rtc_startup_tick; + while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) { + if ((HAL_GetTick() - tickstart ) > PYB_LSE_TIMEOUT_VALUE) { + return HAL_TIMEOUT; + } + } + } else { + // we now have to wait for LSI ready or timeout + uint32_t tickstart = rtc_startup_tick; + while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET) { + if ((HAL_GetTick() - tickstart ) > PYB_LSI_TIMEOUT_VALUE) { + return HAL_TIMEOUT; + } + } } + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; if (rtc_use_lse) { PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; @@ -313,15 +367,40 @@ void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc) { } if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { //Error_Handler(); - return; + return HAL_ERROR; } // enable RTC peripheral clock __HAL_RCC_RTC_ENABLE(); + return HAL_OK; } -void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc) { - __HAL_RCC_RTC_DISABLE(); +STATIC void RTC_CalendarConfig(void) { + // set the date to 1st Jan 2015 + RTC_DateTypeDef date; + date.Year = 15; + date.Month = 1; + date.Date = 1; + date.WeekDay = RTC_WEEKDAY_THURSDAY; + + if(HAL_RTC_SetDate(&RTCHandle, &date, FORMAT_BIN) != HAL_OK) { + // init error + return; + } + + // set the time to 00:00:00 + RTC_TimeTypeDef time; + time.Hours = 0; + time.Minutes = 0; + time.Seconds = 0; + time.TimeFormat = RTC_HOURFORMAT12_AM; + time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + time.StoreOperation = RTC_STOREOPERATION_RESET; + + if (HAL_RTC_SetTime(&RTCHandle, &time, FORMAT_BIN) != HAL_OK) { + // init error + return; + } } /******************************************************************************/ @@ -369,7 +448,25 @@ MP_DEFINE_CONST_FUN_OBJ_1(pyb_rtc_info_obj, pyb_rtc_info); /// `weekday` is 1-7 for Monday through Sunday. /// /// `subseconds` counts down from 255 to 0 + +#define MEG_DIV_64 (1000000 / 64) +#define MEG_DIV_SCALE ((RTC_SYNCH_PREDIV + 1) / 64) + +#if defined(MICROPY_HW_RTC_USE_US) && MICROPY_HW_RTC_USE_US +uint32_t rtc_subsec_to_us(uint32_t ss) { + return ((RTC_SYNCH_PREDIV - ss) * MEG_DIV_64) / MEG_DIV_SCALE; +} + +uint32_t rtc_us_to_subsec(uint32_t us) { + return RTC_SYNCH_PREDIV - (us * MEG_DIV_SCALE / MEG_DIV_64); +} +#else +#define rtc_us_to_subsec +#define rtc_subsec_to_us +#endif + mp_obj_t pyb_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { + rtc_init_finalise(); if (n_args == 1) { // get date and time // note: need to call get time then get date to correctly access the registers @@ -385,7 +482,7 @@ mp_obj_t pyb_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { mp_obj_new_int(time.Hours), mp_obj_new_int(time.Minutes), mp_obj_new_int(time.Seconds), - mp_obj_new_int(time.SubSeconds), + mp_obj_new_int(rtc_subsec_to_us(time.SubSeconds)), }; return mp_obj_new_tuple(8, tuple); } else { @@ -404,7 +501,7 @@ mp_obj_t pyb_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { time.Hours = mp_obj_get_int(items[4]); time.Minutes = mp_obj_get_int(items[5]); time.Seconds = mp_obj_get_int(items[6]); - time.SubSeconds = mp_obj_get_int(items[7]); + time.SubSeconds = rtc_us_to_subsec(mp_obj_get_int(items[7])); time.TimeFormat = RTC_HOURFORMAT12_AM; time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; time.StoreOperation = RTC_STOREOPERATION_SET; @@ -429,6 +526,8 @@ mp_obj_t pyb_rtc_wakeup(mp_uint_t n_args, const mp_obj_t *args) { // 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 + rtc_init_finalise(); + // disable wakeup IRQ while we configure it HAL_NVIC_DisableIRQ(RTC_WKUP_IRQn); @@ -539,13 +638,31 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_wakeup_obj, 2, 4, pyb_rtc_wakeup); // When an integer argument is provided, check that it falls in the range [-511 to 512] // and set the calibration value; otherwise return calibration value mp_obj_t pyb_rtc_calibration(mp_uint_t n_args, const mp_obj_t *args) { + rtc_init_finalise(); mp_int_t cal; if (n_args == 2) { cal = mp_obj_get_int(args[1]); mp_uint_t cal_p, cal_m; if (cal < -511 || cal > 512) { +#if defined(MICROPY_HW_RTC_USE_CALOUT) && MICROPY_HW_RTC_USE_CALOUT + if ((cal & 0xfffe) == 0x0ffe) { + // turn on/off X18 (PC13) 512Hz output + // Note: + // Output will stay active even in VBAT mode (and inrease current) + if (cal & 1) { + HAL_RTCEx_SetCalibrationOutPut(&RTCHandle, RTC_CALIBOUTPUT_512HZ); + } else { + HAL_RTCEx_DeactivateCalibrationOutPut(&RTCHandle); + } + return mp_obj_new_int(cal & 1); + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "calibration value out of range")); + } +#else nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "calibration value out of range")); +#endif } if (cal > 0) { cal_p = RTC_SMOOTHCALIB_PLUSPULSES_SET; diff --git a/stmhal/rtc.h b/stmhal/rtc.h index 6aacd653c6..c8f215c053 100644 --- a/stmhal/rtc.h +++ b/stmhal/rtc.h @@ -27,4 +27,5 @@ extern RTC_HandleTypeDef RTCHandle; extern const mp_obj_type_t pyb_rtc_type; -void rtc_init(void); +void rtc_init_start(void); +void rtc_init_finalise(void); diff --git a/stmhal/system_stm32.c b/stmhal/system_stm32.c index 22c917adcb..e286a15725 100644 --- a/stmhal/system_stm32.c +++ b/stmhal/system_stm32.c @@ -269,15 +269,66 @@ void SystemClock_Config(void) regarding system frequency refer to product datasheet. */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); - /* Enable HSE Oscillator and activate PLL with HSE as source */ - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; - RCC_OscInitStruct.HSEState = RCC_HSE_ON; - RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = MICROPY_HW_CLK_PLLM; - RCC_OscInitStruct.PLL.PLLN = MICROPY_HW_CLK_PLLN; - RCC_OscInitStruct.PLL.PLLP = MICROPY_HW_CLK_PLLP; - RCC_OscInitStruct.PLL.PLLQ = MICROPY_HW_CLK_PLLQ; + /* Enable HSE Oscillator and activate PLL with HSE as source */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 + clocks dividers */ + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + +#if defined(MICROPY_HW_CLK_LAST_FREQ) && MICROPY_HW_CLK_LAST_FREQ + #if defined(MCU_SERIES_F7) + #define FREQ_BKP BKP31R + #else + #define FREQ_BKP BKP19R + #endif + uint32_t m = RTC->FREQ_BKP; + uint32_t n; + uint32_t p; + uint32_t q; + + // 222111HH HHQQQQPP nNNNNNNN NNMMMMMM + uint32_t h = (m >> 22) & 0xf; + uint32_t b1 = (m >> 26) & 0x7; + uint32_t b2 = (m >> 29) & 0x7; + q = (m >> 18) & 0xf; + p = (((m >> 16) & 0x03)+1)*2; + n = (m >> 6) & 0x3ff; + m &= 0x3f; + if ((q < 2) || (q > 15) || (p > 8) || (p < 2) || (n < 192) || (n >= 433) || (m < 2)) { + m = MICROPY_HW_CLK_PLLM; + n = MICROPY_HW_CLK_PLLN; + p = MICROPY_HW_CLK_PLLP; + q = MICROPY_HW_CLK_PLLQ; + h = RCC_SYSCLK_DIV1; + b1 = RCC_HCLK_DIV4; + b2 = RCC_HCLK_DIV2; + } else { + h <<= 4; + b1 <<= 10; + b2 <<= 10; + } + RCC_OscInitStruct.PLL.PLLM = m; //MICROPY_HW_CLK_PLLM; + RCC_OscInitStruct.PLL.PLLN = n; //MICROPY_HW_CLK_PLLN; + RCC_OscInitStruct.PLL.PLLP = p; //MICROPY_HW_CLK_PLLP; + RCC_OscInitStruct.PLL.PLLQ = q; //MICROPY_HW_CLK_PLLQ; + + RCC_ClkInitStruct.AHBCLKDivider = h; //RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = b1; //RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = b2; //RCC_HCLK_DIV2; +#else + RCC_OscInitStruct.PLL.PLLM = MICROPY_HW_CLK_PLLM; + RCC_OscInitStruct.PLL.PLLN = MICROPY_HW_CLK_PLLN; + RCC_OscInitStruct.PLL.PLLP = MICROPY_HW_CLK_PLLP; + RCC_OscInitStruct.PLL.PLLQ = MICROPY_HW_CLK_PLLQ; + + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; +#endif if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { __fatal_error("HAL_RCC_OscConfig"); @@ -291,14 +342,6 @@ void SystemClock_Config(void) } #endif - /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 - clocks dividers */ - RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); - RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; - RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; - RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; - RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; - #if !defined(MICROPY_HW_FLASH_LATENCY) #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_5 #endif