/*!
 * @file        apm32f0xx_rcm.h
 *
 * @brief       This file provides all the RCM firmware functions
 *
 * @version     V1.0.4
 *
 * @date        2025-07-07
 *
 * @attention
 *
 *  Copyright (C) 2020-2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "apm32f0xx_rcm.h"

/** @addtogroup APM32F0xx_StdPeriphDriver
  @{
*/

#if defined(RCM)

/** @addtogroup RCM_Driver RCM Driver
  @{
*/

/** @defgroup RCM_Macros Macros
  @{
*/

/**@} end of group RCM_Macros*/

/** @defgroup RCM_Enumerations Enumerations
  @{
*/

/**@} end of group RCM_Enumerations*/

/** @defgroup RCM_Structures Structures
  @{
*/

/**@} end of group RCM_Structures*/

/** @defgroup RCM_Variables Variables
  @{
*/

/**@} end of group RCM_Variables*/

/** @defgroup RCM_Functions Functions
  @{
*/

/*!
 * @brief   Resets the clock configuration
 *
 * @param   None
 *
 * @retval  None
 */
void RCM_Reset(void)
{
    /** Set HSIEN bit */
    RCM->CTRL1_B.HSIEN = BIT_SET;
    RCM->CFG1 &= (uint32_t)0x08FFB80C;
    RCM->CTRL1 &= (uint32_t)0xFEF6FFFF;
    RCM->CTRL1_B.HSEBCFG = BIT_RESET;
    RCM->CFG1 &= (uint32_t)0xFFC0FFFF;
    RCM->CFG2 &= (uint32_t)0xFFFFFFF0;
    RCM->CFG3 &= (uint32_t)0xFFF0FEAC;
    RCM->CTRL2_B.HSI14EN = BIT_RESET;

    /** Disable all interrupts and clear pending bits */
    RCM->INT = 0x00FF0000;
}

/*!
 * @brief       Configures HSE
 *
 * @param       state:   state of the HSE
 *                       This parameter can be one of the following values:
 *                       @arg RCM_HSE_OPEN: turn ON the HSE oscillator
 *                       @arg RCM_HSE_BYPASS: HSE oscillator bypassed with external clock
 *
 * @retval      None
 *
 * @note        HSE can not be stopped if it is used directly or through the PLL as system clock
 */
void RCM_ConfigHSE(RCM_HSE_T state)
{
    RCM->CTRL1_B.HSEEN = BIT_RESET;

    RCM->CTRL1_B.HSEBCFG = BIT_RESET;

    if (state == RCM_HSE_OPEN)
    {
        RCM->CTRL1_B.HSEEN = BIT_SET;
    }
    else if (state == RCM_HSE_BYPASS)
    {
        RCM->CTRL1_B.HSEBCFG = BIT_SET;
        RCM->CTRL1_B.HSEEN = BIT_SET;
    }
}

/*!
 * @brief       Waits for HSE be ready
 *
 * @param       None
 *
 * @retval      SUCCESS     HSE oscillator is stable and ready to use
 *              ERROR       HSE oscillator not yet ready
 */
uint8_t RCM_WaitHSEReady(void)
{
    __IO uint32_t cnt;

    for (cnt = 0; cnt < HSE_STARTUP_TIMEOUT; cnt++)
    {
        if (RCM->CTRL1_B.HSERDYFLG == BIT_SET)
        {
            return SUCCESS;
        }
    }

    return ERROR;
}

/*!
 * @brief       Set HSI trimming value
 *
* @param        HSITrim:    HSI trimming value
 *                          This parameter must be a number between 0 and 0x1F.
 *
 * @retval      None
 */
void RCM_SetHSITrim(uint8_t HSITrim)
{
    RCM->CTRL1_B.HSITRM = HSITrim;
}

/*!
 * @brief       Enable HSI
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        HSI can not be stopped if it is used directly or through the PLL as system clock
 */
void RCM_EnableHSI(void)
{
    RCM->CTRL1_B.HSIEN = BIT_SET;
}

/*!
 * @brief       Disable HSI
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        HSI can not be stopped if it is used directly or through the PLL as system clock
 */
void RCM_DisableHSI(void)
{
    RCM->CTRL1_B.HSIEN = BIT_RESET;
}

/*!
 * @brief       Set HSI14 trimming value
 *
 * @param        HSI14TRM:  HSI trimming value
 *                            This parameter must be a number between 0 and 0x1F.
 *
 * @retval      None
 */
void RCM_SetHSI14Trim(uint8_t HSI14Trim)
{
    RCM->CTRL2_B.HSI14TRM = HSI14Trim;
}

/*!
 * @brief       Enable HSI14
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void RCM_EnableHSI14(void)
{
    RCM->CTRL2_B.HSI14EN = BIT_SET;
}

/*!
 * @brief       Disable HSI14
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void RCM_DisableHSI14(void)
{
    RCM->CTRL2_B.HSI14EN = BIT_RESET;
}

/*!
 * @brief       Enable HSI14 ADC
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void RCM_EnableHSI14ADC(void)
{
    RCM->CTRL2_B.HSI14TO = BIT_RESET;
}

/*!
 * @brief       Disable HSI14 ADC
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void RCM_DisableHSI14ADC(void)
{
    RCM->CTRL2_B.HSI14TO = BIT_SET;
}

/*!
 * @brief       Configures LSE
 *
 * @param       state:   state of the LSE
 *                       This parameter can be one of the following values:
 *                       @arg RCM_LSE_OPEN: turn ON the HSE oscillator
 *                       @arg RCM_LSE_BYPASS: HSE oscillator bypassed with external clock
 *
 * @retval      None
 *
 * @note        LSE can not be stopped if it is used directly or through the PLL as system clock
 */
void RCM_ConfigLSE(RCM_LSE_T state)
{
    RCM->BDCTRL_B.LSEEN = BIT_RESET;

    RCM->BDCTRL_B.LSEBCFG = BIT_RESET;

    if (state == RCM_LSE_OPEN)
    {
        RCM->BDCTRL_B.LSEEN = BIT_SET;
    }
    else if (state == RCM_LSE_BYPASS)
    {
        RCM->BDCTRL_B.LSEBCFG = BIT_SET;
        RCM->BDCTRL_B.LSEEN = BIT_SET;
    }
}

/*!
 * @brief       Configures LSE drive capability
 *
 * @param       state:   State of the LSE Drive
 *                       This parameter can be one of the following values:
 *                       @arg RCM_LSE_DRIVE_Low: LSE oscillator low drive capability.
 *                       @arg RCM_LSE_DRIVE_MediumLow: LSE oscillator medium low drive capability.
 *                       @arg RCM_LSE_DRIVE_MediumHigh: LSE oscillator medium high drive capability.
 *                       @arg RCM_LSE_DRIVE_High: LSE oscillator high drive capability.
 *
 * @retval      None
 *
 * @note        LSE can not be stopped if it is used directly or through the PLL as system clock
 */
