/*******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC 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
 */
/******************************************************************************/
/** \file hc32f460_adc.c
 **
 ** A detailed description is available at
 ** @link AdcGroup Adc description @endlink
 **
 **   - 2018-11-30  CDT First version for Device Driver Library of Adc.
 **
 ******************************************************************************/

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32f460_adc.h"
#include "hc32f460_utility.h"

/**
 *******************************************************************************
 ** \addtogroup AdcGroup
 ******************************************************************************/
//@{

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/*! Parameter validity check for ADC peripherals. */
#define IS_ADC_PERIPH(ADCx)                                                     \
(   ((ADCx) == M4_ADC1)                         ||                              \
    ((ADCx) == M4_ADC2))

/*! Parameter validity check for ADC1 channel index. */
#define IS_ADC1_CH_INDEX(x)                                                     \
(   ((x) < ADC1_CH_COUNT))

/*! Parameter validity check for ADC2 channel index. */
#define IS_ADC2_CH_INDEX(x)                                                     \
(   ((x) < ADC2_CH_COUNT))

/*! Parameter validity check for ADC average count. */
#define IS_ADC_AVCNT(AVCNT)                                                     \
(   ((AVCNT) == AdcAvcnt_2)                     ||                              \
    (((AVCNT) >= AdcAvcnt_4) && ((AVCNT) <= AdcAvcnt_256)))

/*! Parameter validity check for ADC data alignment. */
#define IS_ADC_DATA_ALIGN(ALIGN)                                                \
(   ((ALIGN) == AdcDataAlign_Right)             ||                              \
    ((ALIGN) == AdcDataAlign_Left))

/*! Parameter validity check for ADC auto clear DR. */
#define IS_ADC_CLREN(EN)                                                        \
(   ((EN) == AdcClren_Enable)                   ||                              \
    ((EN) == AdcClren_Disable))

/*! Parameter validity check for ADC resolution. */
#define IS_ADC_RESOLUTION(RESOLUTION)                                           \
(   ((RESOLUTION) == AdcResolution_8Bit)        ||                              \
    ((RESOLUTION) == AdcResolution_10Bit)       ||                              \
    ((RESOLUTION) == AdcResolution_12Bit))

/*! Parameter validity check for ADC scan convert mode. */
#define IS_ADC_SCAN_MODE(MODE)                                                  \
(   ((MODE) == AdcMode_SAOnce)                  ||                              \
    ((MODE) == AdcMode_SAContinuous)            ||                              \
    ((MODE) == AdcMode_SAOnceSBOnce)            ||                              \
    ((MODE) == AdcMode_SAContinuousSBOnce))

/*! Parameter validity check for ADC RSCHSEL. */
#define IS_ADC_RSCHSEL(SEL)                                                     \
(   ((SEL) == AdcRschsel_Continue)              ||                              \
    ((SEL) == AdcRschsel_Restart))

/*! Parameter validity check for ADC SA trigger source. */
#define IS_ADC_TRGEN(EN)                                                        \
(   ((EN) == AdcTrgen_Enable)                   ||                              \
    ((EN) == AdcTrgen_Disable))

/*! Parameter validity check for ADC SA trigger source. */
#define IS_ADC_TRGSEL(SEL)                                                      \
(   ((SEL) == AdcTrgsel_ADTRGX)                 ||                              \
    ((SEL) == AdcTrgsel_TRGX0)                  ||                              \
    ((SEL) == AdcTrgsel_TRGX1)                  ||                              \
    ((SEL) == AdcTrgsel_TRGX0_TRGX1))

/*! Parameter validity check for ADC common trigger. */
#define IS_ADC_COM_TRIGGER(x)                                                   \
(   ((x) == AdcComTrigger_1)                    ||                              \
    ((x) == AdcComTrigger_2)                    ||                              \
    ((x) == AdcComTrigger_1_2))

/*! Parameter validity check for ADC EOCAIEN/ENCBIEN. */
#define IS_ADC_EOCIEN(EN)                                                       \
(   ((EN) == AdcEocien_Disable)                 ||                              \
    ((EN) == AdcEocien_Enable))

/*! Parameter validity check for ADC sampling time. */
#define IS_ADC_SAMPLE_TIME(TIME)                                                \
(   ((TIME) == 255u)                            ||                              \
    (((TIME) >= 5u) && ((TIME) <= 254u)))

/*! Parameter validity check for ADC sync trigger mode. */
#define IS_ADC_SYNC_MODE(MODE)                                                  \
(   ((MODE) == AdcSync_SingleSerial)            ||                              \
    ((MODE) == AdcSync_SingleParallel)          ||                              \
    ((MODE) == AdcSync_ContinuousSerial)        ||                              \
    ((MODE) == AdcSync_ContinuousParallel))

/*! Parameter validity check for ADC sync able. */
#define IS_ADC_SYNC_ENABLE(EN)                                                  \
(   ((EN) == AdcSync_Disable)                   ||                              \
    ((EN) == AdcSync_Enable))

/*! Parameter validity check for ADC ADWIEN  */
#define IS_ADC_AWDIEN(EN)                                                       \
(   ((EN) == AdcAwdInt_Disable)                 ||                              \
    ((EN) == AdcAwdInt_Enable))

/*! Parameter validity check for ADC AWDSS */
#define IS_ADC_AWDSS(SS)                                                        \
(   ((SS) == AdcAwdSel_SA_SB)                   ||                              \
    ((SS) == AdcAwdSel_SA)                      ||                              \
    ((SS) == AdcAwdSel_SB)                      ||                              \
    ((SS) == AdcAwdSel_SB_SA))

/*! Parameter validity check for ADC AWDMD */
#define IS_ADC_AWDMD(MD)                                                        \
(   ((MD) == AdcAwdCmpMode_0)                   ||                              \
    ((MD) == AdcAwdCmpMode_1))

/*! Parameter validity check for ADC AWDEN */
#define IS_ADC_AWDEN(EN)                                                        \
(   ((EN) == AdcAwd_Disable)                    ||                              \
    ((EN) == AdcAwd_Enable))

