/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */ /************************************************************************************ ** ** ** mcHF QRP Transceiver ** ** K Atanassov - M0NKA 2014 ** ** ** **---------------------------------------------------------------------------------** ** ** ** Licence: GNU GPLv3 ** ************************************************************************************/ #include "uhsdr_board.h" #include "uhsdr_rtc.h" #ifdef STM32F4 #include "stm32f4xx_hal_rtc.h" #include "stm32f4xx_hal_rtc_ex.h" #include "stm32f4xx_hal_rcc.h" #elif defined(STM32H7) #include "stm32h7xx_hal_rtc.h" #include "stm32h7xx_hal_rtc_ex.h" #include "stm32h7xx_hal_rcc.h" #elif defined(STM32F7) #include "stm32f7xx_hal_rtc.h" #include "stm32f7xx_hal_rtc_ex.h" #include "stm32f7xx_hal_rcc.h" #endif #include "rtc.h" /* Private macros */ /* Internal status registers for RTC */ #define RTC_PRESENCE_REG RTC_BKP_DR1 // previously we used DR0 which is also used by the HAL Layer, so we move to DR1 #define RTC_PRESENCE_INIT_VAL 0x0001 // if we find this value after power on, we assume battery is available #define RTC_PRESENCE_OK_VAL 0x0002 // then we set this value to rembember a clock is present //#define RTC_PRESENCE_ACK_VAL 0x0003 // if we find this value after power on, we assume user enabled RTC //#define RTC_PRESENCE_NACK_VAL 0x0004 // if we find this value after power on, we assume user decided against using RTC #ifdef USE_RTC_LSE static void RTC_LSE_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.LSEState = RCC_LSE_ON; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; HAL_RCC_OscConfig(&RCC_OscInitStruct); HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); __HAL_RCC_RTC_ENABLE(); HAL_RTCEx_BKUPWrite(&hrtc,RTC_PRESENCE_REG,RTC_PRESENCE_OK_VAL); } void Rtc_FullReset() { __HAL_RCC_BACKUPRESET_FORCE(); } #if 0 static void RTC_LSI_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; RCC_OscInitStruct.LSIState = RCC_LSI_ON; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; HAL_RCC_OscConfig(&RCC_OscInitStruct); HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); __HAL_RCC_RTC_ENABLE(); HAL_RTCEx_BKUPWrite(&hrtc,RTC_PRESENCE_REG,RTC_PRESENCE_OK_VAL); } #endif static void Rtc_StartInternal(bool doClock) { if (doClock) { // ok, there is a battery, so let us now start the oscillator RTC_LSE_Config(); } // very first start of rtc hrtc.Instance = RTC; hrtc.Init.HourFormat = RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv = 127; hrtc.Init.SynchPrediv = 255; hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; HAL_RTC_Init(&hrtc); } void Rtc_Start() { Rtc_StartInternal(true); } #endif bool Rtc_isEnabled() { bool retval = false; #ifdef USE_RTC_LSE // FIXME: H7 Port #ifndef STM32H7 __HAL_RCC_PWR_CLK_ENABLE(); #else __HAL_RCC_RTC_CLK_ENABLE(); #endif HAL_PWR_EnableBkUpAccess(); __HAL_RCC_RTC_ENABLE(); if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) != RESET) { // we detected most likely a running RTC or a system capable of running an RTC since the external oscillator is running; retval = true; } switch(HAL_RTCEx_BKUPRead(&hrtc,RTC_PRESENCE_REG)) { case 0: // 0 -> cleared backup ram -> no battery or reset // if we find the RTC_PRESENCE_INIT_VAL in the backup register next time we boot // we know there is a battery present. HAL_RTCEx_BKUPWrite(&hrtc,RTC_PRESENCE_REG,RTC_PRESENCE_INIT_VAL); break; case RTC_PRESENCE_OK_VAL: retval = true; ts.vbat_present = true; break; case RTC_PRESENCE_INIT_VAL: ts.vbat_present = true; break; default: // TODO: Anything else is a problem, since who wrote a different value? Not this code! break; } #endif return retval; } bool Rtc_SetPpm(int16_t ppm) { bool retval = false; if (ppm >= RTC_CALIB_PPM_MIN && ppm <= RTC_CALIB_PPM_MAX) { uint32_t calm; uint32_t calp; float64_t ppm2pulses = rint((float64_t)ppm * 1.048576); // = (32 * 32768) / 1000.0000 if (ppm2pulses <= 0.0) // important, we must make sure to not set calp if 0ppm { calm = - ppm2pulses; calp = RTC_SMOOTHCALIB_PLUSPULSES_RESET; } else { calm = 512 - ppm2pulses; if (calm > 512) { calm = 0; } calp = RTC_SMOOTHCALIB_PLUSPULSES_SET; } HAL_RTCEx_SetSmoothCalib(&hrtc,RTC_SMOOTHCALIB_PERIOD_32SEC,calp,calm); retval = true; } return retval; } /** * @brief Gets RTC current time. THIS A replacement for the not working HAL_RTC_GetTime() function/ * @param hrtc: pointer to a RTC_HandleTypeDef structure that contains * the configuration information for RTC. * @param sTime: Pointer to Time structure * @param Format: Specifies the format of the entered parameters. * This parameter can be one of the following values: * @arg RTC_FORMAT_BIN: Binary data format * @arg RTC_FORMAT_BCD: BCD data format * @note You cannot use SubSeconds etc since these are not read due to an apparent bug in some STM32F407 silicon. * On my STM32F407 reading the SSR will for sure stop the RTC shadow registers and cause also other troubles. * Probably different hardware does not have this problem but we don't need subseconds anyway. * @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values * in the higher-order calendar shadow registers to ensure consistency between the time and date values. * Reading RTC current time locks the values in calendar shadow registers until current date is read. * @retval HAL status */ // we need this to keep the compiler happy and keep the read of the DR static volatile uint32_t dr_dummy ; void Rtc_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format) { uint32_t tmpreg = 0U; /* Check the parameters */ assert_param(IS_RTC_FORMAT(Format)); /* Get subseconds structure field from the corresponding register */ // See above: sTime->SubSeconds = (uint32_t)(hrtc->Instance->SSR); //sTime->SubSeconds = 0; /* Get SecondFraction structure field from the corresponding register field*/ sTime->SecondFraction = (uint32_t)(hrtc->Instance->PRER & RTC_PRER_PREDIV_S); // sTime->SecondFraction = 0; /* Get the TR register */ tmpreg = (uint32_t)(hrtc->Instance->TR & RTC_TR_RESERVED_MASK); dr_dummy = (uint32_t)hrtc->Instance->DR; /* Fill the structure fields with the read parameters */ sTime->Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16U); sTime->Minutes = (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >>8U); sTime->Seconds = (uint8_t)(tmpreg & (RTC_TR_ST | RTC_TR_SU)); sTime->TimeFormat = (uint8_t)((tmpreg & (RTC_TR_PM)) >> 16U); /* Check the input parameters format */ if(Format == RTC_FORMAT_BIN) { /* Convert the time structure parameters to Binary format */ sTime->Hours = (uint8_t)RTC_Bcd2ToByte(sTime->Hours); sTime->Minutes = (uint8_t)RTC_Bcd2ToByte(sTime->Minutes); sTime->Seconds = (uint8_t)RTC_Bcd2ToByte(sTime->Seconds); } }