void RCM_ConfigDriveLSE(RCM_LSE_DRIVE_T state)
{
    RCM->BDCTRL_B.LSEDRVCFG = state;
}

/*!
 * @brief       Enable LSI
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        LSI can not be stopped if the IWDT is running
 */
void RCM_EnableLSI(void)
{
    RCM->CSTS_B.LSIEN = BIT_SET;
}

/*!
 * @brief       Disable LSI
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        LSI can not be stopped if the IWDT is running
 */
void RCM_DisableLSI(void)
{
    RCM->CSTS_B.LSIEN = BIT_RESET;
}

/*!
 * @brief       Configures the PLL clock source and multiplication factor
 *
 * @param       pllSelect:   PLL entry clock source select
 *                           This parameter can be one of the following values:
 *                           @arg RCM_PLL_SEL_HSI_DIV2: HSI clock divided by 2 selected as PLL clock source
 *                           @arg RCM_PLL_SEL_HSE: HSE/CLKDIV1 selected as PLL clock entry
 *                           @arg RCM_PLL_SEL_HSI48: HSI48 oscillator clock selected as PLL clock source, It's only for 072 and 091 devices
 *                           @arg RCM_PLL_SEL_HSI: HSI clock selected as PLL clock entry, It's only for 072 and 091 devices
 *
 * @param       pllMf:       PLL multiplication factor
 *                           This parameter can be RCM_PLLMF_x where x:[2,16]
 *
 * @retval      None
 *
 * @note
 */
void RCM_ConfigPLL(RCM_PLL_SEL_T pllSelect, RCM_PLLMF_T pllMf)
{
    RCM->CFG1_B.PLLMULCFG = pllMf;
    RCM->CFG1_B.PLLSRCSEL = pllSelect;
}

/*!
 * @brief       Enables PLL
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        The PLL can not be disabled if it is used as system clock
 */
void RCM_EnablePLL(void)
{
    RCM->CTRL1_B.PLLEN = BIT_SET;
}

/*!
 * @brief       Disable PLL
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        The PLL can not be disabled if it is used as system clock
 */
void RCM_DisablePLL(void)
{
    RCM->CTRL1_B.PLLEN = BIT_RESET;
}

#if defined(APM32F072) || defined(APM32F091)
/*!
 * @brief       Enables HSI48
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        It's only for APM32F072 and APM32F091 devices
 */
void RCM_EnableHSI48(void)
{
    RCM->CTRL2_B.HSI48EN = BIT_SET;
}

/*!
 * @brief       Enables HSI48
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        It's only for APM32F072 and APM32F091 devices
 */
void RCM_DisableHSI48(void)
{
    RCM->CTRL2_B.HSI48EN = BIT_RESET;
}

/*!
 * @brief       Read HSI48 Calibration Value
 *
 * @param       None
 *
 * @retval      Return of HSI48 Value
 */
uint32_t RCM_ReadHSI48CalibrationValue(void)
{
    uint32_t calValue;

    calValue = RCM->CTRL2_B.HSI48CAL;

    return calValue;
}
#endif /* APM32F072 || APM32F091 */

/*!
 * @brief       Configures the CLK division factor
 *
 * @param       state:   specifies the PLLDIVCFG clock division factor.
 *                       This parameter can be RCM_CLK_Divx where x:[1,16]
 *
 * @retval      None
 *
 * @note        This function must be used only when the PLL is disabled
 */
void RCM_ConfigCLKDIV(RCM_CLK_DIV_T state)
{
    RCM->CFG2_B.PLLDIVCFG = state;
}

/*!
 * @brief        Enable Clock Security System
 *
 * @param        None
 *
 * @retval       None
 */
void RCM_EnableCCS(void)
{
    RCM->CTRL1_B.CSSEN = BIT_SET;
}

/*!
 * @brief        Disable Clock Security System
 *
 * @param        None
 *
 * @retval       None
 */
void RCM_DisableCCS(void)
{
    RCM->CTRL1_B.CSSEN = BIT_RESET;
}

#if defined (APM32F030) || defined (APM32F051)
/*!
 * @brief       Selects clock ouput source
 *
 * @param       cocClock:  specifies the clock source to output
 *                         This parameter can be one of the following values:
 *                         @arg RCM_COC_NO_CLOCK:     No clock selected.
 *                         @arg RCM_COC_HSI14:        HSI14 oscillator clock selected.
 *                         @arg RCM_COC_LSI:          LSI oscillator clock selected.
 *                         @arg RCM_COC_LSE:          LSE oscillator clock selected.
 *                         @arg RCM_COC_SYSCLK:       System clock selected.
 *                         @arg RCM_COC_HSI:          HSI oscillator clock selected.
 *                         @arg RCM_COC_HSE:          HSE oscillator clock selected.
 *                         @arg RCM_COC_PLLCLK_DIV_2: PLL clock divided by 2 selected.
 *
 * @retval      None
 */
void RCM_ConfigCOC(RCM_COCCLK_T cocClock)
{
    RCM->CFG1_B.MCOSEL = cocClock;
}
#else

/*!
 * @brief       Selects clock ouput source
 *
 * @param       cocClock:  specifies the clock source to output
 *                         This parameter can be one of the following values:
 *                         @arg RCM_COC_NO_CLOCK:     No clock selected.
 *                         @arg RCM_COC_HSI14:        HSI14 oscillator clock selected.
 *                         @arg RCM_COC_LSI:          LSI oscillator clock selected.
 *                         @arg RCM_COC_LSE:          LSE oscillator clock selected.
 *                         @arg RCM_COC_SYSCLK:       System clock selected.
 *                         @arg RCM_COC_HSI:          HSI oscillator clock selected.
 *                         @arg RCM_COC_HSE:          HSE oscillator clock selected.
 *                         @arg RCM_COC_PLLCLK_DIV_2: PLL clock divided by 2 selected.
 *                         @arg RCM_COC_PLLCLK:       PLL clock divided selected.
 *                         @arg RCM_COC_HSI48:        HSI48 oscillator clock selected.
 *
 * @param       divided:   specifies the prescaler on COC pin.
 *                         This parameter can only be the following value:
 *                         @arg RCM_COC_DIV_1:        MCOSEL clock is divided by 1.
 *                         @arg RCM_COC_DIV_2:        MCOSEL clock is divided by 2.
 *                         @arg RCM_COC_DIV_4:        MCOSEL clock is divided by 4.
 *                         @arg RCM_COC_DIV_8:        MCOSEL clock is divided by 8.
 *                         @arg RCM_COC_DIV_16:       MCOSEL clock is divided by 16.
 *                         @arg RCM_COC_DIV_32:       MCOSEL clock is divided by 32.
 *                         @arg RCM_COC_DIV_64:       MCOSEL clock is divided by 64.
 *                         @arg RCM_COC_DIV_128:      MCOSEL clock is divided by 128.
 *
 * @retval      None
 */