/*! Parameter validity check for ADC PGA control */
#define IS_ADC_PGA_CTL(CTL)                                                     \
(   ((CTL) == AdcPgaCtl_Invalid)                ||                              \
    ((CTL) == AdcPgaCtl_Amplify))

/*! Parameter validity check for ADC gain factor. */
#define IS_ADC_PGA_FACTOR(FACTOR)                                               \
(   ((FACTOR) == AdcPgaFactor_2)                ||                              \
    (((FACTOR) >= AdcPgaFactor_2P133) && ((FACTOR) <= AdcPgaFactor_32)))

/*! Parameter validity check for ADC PGA negative. */
#define IS_ADC_PGA_NEGATIVE(N)                                                  \
(   ((N) == AdcPgaNegative_VSSA)                ||                              \
    ((N) == AdcPgaNegative_PGAVSS))

/*! Parameter validity check for ADC PGA channel. */
#define IS_ADC_PGA_CH(ch)                                                       \
(   ((ch) == PGA_CH_NONE)                       ||                              \
    ((ch) == PGA_CH0)                           ||                              \
    ((ch) == PGA_CH1)                           ||                              \
    ((ch) == PGA_CH2)                           ||                              \
    ((ch) == PGA_CH3)                           ||                              \
    ((ch) == PGA_CH4)                           ||                              \
    ((ch) == PGA_CH5)                           ||                              \
    ((ch) == PGA_CH6)                           ||                              \
    ((ch) == PGA_CH7)                           ||                              \
    ((ch) == PGA_CH8))

/*! Parameter validity check for ADC trigger source event . */
#define IS_ADC_TRIG_SRC_EVENT(x)                                                \
(   ((x) == EVT_PORT_EIRQ0)                                         ||          \
    (((x) > EVT_PORT_EIRQ0) && ((x) <= EVT_PORT_EIRQ15))            ||          \
    (((x) >= EVT_DMA1_TC0) && ((x) <= EVT_DMA2_BTC3))               ||          \
    (((x) >= EVT_EFM_OPTEND) && ((x) <= EVT_USBFS_SOF))             ||          \
    (((x) >= EVT_DCU1) && ((x) <= EVT_DCU4))                        ||          \
    (((x) >= EVT_TMR01_GCMA) && ((x) <= EVT_TMR02_GCMB))            ||          \
    (((x) >= EVT_RTC_ALM) && ((x) <= EVT_RTC_PRD))                  ||          \
    (((x) >= EVT_TMR61_GCMA) && ((x) <= EVT_TMR61_GUDF))            ||          \
    (((x) >= EVT_TMR61_SCMA) && ((x) <= EVT_TMR61_SCMB))            ||          \
    (((x) >= EVT_TMR62_GCMA) && ((x) <= EVT_TMR62_GUDF))            ||          \
    (((x) >= EVT_TMR62_SCMA) && ((x) <= EVT_TMR62_SCMB))            ||          \
    (((x) >= EVT_TMR63_GCMA) && ((x) <= EVT_TMR63_GUDF))            ||          \
    (((x) >= EVT_TMR63_SCMA) && ((x) <= EVT_TMR63_SCMB))            ||          \
    (((x) >= EVT_TMRA1_OVF) && ((x) <= EVT_TMRA5_CMP))              ||          \
    (((x) >= EVT_TMRA6_OVF) && ((x) <= EVT_TMRA6_CMP))              ||          \
    (((x) >= EVT_USART1_EI) && ((x) <= EVT_USART4_RTO))             ||          \
    (((x) >= EVT_SPI1_SPRI) && ((x) <= EVT_AOS_STRG))               ||          \
    (((x) >= EVT_TMR41_SCMUH) && ((x) <= EVT_TMR42_SCMWL))          ||          \
    (((x) >= EVT_TMR43_SCMUH) && ((x) <= EVT_TMR43_SCMWL))          ||          \
    (((x) >= EVT_EVENT_PORT1)  && ((x) <= EVT_EVENT_PORT4))         ||          \
    (((x) >= EVT_I2S1_TXIRQOUT)  && ((x) <= EVT_I2S1_RXIRQOUT))     ||          \
    (((x) >= EVT_I2S2_TXIRQOUT)  && ((x) <= EVT_I2S2_RXIRQOUT))     ||          \
    (((x) >= EVT_I2S3_TXIRQOUT)  && ((x) <= EVT_I2S3_RXIRQOUT))     ||          \
    (((x) >= EVT_I2S4_TXIRQOUT)  && ((x) <= EVT_I2S4_RXIRQOUT))     ||          \
    (((x) >= EVT_ACMP1)  && ((x) <= EVT_ACMP3))                     ||          \
    (((x) >= EVT_I2C1_RXI) && ((x) <= EVT_I2C3_EEI))                ||          \
    (((x) >= EVT_PVD_PVD1) && ((x) <= EVT_OTS))                     ||          \
    ((x)  == EVT_WDT_REFUDF)                                        ||          \
    (((x) >= EVT_ADC1_EOCA) && ((x) <= EVT_TRNG_END))               ||          \
    (((x) >= EVT_SDIOC1_DMAR) && ((x) <= EVT_SDIOC1_DMAW))          ||          \
    (((x) >= EVT_SDIOC2_DMAR) && ((x) <= EVT_SDIOC2_DMAW))          ||          \
    ((x) == EVT_MAX))

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/
static void ADC_ReadAllData(const M4_ADC_TypeDef *ADCx, uint16_t *pu16AdcData, uint8_t u8Length);

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern')
 ******************************************************************************/
