/**
******************************************************************************
* @file stm32l1xx_hal_rcc_ex.c
* @author MCD Application Team
* @brief Extended RCC HAL module driver.
* This file provides firmware functions to manage the following
* functionalities RCC extension peripheral:
* + Extended Peripheral Control functions
*
******************************************************************************
* @attention
*
*
© Copyright(c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32l1xx_hal.h"
/** @addtogroup STM32L1xx_HAL_Driver
* @{
*/
#ifdef HAL_RCC_MODULE_ENABLED
/** @defgroup RCCEx RCCEx
* @brief RCC Extension HAL module driver
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/** @defgroup RCCEx_Private_Constants RCCEx Private Constants
* @{
*/
/**
* @}
*/
/* Private macro -------------------------------------------------------------*/
/** @defgroup RCCEx_Private_Macros RCCEx Private Macros
* @{
*/
/**
* @}
*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/** @defgroup RCCEx_Exported_Functions RCCEx Exported Functions
* @{
*/
/** @defgroup RCCEx_Exported_Functions_Group1 Extended Peripheral Control functions
* @brief Extended Peripheral Control functions
*
@verbatim
===============================================================================
##### Extended Peripheral Control functions #####
===============================================================================
[..]
This subsection provides a set of functions allowing to control the RCC Clocks
frequencies.
[..]
(@) Important 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) are set to their reset values.
@endverbatim
* @{
*/
/**
* @brief Initializes the RCC extended peripherals clocks according to the specified
* parameters in the RCC_PeriphCLKInitTypeDef.
* @param PeriphClkInit pointer to an RCC_PeriphCLKInitTypeDef structure that
* contains the configuration information for the Extended Peripherals clocks(RTC/LCD clock).
* @retval HAL status
* @note If HAL_ERROR returned, first switch-OFF HSE clock oscillator with @ref HAL_RCC_OscConfig()
* to possibly update HSE divider.
*/
HAL_StatusTypeDef HAL_RCCEx_PeriphCLKConfig(RCC_PeriphCLKInitTypeDef *PeriphClkInit)
{
uint32_t tickstart;
uint32_t temp_reg;
/* Check the parameters */
assert_param(IS_RCC_PERIPHCLOCK(PeriphClkInit->PeriphClockSelection));
/*------------------------------- RTC/LCD Configuration ------------------------*/
if ((((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_RTC) == RCC_PERIPHCLK_RTC)
#if defined(LCD)
|| (((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LCD) == RCC_PERIPHCLK_LCD)
#endif /* LCD */
)
{
/* check for RTC Parameters used to output RTCCLK */
if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_RTC) == RCC_PERIPHCLK_RTC)
{
assert_param(IS_RCC_RTCCLKSOURCE(PeriphClkInit->RTCClockSelection));
}
#if defined(LCD)
if(((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LCD) == RCC_PERIPHCLK_LCD)
{
assert_param(IS_RCC_RTCCLKSOURCE(PeriphClkInit->LCDClockSelection));
}
#endif /* LCD */
FlagStatus pwrclkchanged = RESET;
/* As soon as function is called to change RTC clock source, activation of the
power domain is done. */
/* Requires to enable write access to Backup Domain of necessary */
if(__HAL_RCC_PWR_IS_CLK_DISABLED())
{
__HAL_RCC_PWR_CLK_ENABLE();
pwrclkchanged = SET;
}
if(HAL_IS_BIT_CLR(PWR->CR, PWR_CR_DBP))
{
/* Enable write access to Backup domain */
SET_BIT(PWR->CR, PWR_CR_DBP);
/* Wait for Backup domain Write protection disable */
tickstart = HAL_GetTick();
while(HAL_IS_BIT_CLR(PWR->CR, PWR_CR_DBP))
{
if((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
/* Check if user wants to change HSE RTC prescaler whereas HSE is enabled */
temp_reg = (RCC->CR & RCC_CR_RTCPRE);
if ((temp_reg != (PeriphClkInit->RTCClockSelection & RCC_CR_RTCPRE))
#if defined (LCD)
|| (temp_reg != (PeriphClkInit->LCDClockSelection & RCC_CR_RTCPRE))
#endif /* LCD */
)
{ /* Check HSE State */
if ((PeriphClkInit->RTCClockSelection & RCC_CSR_RTCSEL) == RCC_CSR_RTCSEL_HSE)
{
if (HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSERDY))
{
/* To update HSE divider, first switch-OFF HSE clock oscillator*/
return HAL_ERROR;
}
}
}
/* Reset the Backup domain only if the RTC Clock source selection is modified from reset value */
temp_reg = (RCC->CSR & RCC_CSR_RTCSEL);
if((temp_reg != 0x00000000U) && (((temp_reg != (PeriphClkInit->RTCClockSelection & RCC_CSR_RTCSEL)) \
&& (((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_RTC) == RCC_PERIPHCLK_RTC))
#if defined(LCD)
|| ((temp_reg != (PeriphClkInit->LCDClockSelection & RCC_CSR_RTCSEL)) \
&& (((PeriphClkInit->PeriphClockSelection) & RCC_PERIPHCLK_LCD) == RCC_PERIPHCLK_LCD))
#endif /* LCD */
))
{
/* Store the content of CSR register before the reset of Backup Domain */
temp_reg = (RCC->CSR & ~(RCC_CSR_RTCSEL));
/* RTC Clock selection can be changed only if the Backup Domain is reset */
__HAL_RCC_BACKUPRESET_FORCE();
__HAL_RCC_BACKUPRESET_RELEASE();
/* Restore the Content of CSR register */
RCC->CSR = temp_reg;
/* Wait for LSERDY if LSE was enabled */
if (HAL_IS_BIT_SET(temp_reg, RCC_CSR_LSEON))
{
/* Get Start Tick */
tickstart = HAL_GetTick();
/* Wait till LSE is ready */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == 0U)
{
if((HAL_GetTick() - tickstart ) > RCC_LSE_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}
}
__HAL_RCC_RTC_CONFIG(PeriphClkInit->RTCClockSelection);
/* Require to disable power clock if necessary */
if(pwrclkchanged == SET)
{
__HAL_RCC_PWR_CLK_DISABLE();
}
}
return HAL_OK;
}
/**
* @brief Get the PeriphClkInit according to the internal RCC configuration registers.
* @param PeriphClkInit pointer to an RCC_PeriphCLKInitTypeDef structure that
* returns the configuration information for the Extended Peripherals clocks(RTC/LCD clocks).
* @retval None
*/
void HAL_RCCEx_GetPeriphCLKConfig(RCC_PeriphCLKInitTypeDef *PeriphClkInit)
{
uint32_t srcclk;
/* Set all possible values for the extended clock type parameter------------*/
PeriphClkInit->PeriphClockSelection = RCC_PERIPHCLK_RTC;
#if defined(LCD)
PeriphClkInit->PeriphClockSelection |= RCC_PERIPHCLK_LCD;
#endif /* LCD */
/* Get the RTC/LCD configuration -----------------------------------------------*/
srcclk = __HAL_RCC_GET_RTC_SOURCE();
if (srcclk != RCC_RTCCLKSOURCE_HSE_DIV2)
{
/* Source clock is LSE or LSI*/
PeriphClkInit->RTCClockSelection = srcclk;
}
else
{
/* Source clock is HSE. Need to get the prescaler value*/
PeriphClkInit->RTCClockSelection = srcclk | (READ_BIT(RCC->CR, RCC_CR_RTCPRE));
}
#if defined(LCD)
PeriphClkInit->LCDClockSelection = PeriphClkInit->RTCClockSelection;
#endif /* LCD */
}
/**
* @brief Return the peripheral clock frequency
* @note Return 0 if peripheral clock is unknown
* @param PeriphClk Peripheral clock identifier
* This parameter can be one of the following values:
* @arg @ref RCC_PERIPHCLK_RTC RTC peripheral clock
* @arg @ref RCC_PERIPHCLK_LCD LCD peripheral clock (*)
* @note (*) means that this peripheral is not present on all the devices
* @retval Frequency in Hz (0: means that no available frequency for the peripheral)
*/
uint32_t HAL_RCCEx_GetPeriphCLKFreq(uint32_t PeriphClk)
{
uint32_t frequency = 0;
uint32_t srcclk;
/* Check the parameters */
assert_param(IS_RCC_PERIPHCLOCK(PeriphClk));
switch (PeriphClk)
{
case RCC_PERIPHCLK_RTC:
#if defined(LCD)
case RCC_PERIPHCLK_LCD:
#endif /* LCD */
{
/* Get the current RTC source */
srcclk = __HAL_RCC_GET_RTC_SOURCE();
/* Check if LSE is ready if RTC clock selection is LSE */
if (srcclk == RCC_RTCCLKSOURCE_LSE)
{
if (HAL_IS_BIT_SET(RCC->CSR, RCC_CSR_LSERDY))
{
frequency = LSE_VALUE;
}
}
/* Check if LSI is ready if RTC clock selection is LSI */
else if (srcclk == RCC_RTCCLKSOURCE_LSI)
{
if (HAL_IS_BIT_SET(RCC->CSR, RCC_CSR_LSIRDY))
{
frequency = LSI_VALUE;
}
}
/* Check if HSE is ready and if RTC clock selection is HSE */
else if (srcclk == RCC_RTCCLKSOURCE_HSE_DIVX)
{
if (HAL_IS_BIT_SET(RCC->CR, RCC_CR_HSERDY))
{
/* Get the current HSE clock divider */
switch (__HAL_RCC_GET_RTC_HSE_PRESCALER())
{
case RCC_RTC_HSE_DIV_16: /* HSE DIV16 has been selected */
{
frequency = HSE_VALUE / 16U;
break;
}
case RCC_RTC_HSE_DIV_8: /* HSE DIV8 has been selected */
{
frequency = HSE_VALUE / 8U;
break;
}
case RCC_RTC_HSE_DIV_4: /* HSE DIV4 has been selected */
{
frequency = HSE_VALUE / 4U;
break;
}
default: /* HSE DIV2 has been selected */
{
frequency = HSE_VALUE / 2U;
break;
}
}
}
}
else
{
/* No clock source, frequency default init at 0 */
}
break;
}
default:
break;
}
return(frequency);
}
#if defined(RCC_LSECSS_SUPPORT)
/**
* @brief Enables the LSE Clock Security System.
* @note If a failure is detected on the external 32 kHz oscillator, the LSE clock is no longer supplied
* to the RTC but no hardware action is made to the registers.
* In Standby mode a wakeup is generated. In other modes an interrupt can be sent to wakeup
* the software (see Section 5.3.4: Clock interrupt register (RCC_CIR) on page 104).
* The software MUST then disable the LSECSSON bit, stop the defective 32 kHz oscillator
* (disabling LSEON), and can change the RTC clock source (no clock or LSI or HSE, with
* RTCSEL), or take any required action to secure the application.
* @note LSE CSS available only for high density and medium+ devices
* @retval None
*/
void HAL_RCCEx_EnableLSECSS(void)
{
*(__IO uint32_t *) CSR_LSECSSON_BB = (uint32_t)ENABLE;
}
/**
* @brief Disables the LSE Clock Security System.
* @note Once enabled this bit cannot be disabled, except after an LSE failure detection
* (LSECSSD=1). In that case the software MUST disable the LSECSSON bit.
* Reset by power on reset and RTC software reset (RTCRST bit).
* @note LSE CSS available only for high density and medium+ devices
* @retval None
*/
void HAL_RCCEx_DisableLSECSS(void)
{
/* Disable LSE CSS */
*(__IO uint32_t *) CSR_LSECSSON_BB = (uint32_t)DISABLE;
/* Disable LSE CSS IT */
__HAL_RCC_DISABLE_IT(RCC_IT_LSECSS);
}
/**
* @brief Enable the LSE Clock Security System IT & corresponding EXTI line.
* @note LSE Clock Security System IT is mapped on RTC EXTI line 19
* @retval None
*/
void HAL_RCCEx_EnableLSECSS_IT(void)
{
/* Enable LSE CSS */
*(__IO uint32_t *) CSR_LSECSSON_BB = (uint32_t)ENABLE;
/* Enable LSE CSS IT */
__HAL_RCC_ENABLE_IT(RCC_IT_LSECSS);
/* Enable IT on EXTI Line 19 */
__HAL_RCC_LSECSS_EXTI_ENABLE_IT();
__HAL_RCC_LSECSS_EXTI_ENABLE_RISING_EDGE();
}
/**
* @brief Handle the RCC LSE Clock Security System interrupt request.
* @retval None
*/
void HAL_RCCEx_LSECSS_IRQHandler(void)
{
/* Check RCC LSE CSSF flag */
if(__HAL_RCC_GET_IT(RCC_IT_LSECSS))
{
/* RCC LSE Clock Security System interrupt user callback */
HAL_RCCEx_LSECSS_Callback();
/* Clear RCC LSE CSS pending bit */
__HAL_RCC_CLEAR_IT(RCC_IT_LSECSS);
}
}
/**
* @brief RCCEx LSE Clock Security System interrupt callback.
* @retval none
*/
__weak void HAL_RCCEx_LSECSS_Callback(void)
{
/* NOTE : This function should not be modified, when the callback is needed,
the @ref HAL_RCCEx_LSECSS_Callback should be implemented in the user file
*/
}
#endif /* RCC_LSECSS_SUPPORT */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif /* HAL_RCC_MODULE_ENABLED */
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/