void RCM_ConfigCOC(RCM_COCCLK_T cocClock, RCM_COCPRE_T divided)
{
    RCM->CFG1_B.MCOPSC = divided;

    if (cocClock != RCM_COC_PLLCLK)
    {
        RCM->CFG1_B.MCOPLLPSC = BIT_RESET;
        RCM->CFG1_B.MCOSEL = cocClock;
    }
    else
    {
        RCM->CFG1_B.MCOPLLPSC = BIT_SET;
        RCM->CFG1_B.MCOSEL = 0x07;
    }
}
#endif /* APM32F072 */

/*!
 * @brief       Configures the system clock
 *
 * @param       sysClkSelect:   specifies the clock source used as system clock
 *                              This parameter can be one of the following values:
 *                              @arg RCM_SYSCLK_SEL_HSI:    HSI selected as system clock source
 *                              @arg RCM_SYSCLK_SEL_HSE:    HSE selected as system clock source
 *                              @arg RCM_SYSCLK_SEL_PLL:    PLL selected as system clock source
 *                              @arg RCM_SYSCLK_SEL_HSI48:  HSI48 selected as system clock source,It's only for 072 devices
 *
 * @retval      None
 */
void RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_T sysClkSelect)
{
    RCM->CFG1_B.SCLKSEL = sysClkSelect;
}

/*!
 * @brief       returns the clock source used as system clock
 *
 * @param       None
 *
 * @retval      The clock source used as system clock
 */
RCM_SYSCLK_SEL_T RCM_ReadSYSCLKSource(void)
{
    RCM_SYSCLK_SEL_T sysClock;

    sysClock = (RCM_SYSCLK_SEL_T)RCM->CFG1_B.SCLKSWSTS;

    return sysClock;
}

/*!
 * @brief       Configures the AHB clock
 *
 * @param       AHBDiv:   AHB divider number. This clock is derived from the system clock (SYSCLK)
 *                        This parameter can be one of the following values:
 *                        @arg RCM_SYSCLK_DIV_1:   AHB clock = SYSCLK
 *                        @arg RCM_SYSCLK_DIV_2:   AHB clock = SYSCLK/2
 *                        @arg RCM_SYSCLK_DIV_4:   AHB clock = SYSCLK/4
 *                        @arg RCM_SYSCLK_DIV_8:   AHB clock = SYSCLK/8
 *                        @arg RCM_SYSCLK_DIV_16:  AHB clock = SYSCLK/16
 *                        @arg RCM_SYSCLK_DIV_64:  AHB clock = SYSCLK/64
 *                        @arg RCM_SYSCLK_DIV_128: AHB clock = SYSCLK/128
 *                        @arg RCM_SYSCLK_DIV_256: AHB clock = SYSCLK/256
 *                        @arg RCM_SYSCLK_DIV_512: AHB clock = SYSCLK/512
 *
 * @retval      None
 */
void RCM_ConfigAHB(RCM_AHBDIV_T AHBDiv)
{
    RCM->CFG1_B.AHBPSC = AHBDiv;
}

/*!
 * @brief       Configures the APB clock
 *
 * @param       APBDiv:   defines the APB clock divider. This clock is derived from the AHB clock (HCLK)
 *                        This parameter can be one of the following values:
 *                        @arg RCM_HCLK_DIV_1:  APB clock = HCLK
 *                        @arg RCM_HCLK_DIV_2:  APB clock = HCLK/2
 *                        @arg RCM_HCLK_DIV_4:  APB clock = HCLK/4
 *                        @arg RCM_HCLK_DIV_8:  APB clock = HCLK/8
 *                        @arg RCM_HCLK_DIV_16: APB clock = HCLK/16
 *
 * @retval      None
 */
void RCM_ConfigAPB(RCM_APBDIV_T APBDiv)
{
    RCM->CFG1_B.APB1PSC = APBDiv;
}

#if defined(APM32F051) || defined(APM32F072) || defined(APM32F091)
/*!
 * @brief       Configures the CEC clock
 *
 * @param       CECClk:   defines the CEC clock divider. This clock is derived
 *                        from the HSI/244 (32768Hz) clock or LSI clock
 *                        This parameter can be one of the following values:
 *                        @arg RCM_CECCLK_HSI_DIV_224: CEC clock = HSI/244 (32768Hz)
 *                        @arg RCM_CECCLK_LSI_DIV: CEC clock = LSI
 *
 * @retval      None
 */
void RCM_ConfigCECCLK(RCM_CECCLK_T CECClk)
{
    RCM->CFG3_B.CECSEL = CECClk;
}
#endif /* APM32F051 || APM32F072 || APM32F091 */

/*!
 * @brief       Configures the I2C clock
 *
 * @param       I2CClk:   defines the I2C1 clock source. This clock is derived
 *                        from the HSI or System clock.
 *                        This parameter can be one of the following values:
 *                        @arg RCM_I2C1CLK_HSI:    I2C1 clock = HSI
 *                        @arg RCM_I2C1CLK_SYSCLK: I2C1 clock = System Clock
 *
 * @retval      None
 */
void RCM_ConfigI2CCLK(RCM_I2CCLK_T I2CClk)
{
    RCM->CFG3_B.I2C1SEL = I2CClk;
}

/*!
 * @brief       Configures the USART clock (USARTCLK)
 *
 * @param       USARTClk: defines the USART clock source. This clock is derived
 *                        from the HSI or System clock.
 *                        This parameter can be one of the following values:
 *                        @arg RCM_USART1CLK_PCLK:   USART1 clock = APB Clock (PCLK)
 *                        @arg RCM_USART1CLK_SYSCLK: USART1 clock = System Clock
 *                        @arg RCM_USART1CLK_LSE:    USART1 clock = LSE Clock
 *                        @arg RCM_USART1CLK_HSI:    USART1 clock = HSI Clock
 *                        Under it's only for APM32F072 and APM32F091 devices
 *                        @arg RCM_USART2CLK_PCLK:   USART2 clock = APB Clock (PCLK)
 *                        @arg RCM_USART2CLK_SYSCLK: USART2 clock = System Clock
 *                        @arg RCM_USART2CLK_LSE:    USART2 clock = LSE Clock
 *                        @arg RCM_USART2CLK_HSI:    USART2 clock = HSI Clock
 *                        Under it's only for APM32F091 devices
 *                        @arg RCM_USART3CLK_PCLK:   USART3 clock = APB Clock (PCLK)
 *                        @arg RCM_USART3CLK_SYSCLK: USART3 clock = System Clock
 *                        @arg RCM_USART3CLK_LSE:    USART3 clock = LSE Clock
 *                        @arg RCM_USART3CLK_HSI:    USART3 clock = HSI Clock
 *
 * @retval      None
 */