/**
 *******************************************************************************
 ** \brief Initializes an ADC instance.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] pstcInit                Pointer to ADC initialization structure.
 **                                     See @ref stc_adc_init_t for details.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_Init(M4_ADC_TypeDef *ADCx, const stc_adc_init_t *pstcInit)
{
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != ADCx) && (NULL != pstcInit))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_ADC_RESOLUTION(pstcInit->enResolution));
        DDL_ASSERT(IS_ADC_DATA_ALIGN(pstcInit->enDataAlign));
        DDL_ASSERT(IS_ADC_CLREN(pstcInit->enAutoClear));
        DDL_ASSERT(IS_ADC_SCAN_MODE(pstcInit->enScanMode));
        DDL_ASSERT(IS_ADC_RSCHSEL(pstcInit->enRschsel));

        /* Stop ADC conversion. */
        ADCx->STR = 0u;

        ADCx->CR0_f.ACCSEL  = pstcInit->enResolution;
        ADCx->CR0_f.DFMT    = pstcInit->enDataAlign;
        ADCx->CR0_f.CLREN   = pstcInit->enAutoClear;
        ADCx->CR0_f.MS      = pstcInit->enScanMode;
        ADCx->CR1_f.RSCHSEL = pstcInit->enRschsel;

        /* Disable EOC(End Of Conversion) interrupts default. */
        ADCx->ICR = (uint8_t)0x0;

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Deinitializes an ADC instance.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_DeInit(M4_ADC_TypeDef *ADCx)
{
    uint8_t i;
    uint8_t u8SstrNum;
    uint32_t u32SSTRAddr;
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        /* Set the value of all registers to the reset value. */
        /* Stop ADC conversion. */
        ADCx->STR       = 0u;
        ADCx->CR0       = 0u;
        ADCx->CR1       = 0u;
        ADCx->TRGSR     = 0u;
        ADCx->CHSELRA0  = 0u;
        ADCx->CHSELRB0  = 0u;
        ADCx->AVCHSELR0 = 0u;
        ADCx->CHMUXR0   = (uint16_t)0x3210;
        ADCx->CHMUXR1   = (uint16_t)0x7654;
        ADCx->ISR       = 0u;
        ADCx->ICR       = (uint8_t)0x03;
        ADCx->AWDCR     = 0u;
        ADCx->AWDDR0    = 0u;
        ADCx->AWDDR1    = 0u;
        ADCx->AWDCHSR0  = 0u;
        ADCx->AWDSR0    = 0u;

        u32SSTRAddr = (uint32_t)&ADCx->SSTR0;
        if (M4_ADC1 == ADCx)
        {
            ADCx->CHSELRA1  = 0u;
            ADCx->CHSELRB1  = 0u;
            ADCx->AVCHSELR1 = 0u;
            ADCx->CHMUXR2   = (uint16_t)0xBA98;
            ADCx->CHMUXR3   = (uint16_t)0xFEDC;
            ADCx->SYNCCR    = (uint16_t)0x0C00;
            ADCx->AWDCHSR1  = 0u;
            ADCx->AWDSR1    = 0u;
            ADCx->PGACR     = 0u;
            ADCx->PGAGSR    = 0u;
            ADCx->PGAINSR0  = 0u;
            ADCx->PGAINSR1  = 0u;
            ADCx->SSTRL     = 0x0Bu;
            u8SstrNum = 16u;
        }
        else
        {
            u8SstrNum = 9u;
        }

        for (i=0u; i<u8SstrNum; i++)
        {
            *(__IO uint8_t *)u32SSTRAddr = 0x0Bu;
            u32SSTRAddr++;
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Set scan mode.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param[in] enMode                   See @ref en_adc_scan_mode_t for details.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_SetScanMode(M4_ADC_TypeDef *ADCx, en_adc_scan_mode_t enMode)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_ADC_SCAN_MODE(enMode));

        ADCx->CR0_f.MS = enMode;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Set trigger source.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] pstcTrgCfg              Pointer to ADC trigger source configuration structure.
 ** \arg u8Sequence                     The sequence which you want to set it's trigger source.
 ** \arg enTrgEnable                    Enable or disable trigger source.
 ** \arg enTrgSel                       The type of trigger source.
 ** \arg enInTrg0                       Event number @ref en_event_src_t.
 ** \arg enInTrg1                       Event number @ref en_event_src_t.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 ** \note Sequence A and Sequence B CAN NOT set the same trigger source.
 **
 ******************************************************************************/