void RCM_ConfigUSARTCLK(RCM_USARTCLK_T USARTClk)
{
    if (USARTClk >> 16 == 1)
    {
        RCM->CFG3_B.USART1SEL = (uint32_t)(USARTClk & 0x00000003);
    }
#if defined(APM32F072) || defined(APM32F091)
    else if (USARTClk >> 17 == 1)
    {
        RCM->CFG3_B.USART2SEL = (uint32_t)(USARTClk & 0x00000003);
    }
#endif /* APM32F072 || APM32F091 */
#if defined(APM32F091)
    else
    {
        RCM->CFG3_B.USART3SEL = (uint32_t)(USARTClk & 0x00000003);
    }
#endif /* APM32F091 */
}

#if defined(APM32F072)
/*!
 * @brief       Configures the USB clock (USBCLK)
 *
 * @param       USBClk: defines the USB clock source. This clock is derived
 *                      from the HSI48 or System clock.
 *                      This parameter can be one of the following values:
 *                      @arg RCM_USBCLK_HSI48:  USB clock = HSI48
 *                      @arg RCM_USBCLK_PLLCLK: USB clock = PLL Clock
 *
 * @retval      None
 *
 * @note        It's only for APM32F072 devices
 */
void RCM_ConfigUSBCLK(RCM_USBCLK_T USBClk)
{
    RCM->CFG3_B.USBDSEL = USBClk;
}
#endif /* APM32F072 */

/*!
 * @brief       Read frequency of SYSCLK
 *
 * @param       None
 *
 * @retval      Return frequency of SYSCLK
 */
uint32_t RCM_ReadSYSCLKFreq(void)
{
    uint32_t sysClock, pllMull, plldiv, pllSource;

    sysClock = RCM->CFG1_B.SCLKSEL;

    switch (sysClock)
    {
        case RCM_SYSCLK_SEL_HSI:
            sysClock = HSI_VALUE;
            break;

        case RCM_SYSCLK_SEL_HSE:
            sysClock = HSE_VALUE;
            break;

        case RCM_SYSCLK_SEL_PLL:
            pllMull = RCM->CFG1_B.PLLMULCFG + 2;
            pllSource = RCM->CFG1_B.PLLSRCSEL;
            plldiv = RCM->CFG2_B.PLLDIVCFG + 1;

            if (pllSource == 0x00)
            {
                sysClock = (HSI_VALUE >> 1) * pllMull;
            }
            else if (pllSource == 0x01)
            {
                sysClock = (HSI_VALUE / plldiv) * pllMull;
            }
            else if (pllSource == 0x02)
            {
                sysClock = (HSE_VALUE / plldiv) * pllMull;
            }
            else
            {
                sysClock = (HSI48_VALUE / plldiv) * pllMull;
            }
            break;

        case RCM_SYSCLK_SEL_HSI48:
            sysClock  = HSI48_VALUE;
            break;

        default:
            sysClock  = HSI_VALUE;
            break;
    }

    return sysClock;
}

/*!
 * @brief       Read frequency of HCLK(AHB)
 *
 * @param       None
 *
 * @retval      Return frequency of HCLK
 */
uint32_t RCM_ReadHCLKFreq(void)
{
    uint32_t divider;
    uint32_t sysClk, hclk;
    uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

    sysClk = RCM_ReadSYSCLKFreq();
    divider = AHBPrescTable[RCM->CFG1_B.AHBPSC];
    hclk = sysClk >> divider;

    return hclk;
}

/*!
 * @brief       Read frequency of PCLK
 *
 * @param       None
 *
 * @retval      PCLK1   return frequency of PCLK
 */
uint32_t RCM_ReadPCLKFreq(void)
{
    uint32_t hclk, pclk, divider;
    uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};

    hclk = RCM_ReadHCLKFreq();

    divider = APBPrescTable[RCM->CFG1_B.APB1PSC];
    pclk = hclk >> divider;

    return pclk;
}

/*!
 * @brief       Read frequency of ADC CLK
 *
 * @param       None
 *
 * @retval      Return frequency of ADC CLK
 */
uint32_t RCM_ReadADCCLKFreq(void)
{
    uint32_t adcClk, pclk;

    pclk = RCM_ReadPCLKFreq();

    if (RCM->CFG3_B.ADCSEL)
    {
#if defined(APM32F051) || defined(APM32F072) || defined(APM32F091)
        if (RCM->CFG1_B.ADCPSC)
        {
            adcClk = pclk >> 2;
        }
        else
        {
            adcClk = pclk >> 1;
        }
#else
        adcClk = pclk >> 1;
#endif /* APM32F051 || APM32F072 || APM32F091 */
    }
    else
    {
        adcClk = HSI14_VALUE;
    }

    return adcClk;
}

#if defined(APM32F051) || defined(APM32F072) || defined(APM32F091)
/*!
 * @brief       Read frequency of CEC CLK
 *
 * @param       None
 *
 * @retval      Return frequency of CEC CLK
 */
uint32_t RCM_ReadCECCLKFreq(void)
{
    uint32_t cecClk;

    if (RCM->CFG3_B.CECSEL)
    {
        cecClk = LSE_VALUE;
    }
    else
    {
        cecClk = LSI_VALUE / 244;
    }

    return cecClk;
}
#endif /* APM32F051 || APM32F072 || APM32F091 */

/*!
 * @brief       Read frequency of I2C1 CLK
 *
 * @param       None
 *
 * @retval      Return frequency of I2C1 CLK
 */
uint32_t RCM_ReadI2C1CLKFreq(void)
{
    uint32_t i2c1Clk, sysClk;

    sysClk = RCM_ReadSYSCLKFreq();

    if (RCM->CFG3_B.I2C1SEL)
    {
        i2c1Clk = sysClk;
    }
    else
    {
        i2c1Clk = HSI_VALUE;
    }

    return i2c1Clk;
}

/*!
 * @brief       Read frequency of USART1 CLK
 *
 * @param       None
 *
 * @retval      Return frequency of USART1 CLK
 */
uint32_t RCM_ReadUSART1CLKFreq(void)
{
    uint32_t usart1Clk = 0;

    if (RCM->CFG3_B.USART1SEL == 0x00)
    {
        usart1Clk = RCM_ReadPCLKFreq();
    }
    else if (RCM->CFG3_B.USART1SEL == 0x01)
    {
        usart1Clk = RCM_ReadSYSCLKFreq();
    }
    else if (RCM->CFG3_B.USART1SEL == 0x02)
    {
        usart1Clk = LSE_VALUE;
    }
    else if (RCM->CFG3_B.USART1SEL == 0x03)
    {
        usart1Clk = HSI_VALUE;
    }

    return usart1Clk;
}

#if defined(APM32F072)
/*!
 * @brief       Read frequency of USB CLK
 *
 * @param       None
 *
 * @retval      Return frequency of USB CLK
 */
uint32_t RCM_ReadUSBCLKFreq(void)
{
    uint32_t usbClk, pllClk, pllMull = 0, clkDiv;

    if (RCM->CFG1_B.SCLKSWSTS & 0x02)
    {
        pllMull = RCM->CFG1_B.PLLMULCFG;
        pllMull = pllMull + 2;
    }

    if (RCM->CFG3_B.USBDSEL)
    {
        if (RCM->CFG1_B.PLLSRCSEL == 0x00)
        {
            pllClk = (HSI_VALUE >> 1) * pllMull;
        }
        else
        {
            clkDiv = (RCM->CFG2_B.PLLDIVCFG) + 1;
            pllClk = (HSE_VALUE / clkDiv) * pllMull;
        }
        usbClk = pllClk;
    }
    else
    {
        usbClk = HSI48_VALUE;
    }

    return usbClk;
}
#endif /* APM32F072 */

/*!
 * @brief       Read frequency of USART2 CLK
 *
 * @param       None
 *
 * @retval      Return frequency of USART2 CLK
 */
uint32_t RCM_ReadUSART2CLKFreq(void)
{
    uint32_t usart1Clk = 0;

#if defined(APM32F072) || defined(APM32F091)
    if (RCM->CFG3_B.USART2SEL == 0x00)
    {
        usart1Clk = RCM_ReadPCLKFreq();
    }
    else if (RCM->CFG3_B.USART2SEL == 0x01)
    {
        usart1Clk = RCM_ReadSYSCLKFreq();
    }
    else if (RCM->CFG3_B.USART2SEL == 0x02)
    {
        usart1Clk = LSE_VALUE;
    }
    else if (RCM->CFG3_B.USART2SEL == 0x03)
    {
        usart1Clk = HSI_VALUE;
    }
#else
    usart1Clk = RCM_ReadPCLKFreq();
#endif /* APM32F072 || APM32F091 */

    return usart1Clk;
}

/*!
 * @brief       Configures the RTC clock (RTCCLK)
 *
 * @param       RTCClk:   specifies the RTC clock source
 *                        This parameter can be one of the following values:
 *                        @arg RCM_RTCCLK_LSE:       LSE selected as RTC clock
 *                        @arg RCM_RTCCLK_LSI:       LSI selected as RTC clock
 *                        @arg RCM_RTCCLK_HSE_DIV_32: HSE divided by 32 selected as RTC clock
 *
 * @retval      None
 *
 * @note        Once the RTC clock is selected it can't be changed unless the Backup domain is reset
 */
void RCM_ConfigRTCCLK(RCM_RTCCLK_T RTCClk)
{
    RCM->BDCTRL_B.RTCSRCSEL = RTCClk;
}

/*!
 * @brief       Enables the RTC clock
 *
 * @param       None
 *
 * @retval      None
 */
void RCM_EnableRTCCLK(void)
{
    RCM->BDCTRL_B.RTCCLKEN = BIT_SET;
}

/*!
 * @brief       Disables the RTC clock
 *
 * @param       None
 *
 * @retval      None
 */
void RCM_DisableRTCCLK(void)
{
    RCM->BDCTRL_B.RTCCLKEN = BIT_RESET;
}

/*!
 * @brief       Enable the Backup domain reset
 *
 * @param       None
 *
 * @retval      None
 */
void RCM_EnableBackupReset(void)
{
    RCM->BDCTRL_B.BDRST = BIT_SET;
}

/*!
 * @brief       Disable the Backup domain reset
 *
 * @param       None
 *
 * @retval      None
 */
void RCM_DisableBackupReset(void)
{
    RCM->BDCTRL_B.BDRST = BIT_RESET;
}

/*!
 * @brief       Enables AHB peripheral clock
 *
 * @param       AHBPeriph:   specifies the AHB peripheral to gates its clock
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_AHB_PERIPH_DMA1:  DMA1 clock
 *                           @arg RCM_AHB_PERIPH_DMA2:  DMA2 clock
 *                           @arg RCM_AHB_PERIPH_SRAM:  SRAM clock
 *                           @arg RCM_AHB_PERIPH_FPU:   FPU clock
 *                           @arg RCM_AHB_PERIPH_CRC:   CRC clock
 *                           @arg RCM_AHB_PERIPH_GPIOA: GPIOA clock
 *                           @arg RCM_AHB_PERIPH_GPIOB: GPIOB clock
 *                           @arg RCM_AHB_PERIPH_GPIOC: GPIOC clock
 *                           @arg RCM_AHB_PERIPH_GPIOD: GPIOD clock
 *                           @arg RCM_AHB_PERIPH_GPIOE: GPIOE clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_AHB_PERIPH_GPIOF: GPIOF clock
 *                           @arg RCM_AHB_PERIPH_TSC:   TSC clock
 *
 * @retval      None
 */
void RCM_EnableAHBPeriphClock(uint32_t AHBPeriph)
{
    RCM->AHBCLKEN |= AHBPeriph;
}

/*!
 * @brief       Disable AHB peripheral clock
 *
 * @param       AHBPeriph:   specifies the AHB peripheral to gates its clock
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_AHB_PERIPH_DMA1:  DMA1 clock
 *                           @arg RCM_AHB_PERIPH_DMA2:  DMA2 clock
 *                           @arg RCM_AHB_PERIPH_SRAM:  SRAM clock
 *                           @arg RCM_AHB_PERIPH_FPU:   FPU clock
 *                           @arg RCM_AHB_PERIPH_CRC:   CRC clock
 *                           @arg RCM_AHB_PERIPH_GPIOA: GPIOA clock
 *                           @arg RCM_AHB_PERIPH_GPIOB: GPIOB clock
 *                           @arg RCM_AHB_PERIPH_GPIOC: GPIOC clock
 *                           @arg RCM_AHB_PERIPH_GPIOD: GPIOD clock
 *                           @arg RCM_AHB_PERIPH_GPIOE: GPIOE clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_AHB_PERIPH_GPIOF: GPIOF clock
 *                           @arg RCM_AHB_PERIPH_TSC:   TSC clock
 *
 * @retval      None
 */
void RCM_DisableAHBPeriphClock(uint32_t AHBPeriph)
{
    RCM->AHBCLKEN &= (uint32_t)~AHBPeriph;
}