en_result_t ADC_ConfigTriggerSrc(M4_ADC_TypeDef *ADCx,
                                 const stc_adc_trg_cfg_t *pstcTrgCfg)
{
    uint32_t u32TrgSelR;
    __IO uint32_t *io32AdcxTrgSelR0;
    __IO uint32_t *io32AdcxTrgSelR1;
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != ADCx)          &&
        (NULL != pstcTrgCfg)    &&
        (pstcTrgCfg->u8Sequence <= ADC_SEQ_B))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_ADC_TRGSEL(pstcTrgCfg->enTrgSel));

        if (ADC_SEQ_A == pstcTrgCfg->u8Sequence)
        {
            ADCx->TRGSR_f.TRGSELA = pstcTrgCfg->enTrgSel;
        }
        else
        {
            ADCx->TRGSR_f.TRGSELB = pstcTrgCfg->enTrgSel;
        }

        if (AdcTrgsel_ADTRGX != pstcTrgCfg->enTrgSel)
        {
            if (M4_ADC1 == ADCx)
            {
                io32AdcxTrgSelR0 = &(M4_AOS->ADC1_ITRGSELR0);
                io32AdcxTrgSelR1 = &(M4_AOS->ADC1_ITRGSELR1);
            }
            else
            {
                io32AdcxTrgSelR0 = &(M4_AOS->ADC2_ITRGSELR0);
                io32AdcxTrgSelR1 = &(M4_AOS->ADC2_ITRGSELR1);
            }

            if ((pstcTrgCfg->enTrgSel & AdcTrgsel_TRGX0) == AdcTrgsel_TRGX0)
            {
                DDL_ASSERT(IS_ADC_TRIG_SRC_EVENT(pstcTrgCfg->enInTrg0));

                u32TrgSelR  = *io32AdcxTrgSelR0;
                u32TrgSelR &= ~0x1FFul;
                u32TrgSelR |= pstcTrgCfg->enInTrg0;
                *io32AdcxTrgSelR0 = u32TrgSelR;
            }
            if ((pstcTrgCfg->enTrgSel & AdcTrgsel_TRGX1) == AdcTrgsel_TRGX1)
            {
                DDL_ASSERT(IS_ADC_TRIG_SRC_EVENT(pstcTrgCfg->enInTrg1));

                u32TrgSelR  = *io32AdcxTrgSelR1;
                u32TrgSelR &= ~0x1FFul;
                u32TrgSelR |= pstcTrgCfg->enInTrg1;
                *io32AdcxTrgSelR1 = u32TrgSelR;
            }
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Set trigger source.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u8Seq                   The sequence which you want to set it's trigger source.
 **
 ** \param [in] enState                 Enable or disable trigger source.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_TriggerSrcCmd(M4_ADC_TypeDef *ADCx,
                              uint8_t u8Seq,
                              en_functional_state_t enState)
{
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != ADCx) && (u8Seq <= ADC_SEQ_B))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

        if (ADC_SEQ_A == u8Seq)
        {
            ADCx->TRGSR_f.TRGENA = enState;
        }
        else
        {
            ADCx->TRGSR_f.TRGENB = enState;
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Enable or disable ADC common trigger.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] enTrgSel                ADC trigger source type. See @ref en_adc_trgsel_t for details.
 **
 ** \param [in] enComTrigger            ADC common trigger selection. See @ref en_adc_com_trigger_t for details.
 **
 ** \param [in] enState                 Enable or disable the specified common trigger.
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_ComTriggerCmd(M4_ADC_TypeDef *ADCx, en_adc_trgsel_t enTrgSel, \
                       en_adc_com_trigger_t enComTrigger, en_functional_state_t enState)
{
    uint32_t u32ComTrig = enComTrigger;
    uint32_t u32ITRGSELRAddr;

    DDL_ASSERT(IS_ADC_PERIPH(ADCx));
    DDL_ASSERT(IS_ADC_TRGSEL(enTrgSel) && (enTrgSel != AdcTrgsel_ADTRGX));
    DDL_ASSERT(IS_ADC_COM_TRIGGER(enComTrigger));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

    if (M4_ADC1 == ADCx)
    {
        u32ITRGSELRAddr = (uint32_t)&M4_AOS->ADC1_ITRGSELR0;
    }
    else
    {
        u32ITRGSELRAddr = (uint32_t)&M4_AOS->ADC2_ITRGSELR0;
    }

    u32ComTrig <<= 30u;

    if ((enTrgSel & AdcTrgsel_TRGX0) == AdcTrgsel_TRGX0)
    {
        if (enState == Enable)
        {
            *(__IO uint32_t *)u32ITRGSELRAddr |= u32ComTrig;
        }
        else
        {
            *(__IO uint32_t *)u32ITRGSELRAddr &= ~u32ComTrig;
        }
    }

    if ((enTrgSel & AdcTrgsel_TRGX1) == AdcTrgsel_TRGX1)
    {
        u32ITRGSELRAddr += 4ul;
        if (enState == Enable)
        {
            *(__IO uint32_t *)u32ITRGSELRAddr |= u32ComTrig;
        }
        else
        {
            *(__IO uint32_t *)u32ITRGSELRAddr &= ~u32ComTrig;
        }
    }
}

/**
 *******************************************************************************
 ** \brief Add ADC channel.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] pstcChCfg               Pointer to ADC channel configuration structure.
 ** \arg u32Channel                     The channel(s) you want to configure.
 ** \arg u8Sequence                     The sequence which the channel(s) belong(s) to.
 ** \arg pu8SampTime                    Pointer to sampling time.
 **                                     eg. u32Channel = 1001b
 **                                     pu8SampTime[0] = channel 0's time
 **                                     pu8SampTime[1] = channel 3's time
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 ** \note Sequence A and Sequence B CAN NOT set the same channel!!!
 **
 ******************************************************************************/
en_result_t ADC_AddAdcChannel(M4_ADC_TypeDef *ADCx, const stc_adc_ch_cfg_t *pstcChCfg)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint8_t      i;
    uint8_t      j;
    uint32_t     u32ChannelSel;
    __IO uint8_t *io8Sstr;

    if ((NULL != ADCx)      &&
        (NULL != pstcChCfg) &&
        (pstcChCfg->u8Sequence <= ADC_SEQ_B))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        if (M4_ADC1 == ADCx)
        {
            u32ChannelSel = pstcChCfg->u32Channel & ADC1_CH_ALL;
            if (ADC_SEQ_A == pstcChCfg->u8Sequence)
            {
                ADCx->CHSELRA0 |= (uint16_t)u32ChannelSel;
                ADCx->CHSELRA1 |= (uint16_t)(u32ChannelSel >> 16u);
            }
            else
            {
                ADCx->CHSELRB0 |= (uint16_t)u32ChannelSel;
                ADCx->CHSELRB1 |= (uint16_t)(u32ChannelSel >> 16u);
            }
        }
        else
        {
            u32ChannelSel = pstcChCfg->u32Channel & ADC2_CH_ALL;
            if (ADC_SEQ_A == pstcChCfg->u8Sequence)
            {
                ADCx->CHSELRA0 |= (uint16_t)u32ChannelSel;
            }
            else
            {
                ADCx->CHSELRB0 |= (uint16_t)u32ChannelSel;
            }
        }

        /* Set sampling time */
        i = 0u;
        j = 0u;
        io8Sstr = &(ADCx->SSTR0);
        while (0u != u32ChannelSel)
        {
            if (u32ChannelSel & 0x1ul)
            {
                io8Sstr[i] = pstcChCfg->pu8SampTime[j++];
            }
            u32ChannelSel >>= 1u;
            i++;
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Delete ADC channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32Channel              The channel(s) you want to delete.
 ** \arg ADC1_CH0 ~ ADC1_CH16           Channels of ADC unit 1.
 ** \arg ADC2_CH0 ~ ADC2_CH8            Channels of ADC unit 2.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ** \note                               You can use this function to delete ADC channel(s)
 **                                     and then set the corresponding pin(s) of the channel(s)
 **                                     to the other mode you need in your application.
 **
 ******************************************************************************/
en_result_t ADC_DelAdcChannel(M4_ADC_TypeDef *ADCx, uint32_t u32Channel)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint16_t    u16ChSelR0;
    uint16_t    u16ChSelR1;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        u16ChSelR0 = (uint16_t)u32Channel;
        u16ChSelR1 = (uint16_t)(u32Channel >> 16u);

        ADCx->CHSELRA0 &= (uint16_t)(~u16ChSelR0);
        ADCx->CHSELRB0 &= (uint16_t)(~u16ChSelR0);

        if (M4_ADC1 == ADCx)
        {
            ADCx->CHSELRA1 &= (uint16_t)(~u16ChSelR1);
            ADCx->CHSELRB1 &= (uint16_t)(~u16ChSelR1);
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief ADC interrupt configuration.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u8Seq                   The sequence to be configured.
 **
 ** \param [in] enState                 Enable or Disable sequence conversion done interrupt.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_SeqITCmd(M4_ADC_TypeDef *ADCx,
                         uint8_t u8Seq,
                         en_functional_state_t enState)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint8_t u8Msk = 0u;

    if ((NULL != ADCx) && (u8Seq <= ADC_SEQ_B))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

        u8Msk = (uint8_t)(0x1ul << u8Seq);
        ADCx->ISR &= (uint8_t)(~u8Msk);

        if (Enable == enState)
        {
            ADCx->ICR |= u8Msk;
        }
        else
        {
            ADCx->ICR &= (uint8_t)(~u8Msk);
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief ADC average conversion configuration.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] enAvgCnt                Average after enAvgCnt conversions.
 **                                     See @ref en_adc_avcnt_t for details.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_ConfigAvg(M4_ADC_TypeDef *ADCx, en_adc_avcnt_t enAvgCnt)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_ADC_AVCNT(enAvgCnt));

        ADCx->CR0_f.AVCNT = enAvgCnt;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Add average channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32Channel              The channel(s), which will be set as average channel(s).
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ** \note                               The channel must first be configured as an analog channel
 **                                     by function ADC_AddAdcChannel.
 **
 ******************************************************************************/
en_result_t ADC_AddAvgChannel(M4_ADC_TypeDef *ADCx, uint32_t u32Channel)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint16_t u16AvgChR0;
    uint16_t u16AvgChR1;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        if (M4_ADC1 == ADCx)
        {
            u32Channel &= ADC1_CH_ALL;
        }
        else
        {
            u32Channel &= ADC2_CH_ALL;
        }

        u16AvgChR0 = (uint16_t)u32Channel;
        u16AvgChR1 = (uint16_t)(u32Channel >> 16u);

        ADCx->AVCHSELR0 |= u16AvgChR0;
        if (M4_ADC1 == ADCx)
        {
            ADCx->AVCHSELR1 |= u16AvgChR1;
        }
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Delete average channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32Channel              The average channel(s) which you want to delete.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_DelAvgChannel(M4_ADC_TypeDef *ADCx, uint32_t u32Channel)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint16_t u16AvgChR0;
    uint16_t u16AvgChR1;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        u16AvgChR0 = (uint16_t)u32Channel;
        u16AvgChR1 = (uint16_t)(u32Channel >> 16u);

        ADCx->AVCHSELR0 &= (uint16_t)(~u16AvgChR0);
        if (M4_ADC1 == ADCx)
        {
            ADCx->AVCHSELR1 &= (uint16_t)(~u16AvgChR1);
        }
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief ADC AWD(analog watch dog) configuration.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] pstcAwdCfg              Pointer to the configuration structure.
 **                                     See @ref stc_adc_awd_cfg_t for details.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_ConfigAwd(M4_ADC_TypeDef *ADCx, const stc_adc_awd_cfg_t *pstcAwdCfg)
{
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != ADCx) && (NULL != pstcAwdCfg))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_ADC_AWDMD(pstcAwdCfg->enAwdmd));
        DDL_ASSERT(IS_ADC_AWDSS(pstcAwdCfg->enAwdss));

        ADCx->AWDCR_f.AWDEN  = AdcAwd_Disable;
        ADCx->AWDCR_f.AWDIEN = AdcAwdInt_Disable;
        ADCx->AWDCR_f.AWDMD  = pstcAwdCfg->enAwdmd;
        ADCx->AWDCR_f.AWDSS  = pstcAwdCfg->enAwdss;

        ADCx->AWDDR0 = pstcAwdCfg->u16AwdDr0;
        ADCx->AWDDR1 = pstcAwdCfg->u16AwdDr1;

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Enable or disable ADC AWD(analog watch dog).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] enState                 Enable or disable AWD.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_AwdCmd(M4_ADC_TypeDef *ADCx, en_functional_state_t enState)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

        ADCx->AWDCR_f.AWDEN = enState;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Enable or disable ADC AWD(analog watch dog) interrupt.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] enState                 Enable or disable AWD interrupt.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_AwdITCmd(M4_ADC_TypeDef *ADCx, en_functional_state_t enState)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

        ADCx->AWDCR_f.AWDIEN = enState;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Add AWD channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32Channel              The channel(s), which will be set as AWD channel(s).
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ** \note                               The channel must first be configured as an analog channel
 **                                     by function ADC_AddAdcChannel.
 **
 ******************************************************************************/