/*!
 * @brief       Enable the High Speed APB (APB2) peripheral clock
 *
 * @param       APB2Periph:  specifies the APB2 peripheral to gates its clock
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB2_PERIPH_SYSCFG: SYSCFG clock
 *                           @arg RCM_APB2_PERIPH_USART6: USART6 clock(Only for APM32F030xC and APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART7: USART7 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART8: USART8 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_ADC1:   ADC1 clock
 *                           @arg RCM_APB2_PERIPH_TMR1:   TMR1 clock
 *                           @arg RCM_APB2_PERIPH_SPI1:   SPI1 clock
 *                           @arg RCM_APB2_PERIPH_USART1: USART1 clock
 *                           @arg RCM_APB2_PERIPH_TMR15:  TMR15 clock
 *                           @arg RCM_APB2_PERIPH_TMR16:  TMR16 clock
 *                           @arg RCM_APB2_PERIPH_TMR17:  TMR17 clock
 *                           @arg RCM_APB2_PERIPH_DBGMCU: DBGMCU clock
 *
 * @retval      None
 */
void RCM_EnableAPB2PeriphClock(uint32_t APB2Periph)
{
    RCM->APBCLKEN2 |= APB2Periph;
}

/*!
 * @brief       Disable the High Speed APB (APB2) peripheral clock
 *
 * @param       APB2Periph:  specifies the APB2 peripheral to gates its clock
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB2_PERIPH_SYSCFG: SYSCFG clock
 *                           @arg RCM_APB2_PERIPH_USART6: USART6 clock(Only for APM32F030xC and APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART7: USART7 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART8: USART8 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_ADC1:   ADC1 clock
 *                           @arg RCM_APB2_PERIPH_TMR1:   TMR1 clock
 *                           @arg RCM_APB2_PERIPH_SPI1:   SPI1 clock
 *                           @arg RCM_APB2_PERIPH_USART1: USART1 clock
 *                           @arg RCM_APB2_PERIPH_TMR15:  TMR15 clock
 *                           @arg RCM_APB2_PERIPH_TMR16:  TMR16 clock
 *                           @arg RCM_APB2_PERIPH_TMR17:  TMR17 clock
 *                           @arg RCM_APB2_PERIPH_DBGMCU: DBGMCU clock
 *
 * @retval      None
 */
void RCM_DisableAPB2PeriphClock(uint32_t APB2Periph)
{
    RCM->APBCLKEN2 &= (uint32_t)~APB2Periph;
}

/*!
 * @brief       Enable the Low Speed APB (APB1) peripheral clock
 *
 * @param       APB1Periph:  specifies the APB1 peripheral to gates its clock
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB1_PERIPH_TMR2:   TMR2 clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_TMR3:   TMR3 clock
 *                           @arg RCM_APB1_PERIPH_TMR6:   TMR6 clock
 *                           @arg RCM_APB1_PERIPH_TMR7:   TMR7 clock(Only for APM32F072)
 *                           @arg RCM_APB1_PERIPH_TMR14:  TMR14 clock
 *                           @arg RCM_APB1_PERIPH_WWDT:   WWDG clock
 *                           @arg RCM_APB1_PERIPH_SPI2:   SPI2 clock
 *                           @arg RCM_APB1_PERIPH_USART2: USART2 clock
 *                           @arg RCM_APB1_PERIPH_USART3: USART3 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART4: USART4 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART5: USART5 clock(Only for APM32F030xC and APM32F091)
 *                           @arg RCM_APB1_PERIPH_I2C1:   I2C1 clock
 *                           @arg RCM_APB1_PERIPH_I2C2:   I2C2 clock
 *                           @arg RCM_APB1_PERIPH_USB:    USB clock(Only for APM32F072)
 *                           @arg RCM_APB1_PERIPH_CAN:    CAN clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CRS:    CRS clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_PMU:    PMU clock
 *                           @arg RCM_APB1_PERIPH_DAC:    DAC clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CEC:    CEC clock(Only for APM32F072 and APM32F091)
 *
 * @retval      None
 */
void RCM_EnableAPB1PeriphClock(uint32_t APB1Periph)
{
    RCM->APBCLKEN1 |= APB1Periph;
}

/*!
 * @brief       Disable the Low Speed APB (APB1) peripheral clock
 *
 * @param       APB1Periph:  specifies the APB1 peripheral to gates its clock
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB1_PERIPH_TMR2:   TMR2 clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_TMR3:   TMR3 clock
 *                           @arg RCM_APB1_PERIPH_TMR6:   TMR6 clock
 *                           @arg RCM_APB1_PERIPH_TMR7:   TMR7 clock(Only for APM32F072)
 *                           @arg RCM_APB1_PERIPH_TMR14:  TMR14 clock
 *                           @arg RCM_APB1_PERIPH_WWDT:   WWDG clock
 *                           @arg RCM_APB1_PERIPH_SPI2:   SPI2 clock
 *                           @arg RCM_APB1_PERIPH_USART2: USART2 clock
 *                           @arg RCM_APB1_PERIPH_USART3: USART3 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART4: USART4 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART5: USART5 clock(Only for APM32F030xC and APM32F091)
 *                           @arg RCM_APB1_PERIPH_I2C1:   I2C1 clock
 *                           @arg RCM_APB1_PERIPH_I2C2:   I2C2 clock
 *                           @arg RCM_APB1_PERIPH_USB:    USB clock(Only for APM32F072)
 *                           @arg RCM_APB1_PERIPH_CAN:    CAN clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CRS:    CRS clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_PMU:    PMU clock
 *                           @arg RCM_APB1_PERIPH_DAC:    DAC clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CEC:    CEC clock(Only for APM32F072 and APM32F091)
 *
 * @retval      None
 */
void RCM_DisableAPB1PeriphClock(uint32_t APB1Periph)
{
    RCM->APBCLKEN1 &= (uint32_t)~APB1Periph;
}

/*!
 * @brief       Enable Low Speed AHB peripheral reset
 *
 * @param       AHBPeriph:   specifies the AHB peripheral to reset
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_AHB_PERIPH_GPIOA: GPIOA reset
 *                           @arg RCM_AHB_PERIPH_GPIOB: GPIOB reset
 *                           @arg RCM_AHB_PERIPH_GPIOC: GPIOC reset
 *                           @arg RCM_AHB_PERIPH_GPIOD: GPIOD reset
 *                           @arg RCM_AHB_PERIPH_GPIOE: GPIOE clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_AHB_PERIPH_GPIOF: GPIOF reset
 *                           @arg RCM_AHB_PERIPH_TSC:   TSC clock
 *
 * @retval      None
 */
void RCM_EnableAHBPeriphReset(uint32_t AHBPeriph)
{
    RCM->AHBRST |= AHBPeriph;
}

/*!
 * @brief       Disable Low Speed AHB peripheral reset
 *
 * @param       AHBPeriph:   specifies the AHB peripheral to reset
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_AHB_PERIPH_GPIOA: GPIOA reset
 *                           @arg RCM_AHB_PERIPH_GPIOB: GPIOB reset
 *                           @arg RCM_AHB_PERIPH_GPIOC: GPIOC reset
 *                           @arg RCM_AHB_PERIPH_GPIOD: GPIOD reset
 *                           @arg RCM_AHB_PERIPH_GPIOE: GPIOE clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_AHB_PERIPH_GPIOF: GPIOF reset
 *                           @arg RCM_AHB_PERIPH_TSC:   TSC clock
 *
 * @retval      None
 */
void RCM_DisableAHBPeriphReset(uint32_t AHBPeriph)
{
    RCM->AHBRST &= (uint32_t)~AHBPeriph;
}

/*!
 * @brief       Enable Low Speed APB (APB1) peripheral reset
 *
 * @param       APB1Periph:  specifies the APB1 peripheral to reset
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB1_PERIPH_TMR2:   TMR2 clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_TMR3:   TMR3 clock
 *                           @arg RCM_APB1_PERIPH_TMR6:   TMR6 clock
 *                           @arg RCM_APB1_PERIPH_TMR7:   TMR7 clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_TMR14:  TMR14 clock
 *                           @arg RCM_APB1_PERIPH_WWDT:   WWDG clock
 *                           @arg RCM_APB1_PERIPH_SPI2:   SPI2 clock
 *                           @arg RCM_APB1_PERIPH_USART2: USART2 clock
 *                           @arg RCM_APB1_PERIPH_USART3: USART3 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART4: USART4 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART5: USART5 clock(Only for APM32F030xC and APM32F091)
 *                           @arg RCM_APB1_PERIPH_I2C1:   I2C1 clock
 *                           @arg RCM_APB1_PERIPH_I2C2:   I2C2 clock
 *                           @arg RCM_APB1_PERIPH_USB:    USB clock(Only for APM32F072)
 *                           @arg RCM_APB1_PERIPH_CAN:    CAN clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CRS:    CRS clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_PMU:    PMU clock
 *                           @arg RCM_APB1_PERIPH_DAC:    DAC clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CEC:    CEC clock(Only for APM32F072 and APM32F091)
 *
 * @retval      None
 */
void RCM_EnableAPB1PeriphReset(uint32_t APB1Periph)
{
    RCM->APBRST1 |= APB1Periph;
}

/*!
 * @brief       Disable Low Speed APB (APB1) peripheral reset
 *
 * @param       APB1Periph:  specifies the APB1 peripheral to reset
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB1_PERIPH_TMR2:   TMR2 clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_TMR3:   TMR3 clock
 *                           @arg RCM_APB1_PERIPH_TMR6:   TMR6 clock
 *                           @arg RCM_APB1_PERIPH_TMR7:   TMR7 clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_TMR14:  TMR14 clock
 *                           @arg RCM_APB1_PERIPH_WWDT:   WWDG clock
 *                           @arg RCM_APB1_PERIPH_SPI2:   SPI2 clock
 *                           @arg RCM_APB1_PERIPH_USART2: USART2 clock
 *                           @arg RCM_APB1_PERIPH_USART3: USART3 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART4: USART4 clock(Only for APM32F030xC, APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_USART5: USART5 clock(Only for APM32F030xC and APM32F091)
 *                           @arg RCM_APB1_PERIPH_I2C1:   I2C1 clock
 *                           @arg RCM_APB1_PERIPH_I2C2:   I2C2 clock
 *                           @arg RCM_APB1_PERIPH_USB:    USB clock(Only for APM32F072)
 *                           @arg RCM_APB1_PERIPH_CAN:    CAN clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CRS:    CRS clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_PMU:    PMU clock
 *                           @arg RCM_APB1_PERIPH_DAC:    DAC clock(Only for APM32F072 and APM32F091)
 *                           @arg RCM_APB1_PERIPH_CEC:    CEC clock(Only for APM32F072 and APM32F091)
 *
 * @retval      None
 */
void RCM_DisableAPB1PeriphReset(uint32_t APB1Periph)
{
    RCM->APBRST1 &= (uint32_t)~APB1Periph;
}

/*!
 * @brief       Enable High Speed APB (APB2) peripheral reset
 *
 * @param       APB2Periph:  specifies the APB2 peripheral to reset
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB2_PERIPH_SYSCFG: SYSCFG clock
 *                           @arg RCM_APB2_PERIPH_USART6: USART6 clock(Only for APM32F030xC, APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART7: USART7 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART8: USART8 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_ADC1:   ADC1 clock
 *                           @arg RCM_APB2_PERIPH_TMR1:   TMR1 clock
 *                           @arg RCM_APB2_PERIPH_SPI1:   SPI1 clock
 *                           @arg RCM_APB2_PERIPH_USART1: USART1 clock
 *                           @arg RCM_APB2_PERIPH_TMR15:  TMR15 clock
 *                           @arg RCM_APB2_PERIPH_TMR16:  TMR16 clock
 *                           @arg RCM_APB2_PERIPH_TMR17:  TMR17 clock
 *                           @arg RCM_APB2_PERIPH_DBGMCU: DBGMCU clock
 *
 * @retval      None
 */
void RCM_EnableAPB2PeriphReset(uint32_t APB2Periph)
{
    RCM->APBRST2 |= APB2Periph;
}

/*!
 * @brief       Disable High Speed APB (APB2) peripheral reset
 *
 * @param       APB2Periph:  specifies the APB2 peripheral to reset
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_APB2_PERIPH_SYSCFG: SYSCFG clock
 *                           @arg RCM_APB2_PERIPH_USART6: USART6 clock(Only for APM32F030xC, APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART7: USART7 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_USART8: USART8 clock(Only for APM32F091)
 *                           @arg RCM_APB2_PERIPH_ADC1:   ADC1 clock
 *                           @arg RCM_APB2_PERIPH_TMR1:   TMR1 clock
 *                           @arg RCM_APB2_PERIPH_SPI1:   SPI1 clock
 *                           @arg RCM_APB2_PERIPH_USART1: USART1 clock
 *                           @arg RCM_APB2_PERIPH_TMR15:  TMR15 clock
 *                           @arg RCM_APB2_PERIPH_TMR16:  TMR16 clock
 *                           @arg RCM_APB2_PERIPH_TMR17:  TMR17 clock
 *                           @arg RCM_APB2_PERIPH_DBGMCU: DBGMCU clock
 *
 * @retval      None
 */
void RCM_DisableAPB2PeriphReset(uint32_t APB2Periph)
{
    RCM->APBRST2 &= (uint32_t)~APB2Periph;
}