en_result_t ADC_AddAwdChannel(M4_ADC_TypeDef *ADCx, uint32_t u32Channel)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint16_t    u16ChSelR0;
    uint16_t    u16ChSelR1;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        if (M4_ADC1 == ADCx)
        {
            u32Channel &= ADC1_CH_ALL;
        }
        else
        {
            u32Channel &= ADC2_CH_ALL;
        }

        u16ChSelR0 = (uint16_t)u32Channel;
        u16ChSelR1 = (uint16_t)(u32Channel >> 16u);

        ADCx->AWDCHSR0 |= u16ChSelR0;
        if (M4_ADC1 == ADCx)
        {
            ADCx->AWDCHSR1 |= u16ChSelR1;
        }
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Delete AWD channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32Channel              The AWD channel(s) which you are going to delete.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_DelAwdChannel(M4_ADC_TypeDef *ADCx, uint32_t u32Channel)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint16_t    u16ChSelR0;
    uint16_t    u16ChSelR1;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        u16ChSelR0 = (uint16_t)u32Channel;
        u16ChSelR1 = (uint16_t)(u32Channel >> 16u);

        ADCx->AWDCHSR0 &= (uint16_t)(~u16ChSelR0);
        if (M4_ADC1 == ADCx)
        {
            ADCx->AWDCHSR1 &= (uint16_t)(~u16ChSelR1);
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief ADC programmable gain amplifier(PGA) configuration.
 **
 ** \param [in] enFactor                PGA gain factor.
 ** \param [in] enNegativeIn            PGA negative input select.
 **
 ** \retval None.
 **
 ** \note                               Only ADC1 has PGA.
 **
 ******************************************************************************/
void ADC_ConfigPga(en_adc_pga_factor_t enFactor, en_adc_pga_negative_t enNegativeIn)
{
    DDL_ASSERT(IS_ADC_PGA_FACTOR(enFactor));
    DDL_ASSERT(IS_ADC_PGA_NEGATIVE(enNegativeIn));

    M4_ADC1->PGAGSR_f.GAIN       = enFactor;
    M4_ADC1->PGAINSR1_f.PGAVSSEN = enNegativeIn;
}

/**
 *******************************************************************************
 ** \brief Enable or disable PGA.
 **
 ** \param [in] enState                 Enable or disable PGA.
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_PgaCmd(en_functional_state_t enState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

    if (Enable == enState)
    {
        M4_ADC1->PGACR_f.PGACTL = AdcPgaCtl_Amplify;
    }
    else
    {
        M4_ADC1->PGACR_f.PGACTL = AdcPgaCtl_Invalid;
    }
}

/**
 *******************************************************************************
 ** \brief Select PGA channel.
 **
 ** \param[in] u16Channel               The channel, which you want to gain.
 **
 ** \retval None.
 **
 ** \note                               Only ADC1 has PGA. The channel must first
 **                                     be configured as an analog channel
 **                                     by function ADC_AddAdcChannel
 **
 ******************************************************************************/
void ADC_PgaSelChannel(uint16_t u16Channel)
{
    DDL_ASSERT(IS_ADC_PGA_CH(u16Channel));
    M4_ADC1->PGAINSR0 = u16Channel;
}

/**
 *******************************************************************************
 ** \brief ADC sync mode configuration.
 **
 ** \param [in] enMode                  Synchronous mode types.
 ** \param [in] u8TrgDelay              ADC2 trigger delay time.
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_ConfigSync(en_adc_sync_mode_t enMode, uint8_t u8TrgDelay)
{
    DDL_ASSERT(IS_ADC_SYNC_MODE(enMode));

    /* Disable synchronous mode first. */
    M4_ADC1->SYNCCR_f.SYNCEN  = AdcSync_Disable;

    M4_ADC1->SYNCCR_f.SYNCMD  = enMode;
    M4_ADC1->SYNCCR_f.SYNCDLY = u8TrgDelay;
}

/**
 *******************************************************************************
 ** \brief Enable or disable sync mode.
 **
 ** \param [in] enState                 Enable or disable sync mode.
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_SyncCmd(en_functional_state_t enState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enState));

    M4_ADC1->SYNCCR_f.SYNCEN = enState;
}

/**
 *******************************************************************************
 ** \brief Start an ADC conversion.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ** \note                               Software startup only support sequence A.
 **
 ******************************************************************************/
en_result_t ADC_StartConvert(M4_ADC_TypeDef *ADCx)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        ADCx->STR = 1u;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Stop an ADC conversion and clear flags.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval ErrorTimeout                Timeout.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_StopConvert(M4_ADC_TypeDef *ADCx)
{
    en_result_t enRet = ErrorInvalidParameter;
    __IO uint32_t u32TimeCount = 0u;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        enRet = Ok;
        /* Make sure the ADC is really stopped. */
        while (ADCx->STR == 1u)
        {
            /* Stop ADC conversion. */
            ADCx->STR = 0u;
            if (++u32TimeCount > 10000u)
            {
                enRet = ErrorTimeout;
                break;
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get the conversion status flag.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u8Seq                   The sequence which you want to get
 **                                     it's conversion status flag.
 **
 ** \retval Set                         ADC converted done.
 ** \retval Reset                       ADC is converting or parameter error.
 **
 ******************************************************************************/
en_flag_status_t ADC_GetEocFlag(const M4_ADC_TypeDef *ADCx, uint8_t u8Seq)
{
    en_flag_status_t enFlag = Reset;

    DDL_ASSERT(IS_ADC_PERIPH(ADCx));

    if (ADCx->ISR & ((uint8_t)(0x1ul << u8Seq)))
    {
        enFlag = Set;
    }

    return enFlag;
}

/**
 *******************************************************************************
 ** \brief Clear conversion status flag of sequence A or sequence B.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u8Seq                   The sequence which you want to clear
 **                                     it's conversion status flag.
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_ClrEocFlag(M4_ADC_TypeDef *ADCx, uint8_t u8Seq)
{
    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        ADCx->ISR &= (uint8_t)(~(0x1ul << u8Seq));
    }
}

/**
 *******************************************************************************
 ** \brief ADC start sequence A, check it's EOC status and get the data.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [out] pu16AdcData            The address to store ADC value.
 **                                     The location of the data store depends on
 **                                     the parameter u8Length.
 **                                     u8Length >= ADCx_CH_COUNT(ADC1_CH_COUNT or ADC2_CH_COUNT),
 **                                     all of the ADC data regs will be read:
 **                                     pu16AdcData[0] = data of Channel 0,
 **                                     pu16AdcData[1] = data of Channel 1,
 **                                     pu16AdcData[2] = data of Channel 2,
 **                                                 ...
 **                                     u8Length < ADCx_CH_COUNT(ADC1_CH_COUNT or ADC2_CH_COUNT),
 **                                     only the data of the enabled channles will be read:
 **                                     pu16AdcData[0] = data of the 1st enabled channel,
 **                                     pu16AdcData[1] = data of the 2nd enabled channel,
 **                                     pu16AdcData[2] = data of the 3rd enabled channel,
 **                                                 ...
 **
 ** \param [in] u8Length                The length of the ADC data to be read.
 **
 ** \param [in] u32Timeout              Timeout value.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval ErrorTimeout                Timeout.
 ** \retval OperationInProgress         ADC is converting.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_PollingSa(M4_ADC_TypeDef *ADCx,
                          uint16_t *pu16AdcData,
                          uint8_t u8Length,
                          uint32_t u32Timeout)
{
    en_result_t enRet          = ErrorInvalidParameter;
    uint8_t u8AllDataLength    = 0u;
    uint32_t u32Channel        = 0u;
    uint32_t u32AdcTimeout     = 0u;
    __IO uint32_t u32TimeCount = 0u;

    if ((NULL != ADCx) && (NULL != pu16AdcData) && (0u != u8Length))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        if (M4_ADC1 == ADCx)
        {
            if (u8Length >= ADC1_CH_COUNT)
            {
                u8AllDataLength = ADC1_CH_COUNT;
            }
            else
            {
                u32Channel   = (uint32_t)ADCx->CHSELRA1;
                u32Channel <<= 16u;
                u32Channel  |= (uint32_t)ADCx->CHSELRA0;
            }
        }
        else
        {
            if (u8Length >= ADC2_CH_COUNT)
            {
                u8AllDataLength = ADC2_CH_COUNT;
            }
            else
            {
                u32Channel = (uint32_t)M4_ADC2->CHSELRA0;
            }
        }

        /* Start ADC conversion. */
        ADCx->STR = (uint8_t)0x01;

        /* 10 is the number of required instructions cycles for the below loop statement. */
        u32AdcTimeout = u32Timeout * (SystemCoreClock / 10u / 1000u);
        u32TimeCount  = 0u;
        enRet         = ErrorTimeout;
        while (u32TimeCount < u32AdcTimeout)
        {
            if (ADCx->ISR_f.EOCAF)
            {
                /* Get ADC data. */
                if (u8AllDataLength)
                {
                    ADC_ReadAllData(ADCx, pu16AdcData, u8AllDataLength);
                }
                else
                {
                    ADC_GetChData(ADCx, u32Channel, pu16AdcData, u8Length);
                }

                /* Clear sequence A flag. */
                ADCx->ISR_f.EOCAF = 0u;
                enRet = Ok;
                break;
            }
            u32TimeCount++;
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Reading all data regs of an ADC.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [out] pu16AdcData            The address where the data will be stored.
 **                                     pu16AdcData[0] = data of Channel 0,
 **                                     pu16AdcData[1] = data of Channel 1,
 **                                     pu16AdcData[2] = data of Channel 2,
 **                                                 ...
 **
 ** \param [in] u8Length                The length of the ADC data to be read.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_GetAllData(const M4_ADC_TypeDef *ADCx, uint16_t *pu16AdcData, uint8_t u8Length)
{
    en_result_t enRet = ErrorInvalidParameter;

    DDL_ASSERT(IS_ADC_PERIPH(ADCx));
    if (((M4_ADC1 == ADCx) && (u8Length >= ADC1_CH_COUNT)) ||
        ((M4_ADC2 == ADCx) && (u8Length >= ADC2_CH_COUNT)))
    {
        ADC_ReadAllData(ADCx, pu16AdcData, u8Length);
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Reading the data of the specified channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base
 ** \arg M4_ADC1                        ADC unit 1 instance register base
 ** \arg M4_ADC2                        ADC unit 2 instance register base
 **
 ** \param [in] u32TargetCh             The specified channel(s)
 **
 ** \param [out] pu16AdcData            The address where the data will be stored.
 **                                     pu16AdcData[0] = data of the 1st enabled channel,
 **                                     pu16AdcData[1] = data of the 2nd enabled channel,
 **                                     pu16AdcData[2] = data of the 3rd enabled channel,
 **                                     eg. u32TargetCh = 1001b
 **                                     pu16AdcData[0] = Channel 0's data,
 **                                     pu16AdcData[1] = Channel 3's data,
 **
 ** \param [in] u8Length                The length of the ADC data to be read.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_GetChData(const M4_ADC_TypeDef *ADCx,
                          uint32_t u32TargetCh,
                          uint16_t *pu16AdcData,
                          uint8_t u8Length)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint8_t i;
    uint8_t j;
    uint32_t u32Channel;
    __IO const uint16_t *pu16DataReg = &(ADCx->DR0);

    if ((NULL != ADCx) && (NULL != pu16AdcData) && (0u != u8Length))
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        if (M4_ADC1 == ADCx)
        {
            u32Channel = u32TargetCh & ADC1_CH_ALL;
        }
        else
        {
            u32Channel = u32TargetCh & ADC2_CH_ALL;
        }

        i = 0u;
        j = 0u;
        while ((0u != u32Channel) && (0u != u8Length))
        {
            if (0u != (u32Channel & 0x1ul))
            {
                pu16AdcData[j] = pu16DataReg[i];
                j++;
                u8Length--;
            }

            u32Channel >>= 1u;
            i++;
        }

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get the value of a specified channel via channel index.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base
 ** \arg M4_ADC1                        ADC unit 1 instance register base
 ** \arg M4_ADC2                        ADC unit 2 instance register base
 **
 ** \param [in] u8ChIndex               The index of the specified channel.
 **                                     u8ChIndex < ADC1_CH_COUNT while ADCx == M4_ADC1;
 **                                     u8ChIndex < ADC2_CH_COUNT while ADCx == M4_ADC2.
 **
 ** \retval An uint16_t value -- the ADC value of the specified channel.
 **
 ******************************************************************************/
uint16_t ADC_GetValue(const M4_ADC_TypeDef *ADCx, uint8_t u8ChIndex)
{
    __IO const uint16_t *pu16DataReg = &(ADCx->DR0);

    DDL_ASSERT(IS_ADC_PERIPH(ADCx));

    return pu16DataReg[u8ChIndex];
}

/**
 *******************************************************************************
 ** \brief Get all AWD channels status flags.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \retval u32AwdFlag                  0  -- No ADC channel meets AWD comparison conditions.
 **                                     !0 -- The bit value of the channel that satisfies the
 **                                           AWD condition is 1.
 **
 ******************************************************************************/
uint32_t ADC_GetAwdFlag(const M4_ADC_TypeDef *ADCx)
{
    uint32_t u32AwdFlag;

    DDL_ASSERT(IS_ADC_PERIPH(ADCx));

    if (M4_ADC1 == ADCx)
    {
        u32AwdFlag = ADCx->AWDSR1;
        u32AwdFlag <<= 16u;
        u32AwdFlag |= ADCx->AWDSR0;
    }
    else
    {
        u32AwdFlag = ADCx->AWDSR0;
    }

    return u32AwdFlag;
}

/**
 *******************************************************************************
 ** \brief Clear all AWD channels status flags
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_ClrAwdFlag(M4_ADC_TypeDef *ADCx)
{
    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        ADCx->AWDSR0 = 0u;
        if (M4_ADC1 == ADCx)
        {
            ADCx->AWDSR0 = 0u;
            ADCx->AWDSR1 = 0u;
        }
    }
}

/**
 *******************************************************************************
 ** \brief Clear AWD specified channels status flags.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32AwdCh                The channel(s) which you want to clear it's flag(s).
 **
 ** \retval None.
 **
 ******************************************************************************/
void ADC_ClrAwdChFlag(M4_ADC_TypeDef *ADCx, uint32_t u32AwdCh)
{
    uint16_t u16ChR0;
    uint16_t u16ChR1;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));

        u16ChR0 = (uint16_t)u32AwdCh;
        u16ChR1 = (uint16_t)(u32AwdCh >> 16u);

        ADCx->AWDSR0 &= (uint16_t)(~u16ChR0);
        if (M4_ADC1 == ADCx)
        {
            ADCx->AWDSR1 &= (uint16_t)(~u16ChR1);
        }
    }
}

/**
 *******************************************************************************
 ** \brief Remap an ADC pin to channel(s).
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u32DestChannel          Destination channel(s).
 **
 ** \param [in] u8AdcPin                ADC pin number.
 **
 ** \retval ErrorInvalidParameter       Parameter error.
 ** \retval Ok                          No error occurred.
 **
 ******************************************************************************/
en_result_t ADC_ChannelRemap(M4_ADC_TypeDef *ADCx,
                             uint32_t u32DestChannel,
                             uint8_t u8AdcPin)
{
    en_result_t enRet = ErrorInvalidParameter;
    uint8_t i;
    uint8_t u8OffsetReg;
    uint8_t u8ChPos;
    uint16_t u16AdcPin = u8AdcPin;
    __IO uint16_t *io16Chmuxr = NULL;

    if (NULL != ADCx)
    {
        DDL_ASSERT(IS_ADC_PERIPH(ADCx));
        enRet = Ok;
        if (M4_ADC1 == ADCx)
        {
            if (u16AdcPin <= ADC1_IN15)
            {
                u32DestChannel &= ADC1_PIN_MASK_ALL;
            }
            else
            {
                enRet = ErrorInvalidParameter;
            }
        }
        else
        {
            if ((u16AdcPin >= ADC12_IN4) && (u16AdcPin <= ADC12_IN11))
            {
                u16AdcPin -= 4u;
                u32DestChannel &= ADC2_PIN_MASK_ALL;
            }
            else
            {
                enRet = ErrorInvalidParameter;
            }
        }

        if (Ok == enRet)
        {
            i = 0u;
            while (0u != u32DestChannel)
            {
                if (u32DestChannel & 0x1ul)
                {
                    u8OffsetReg  = i / 4u;
                    u8ChPos      = (i % 4u) * 4u;
                    io16Chmuxr   = &(ADCx->CHMUXR0) + u8OffsetReg;
                    *io16Chmuxr &= (uint16_t)(~(0xFul << u8ChPos));
                    *io16Chmuxr |= (uint16_t)(u16AdcPin << u8ChPos);
                }

                u32DestChannel >>= 1u;
                i++;
            }
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get the number of the pin corresponding to the specified channel.
 **
 ** \param [in] ADCx                    Pointer to ADC instance register base.
 ** \arg M4_ADC1                        ADC unit 1 instance register base.
 ** \arg M4_ADC2                        ADC unit 2 instance register base.
 **
 ** \param [in] u8ChIndex               This channel that you want to get its pin number.
 **
 ** \retval [0, 15]                     The correct ADC pin number.
 ** \retval [0xFF]                      The invalid ADC pin number.
 **
 ******************************************************************************/
uint8_t ADC_GetChannelPinNum(const M4_ADC_TypeDef *ADCx, uint8_t u8ChIndex)
{
    uint8_t u8OffsetPin;
    uint8_t u8OffsetReg;
    uint8_t u8ChPos;
    uint8_t u8AdcPin = ADC_PIN_INVALID;
    __IO const uint16_t *io16Chmuxr = NULL;

    DDL_ASSERT(IS_ADC_PERIPH(ADCx));

    if (M4_ADC1 == ADCx)
    {
        DDL_ASSERT(IS_ADC1_CH_INDEX(u8ChIndex));
        u8OffsetPin = 0u;
    }
    else
    {
        DDL_ASSERT(IS_ADC2_CH_INDEX(u8ChIndex));
        u8OffsetPin = 4u;
    }

    u8OffsetReg = u8ChIndex / 4u;
    u8ChPos     = (u8ChIndex % 4u) * 4u;
    io16Chmuxr  = &(ADCx->CHMUXR0) + u8OffsetReg;
    u8AdcPin    = (uint8_t)((*io16Chmuxr >> u8ChPos) & ((uint16_t)0xF));
    u8AdcPin   += u8OffsetPin;

    return u8AdcPin;
}

/*******************************************************************************
 * Function implementation - local ('static')
 ******************************************************************************/
/**
 *******************************************************************************
 ** \brief Read all data of an ADC. pu16AdcData[0] = DR0, pu16AdcData[1] = DR1, ...
 **
 ******************************************************************************/
static void ADC_ReadAllData(const M4_ADC_TypeDef *ADCx, uint16_t *pu16AdcData, uint8_t u8Length)
{
    uint8_t i;
    __IO const uint16_t *pu16DataReg = &(ADCx->DR0);

    for (i = 0u; i < u8Length; i++)
    {
        pu16AdcData[i] = pu16DataReg[i];
    }
}

//@} // AdcGroup

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