/*!
 * @brief       Enable the specified RCM interrupts
 *
 * @param       interrupt:   specifies the RCM interrupt source to check
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_INT_LSIRDY:    LSI ready interrupt
 *                           @arg RCM_INT_LSERDY:    LSE ready interrupt
 *                           @arg RCM_INT_HSIRDY:    HSI ready interrupt
 *                           @arg RCM_INT_HSERDY:    HSE ready interrupt
 *                           @arg RCM_INT_PLLRDY:    PLL ready interrupt
 *                           @arg RCM_INT_HSI14RDY:  HSI14 ready interrupt
 *                           @arg RCM_INT_HSI48RDY:  HSI48 ready interrupt(Only for APM32F072 and APM32F091 devices)
 *
 * @retval  None
 */
void RCM_EnableInterrupt(uint8_t interrupt)
{
    RCM->INT |= (interrupt << 8);
}

/*!
 * @brief       Disable the specified RCM interrupts
 *
 * @param       interrupt:   specifies the RCM interrupt source to check
 *                           This parameter can be any combination of the following values:
 *                           @arg RCM_INT_LSIRDY:    LSI ready interrupt
 *                           @arg RCM_INT_LSERDY:    LSE ready interrupt
 *                           @arg RCM_INT_HSIRDY:    HSI ready interrupt
 *                           @arg RCM_INT_HSERDY:    HSE ready interrupt
 *                           @arg RCM_INT_PLLRDY:    PLL ready interrupt
 *                           @arg RCM_INT_HSI14RDY:  HSI14 ready interrupt
 *                           @arg RCM_INT_HSI48RDY:  HSI48 ready interrupt(Only for APM32F072 and APM32F091 devices)
 *
 * @retval  None
 */
void RCM_DisableInterrupt(uint8_t interrupt)
{
    RCM->INT &= ~(interrupt << 8);
}

/*!
 * @brief       Read the specified RCM flag status
 *
 * @param       flag:   specifies the flag to check
 *                      This parameter can be one of the following values:
 *                      @arg RCM_FLAG_HSIRDY:   HSI oscillator clock ready
 *                      @arg RCM_FLAG_HSERDY:   HSE oscillator clock ready
 *                      @arg RCM_FLAG_PLLRDY:   PLL clock ready
 *                      @arg RCM_FLAG_LSERDY:   LSE oscillator clock ready
 *                      @arg RCM_FLAG_LSIRDY:   LSI oscillator clock ready
 *                      @arg RCM_FLAG_V18PRRST: V1.8 power domain reset
 *                      @arg RCM_FLAG_OBRST:    Option Byte Loader (OBL) reset
 *                      @arg RCM_FLAG_PINRST:   Pin reset
 *                      @arg RCM_FLAG_PWRRST:   POR/PDR reset
 *                      @arg RCM_FLAG_SWRST:    Software reset
 *                      @arg RCM_FLAG_IWDTRST:  Independent Watchdog reset
 *                      @arg RCM_FLAG_WWDTRST:  Window Watchdog reset
 *                      @arg RCM_FLAG_LPRRST:   Low Power reset
 *                      @arg RCM_FLAG_HSI14RDY: HSI14 clock ready
 *                      @arg RCM_FLAG_HSI48RDY: HSI48 clock ready(Only for APM32F072 and APM32F091)
 *
 * @retval      The new state of flag (SET or RESET)
 */
uint16_t RCM_ReadStatusFlag(RCM_FLAG_T flag)
{
    uint32_t reg, bit;

    bit = (uint32_t)(1 << (flag & 0xff));

    reg = (flag >> 8) & 0xff;

    switch (reg)
    {
        case 0:
            reg = RCM->CTRL1;
            break;

        case 1:
            reg = RCM->BDCTRL;
            break;

        case 2:
            reg = RCM->CSTS;
            break;

        case 3:
            reg = RCM->CTRL2;
            break;

        default:
            break;
    }

    if (reg & bit)
    {
        return SET;
    }

    return RESET;
}

/*!
 * @brief       Clears the RCM reset flags
 *
 * @param       None
 *
 * @retval      None
 *
 * @note        The reset flags are:
 *              RCM_FLAG_V18PRRST; RCM_FLAG_OBRST; RCM_FLAG_PINRST; RCM_FLAG_PWRRST;
 *              RCM_FLAG_SWRST; RCM_FLAG_IWDTRST; RCM_FLAG_WWDTRST; RCM_FLAG_LPRRST;
 */
void RCM_ClearStatusFlag(void)
{
    RCM->CSTS_B.RSTFLGCLR = BIT_SET;
}

/*!
 * @brief       Read the specified RCM interrupt Flag
 *
 * @param       flag:   specifies the RCM interrupt source to check
 *                      This parameter can be one of the following values:
 *                      @arg RCM_INT_LSIRDY:    LSI ready interrupt
 *                      @arg RCM_INT_LSERDY:    LSE ready interrupt
 *                      @arg RCM_INT_HSIRDY:    HSI ready interrupt
 *                      @arg RCM_INT_HSERDY:    HSE ready interrupt
 *                      @arg RCM_INT_PLLRDY:    PLL ready interrupt
 *                      @arg RCM_INT_HSI14RDY:  HSI14 ready interrupt
 *                      @arg RCM_INT_HSI48RDY:  HSI48 ready interrupt(Only for APM32F072 and APM32F091 devices)
 *                      @arg RCC_IT_CSS:        Clock Security System interrupt
 *
 * @retval      The new state of intFlag (SET or RESET)
 */
uint8_t RCM_ReadIntFlag(RCM_INT_T flag)
{
    uint8_t ret;

    ret = (RCM->INT& flag) ? SET : RESET;

    return  ret;
}

/*!
 * @brief       Clears the interrupt flag
 *
 * @param       flag:   specifies the RCM interrupt source to check
 *                      This parameter can be any combination of the following values:
 *                      @arg RCM_INT_LSIRDY:    LSI ready interrupt
 *                      @arg RCM_INT_LSERDY:    LSE ready interrupt
 *                      @arg RCM_INT_HSIRDY:    HSI ready interrupt
 *                      @arg RCM_INT_HSERDY:    HSE ready interrupt
 *                      @arg RCM_INT_PLLRDY:    PLL ready interrupt
 *                      @arg RCM_INT_HSI14RDY:  HSI14 ready interrupt
 *                      @arg RCM_INT_HSI48RDY:  HSI48 ready interrupt(Only for APM32F072 and APM32F091 devices)
 *                      @arg RCC_IT_CSS:        Clock Security System interrupt
 *
 * @retval      None
 */
void RCM_ClearIntFlag(uint8_t flag)
{
    uint32_t temp;

    temp = flag << 16;
    RCM->INT |= temp;
}

/**@} end of group RCM_Functions*/
/**@} end of group RCM_Driver*/

#endif /* RCM */

/**@} end of group APM32F0xx_StdPeriphDriver*/
