/******************************************************************************
 * @file     comp.c
 * @brief    Comparator firmware functions.
 * @version  V4.0.0
 * @date     30-July-2026
 *
 * @note
 * Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd.. All rights reserved.
 *
 * @attention
 * THIS SOFTWARE JUST PROVIDES CUSTOMERS WITH CODING INFORMATION REGARDING
 * THEIR PRODUCTS, WHICH AIMS AT SAVING TIME FOR THEM. SPINTROL SHALL NOT BE
 * LIABLE FOR THE USE OF THE SOFTWARE. SPINTROL DOES NOT GUARANTEE THE
 * CORRECTNESS OF THIS SOFTWARE AND RESERVES THE RIGHT TO MODIFY THE SOFTWARE
 * WITHOUT NOTIFICATION.
 *
 ******************************************************************************/


#include "comp.h"


#if defined(__CC_ARM)
    #pragma push
    #pragma O1
#endif




/******************************************************************************
 * @brief      Configure Comparator
 *
 * @param[in]  eCOMP              :  Select the comparator
 *                                   See COMP_ComparatorSelEnum for details
 * @param[in]  eCH                :  Select the comparator input channel
 *                                   See COMP_CHSelEnum for details
 * @param[in]  i32DACVoltageMV    :  DAC voltage range from 0(0 voltage) ~ 3300 (3300 mV = 3.3 V)
 * @param[in]  u32DeglicthWindowNs:  Filter sample window (nero second)
 *                                   If Filter Clock =10MHZ, its range from 0~31*(1/10MHz)= 3100 ns= 3.1 us
 *
 * @return     none
 *
 * @note       (1) All 3 COMP_x_LO share same DAC_LO(DAC3)
 *             (2) All 3 COMP_x_HI share same DAC_HI(DAC2)
 *             (3) This function do NOT saturate DAC value and deglitch(filter) value, please note it!
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_Init(COMP_ComparatorSelEnum eCOMP, COMP_CHSelEnum eCH, int32_t i32DACVoltageMV, uint32_t u32DeglicthWindowNs)
{
    /* Enable Comparator clock */
    CLOCK_EnableModule(COMP_MODULE);
    
    /* Enable the ADC Bandgap */
    ADC_EnableBandgap();

    /* Enable comparator */
    COMP_Enable(eCOMP);

    /* Set Input channe1 */
    COMP_SelectChannel(eCOMP, eCH);

    /* Set Reference channel - All COMP use channel 2 : DAC2 as Too-High reference and DAC3 as Too-Low reference */
    switch (eCOMP)
    {
        case COMP_0_LO:
        case COMP_0_HI:
            COMP->COMP0CTL.bit.REFSEL = 2;
            break;

        case COMP_1_LO:
        case COMP_1_HI:
            COMP->COMP1CTL.bit.REFSEL = 2;
            break;

        case COMP_2_LO:
        case COMP_2_HI:
            COMP->COMP2CTL.bit.REFSEL = 2;
            break;

        case COMP_3_LO:
        case COMP_3_HI:
            COMP->COMP3CTL.bit.REFSEL = 2;
            break;

        case COMP_4_LO:
        case COMP_4_HI:
            COMP->COMP4CTL.bit.REFSEL = 2;
            break;
    }

    /* Set comparator output = Filtered */
    COMP_SetOutputType(eCOMP, COMP_OUTPUT_FILTERED);

    /* Set Filter window - Threshld value = 0.6 * Window Size */
    COMP_SetFilterWindowTimeNs(eCOMP, u32DeglicthWindowNs, u32DeglicthWindowNs * 3 / 5);

    /* Enable DAC for comparison */
    COMP_EnableDAC(DAC2);
    COMP_EnableDAC(DAC3);

    /* Set DAC value */
    switch (eCOMP)
    {
        case COMP_0_HI:
        case COMP_1_HI:
        case COMP_2_HI:
        case COMP_3_HI:
        case COMP_4_HI:
            COMP_SetDACVoltage(DAC2, i32DACVoltageMV);
            break;

        case COMP_0_LO:
        case COMP_1_LO:
        case COMP_2_LO:
        case COMP_3_LO:
        case COMP_4_LO:
            COMP_SetDACVoltage(DAC3, i32DACVoltageMV);
            break;
    }

    /* Clear latched filter status */
    COMP_ClearAllFilterOutputStatus();
}




/******************************************************************************
 * @brief      Set Filter Window Size (clock cycles)
 *
 * @param[in]  eCOMP     : Select the comparator
 * @param[in]  eOutputSel: Select the comparator output
 *
 * @return     none
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_SetOutputType(COMP_ComparatorSelEnum eCOMP, COMP_OutputSelEnum eOutputSel)
{
    switch (eCOMP)
    {
        case COMP_0_LO:
            COMP->COMP0LCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_0_HI:
            COMP->COMP0HCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_1_LO:
            COMP->COMP1LCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_1_HI:
            COMP->COMP1HCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_2_LO:
            COMP->COMP2LCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_2_HI:
            COMP->COMP2HCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_3_LO:
            COMP->COMP3LCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_3_HI:
            COMP->COMP3HCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_4_LO:
            COMP->COMP4LCTL.bit.SEL4PWM = eOutputSel;
            break;

        case COMP_4_HI:
            COMP->COMP4HCTL.bit.SEL4PWM = eOutputSel;
            break;
    }
}




/******************************************************************************
 * @brief      Set Filter Window Size and Threshold
 *
 * @param[in]  eCOMP      : Select the comparator
 * @param[in]  u8Size     : Filter window size (1 ~ 32)
 * @param[in]  u8Threshold: Filter threshold value (1 ~ 32)
 *
 * @return     none
 *
 * @note       u8Threshold must bigger than u8Size/2
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_SetFilterWindow(COMP_ComparatorSelEnum eCOMP, uint8_t u8Size, uint8_t u8Threshold)
{
    /* Invalid Window size */
    if ((u8Size == 0) || (u8Size > 32))
    {
        return;
    }

    /* Invalid Window Threshold */
    if ((u8Threshold == 0) || (u8Threshold > 32))
    {
        return;
    }

    /* Configure Filter Window */
    switch (eCOMP)
    {
        case COMP_0_LO:
            COMP->COMP0LCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP0LCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_0_HI:
            COMP->COMP0HCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP0HCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_1_LO:
            COMP->COMP1LCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP1LCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_1_HI:
            COMP->COMP1HCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP1HCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_2_LO:
            COMP->COMP2LCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP2LCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_2_HI:
            COMP->COMP2HCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP2HCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_3_LO:
            COMP->COMP3LCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP3LCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_3_HI:
            COMP->COMP3HCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP3HCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_4_LO:
            COMP->COMP4LCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP4LCTL.bit.FLTTH  = u8Threshold - 1;
            break;

        case COMP_4_HI:
            COMP->COMP4HCTL.bit.FLTWIN = u8Size - 1;
            COMP->COMP4HCTL.bit.FLTTH  = u8Threshold - 1;
            break;
    }
}



/******************************************************************************
 * @brief      Set Filter Window Size and Threshold (nero second)
 *
 * @param[in]  eCOMP             : Select the comparator
 * @param[in]  u32SizeTimeNS     : Window Size (Time in ns)
 * @param[in]  u32ThresholdTimeNS: Threshold (Time in ns)
 *
 * @return     none
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_SetFilterWindowTimeNs(COMP_ComparatorSelEnum eCOMP, uint32_t u32SizeTimeNS, uint32_t u32ThresholdTimeNS)
{
    uint32_t u32TimeCnt1, u32TimeCnt2;
    uint32_t u32FilterDiv;

    /* Use the max Filter Window Size(32U) to caculate minimum Filter Div */
    u32FilterDiv = CLOCK_GetModuleClock(COMP_MODULE) / 100000U * (u32SizeTimeNS) / (32U * 10000U);

    /* If minimum Filter Div if too small, increase the Filter Div to legal range, at this time, Filter Window Size is decreased */
    if (u32FilterDiv == 0U)
    {
        /* Use the minimum Filter Div to recaculate Filter Window Size, at this time, 0U <= Filter Window Size < 32U*/
        u32FilterDiv++;
        u32TimeCnt1 = (CLOCK_GetModuleClock(COMP_MODULE) / 100000U * (u32SizeTimeNS) /u32FilterDiv / 10000U);
    }
    /* else if minimum Filter Div if too big, decrease the Filter Div to legal range, at this time, Filter Window Size is increased,
    force Filter Window Size to legal range*/
    else if (u32FilterDiv >= 1024U)
    {
        u32FilterDiv = 1024U;
        u32TimeCnt1 = 32U;
    }
    else
    {
        /* Use the minimum Filter Div to recaculate Filter Window Size */
        u32TimeCnt1 = (CLOCK_GetModuleClock(COMP_MODULE) / 100000U * (u32SizeTimeNS) /u32FilterDiv / 10000U);

        /* If minimum Filter Div can not set Filter Window Size <= 32U, use the (u32FilterDiv + 1) to recaculate Filter Window Size, 
        at this time, Filter Window Size is decreased */
        if (u32TimeCnt1 > 32U)
        {
            /* Use the minimum Filter Div to recaculate Filter Window Size, at this time, 0U <= Filter Window Size < 32U*/
            u32FilterDiv++;
            u32TimeCnt1 = (CLOCK_GetModuleClock(COMP_MODULE) / 100000U * (u32SizeTimeNS) /u32FilterDiv / 10000U);
        }
    }

    /* If Filter Window Size is decreased to 0, set to 1 */
    if (u32TimeCnt1 == 0U)
    {
        u32TimeCnt1 = 1U;
    }

    /* Caculate Filter Threshold */
    u32TimeCnt2 = CLOCK_GetModuleClock(COMP_MODULE) / 100000U * (u32ThresholdTimeNS) / u32FilterDiv / 10000U;

    /* Threshold value must be bigger than WindowSize/2 */
    if ((u32TimeCnt2 * 2) < u32TimeCnt1)
    {
        u32TimeCnt2 = u32TimeCnt1 / 2 + 1;
    }

    if (u32TimeCnt2 == 0)
    {
        u32TimeCnt2 = 1;
    }
    else if (u32TimeCnt2 > 32)
    {
        u32TimeCnt2 = 32;
    }

    /* Set Filter Div */
    COMP_SetFilterClockDiv(eCOMP, u32FilterDiv);

    COMP_SetFilterWindow(eCOMP, u32TimeCnt1, u32TimeCnt2);
}




/******************************************************************************
 * @brief      Set Filter Clock Divider
 *
 * @param[in]  eCOMP    : Select the comparator
 *                        See COMP_ComparatorSelEnum for details
 * @param[in]  u16ClkDiv: Filter clock divider
 *
 * @return     none
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_SetFilterClockDiv(COMP_ComparatorSelEnum eCOMP, uint16_t u16ClkDiv)
{
    if ((u16ClkDiv == 0) || (u16ClkDiv > 1024))
    {
        return;
    }

    switch (eCOMP)
    {
        case COMP_0_LO:
            COMP->COMP0LCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_0_HI:
            COMP->COMP0HCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_1_LO:
            COMP->COMP1LCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_1_HI:
            COMP->COMP1HCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_2_LO:
            COMP->COMP2LCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_2_HI:
            COMP->COMP2HCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_3_LO:
            COMP->COMP3LCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_3_HI:
            COMP->COMP3HCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_4_LO:
            COMP->COMP4LCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;

        case COMP_4_HI:
            COMP->COMP4HCTL.bit.FLTDIV = u16ClkDiv - 1;
            break;
    }
}




/******************************************************************************
 * @brief      Set comparator hysteresis
 *
 * @param[in]  eCOMP   : Select the comparator
 *                       See COMP_ComparatorSelEnum for details
 * @param[in]  eHystSel: Select the hysteresis
 *                       See COMP_HystSelEnum for details
 *
 * @return     none
 *
 * @example    COMP_SetHysteresis(COMP_0_HI, COMP_HYST_12_MV);
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_SetHysteresis(COMP_ComparatorSelEnum eCOMP, COMP_HystSelEnum eHystSel)
{
    switch (eCOMP)
    {
        case COMP_0_LO:
        case COMP_0_HI:
            COMP->COMP0CTL.bit.HYSTSEL = eHystSel;
            break;

        case COMP_1_LO:
        case COMP_1_HI:
            COMP->COMP1CTL.bit.HYSTSEL = eHystSel;
            break;

        case COMP_2_LO:
        case COMP_2_HI:
            COMP->COMP2CTL.bit.HYSTSEL = eHystSel;
            break;

        case COMP_3_LO:
        case COMP_3_HI:
            COMP->COMP3CTL.bit.HYSTSEL = eHystSel;
            break;

        case COMP_4_LO:
        case COMP_4_HI:
            COMP->COMP4CTL.bit.HYSTSEL = eHystSel;
            break;
    }
}




/******************************************************************************
 * @brief      Select the comparator input channel
 *
 * @param[in]  eCOMP:  Select the comparator
 *                     See COMP_ComparatorSelEnum for details
 * @param[in]  eCH  :  Select the comparator input channel
 *                     See COMP_CHSelEnum for details
 *
 * @return     none
 *
 * @example    COMP_SetChannel(COMP_0_HI,COMP0_FROM_PGA0N_OUT)
 *             The above means COMP_0_HI comparator input channel is from PGA0 negative output
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_SelectChannel(COMP_ComparatorSelEnum eCOMP, COMP_CHSelEnum eCH)
{
    switch (eCOMP)
    {
        case COMP_0_LO:
        case COMP_0_HI:
            COMP->COMP0CTL.bit.INSEL = eCH & 0x0F;
            break;

        case COMP_1_LO:
        case COMP_1_HI:
            COMP->COMP1CTL.bit.INSEL = eCH & 0x0F;
            break;

        case COMP_2_LO:
        case COMP_2_HI:
            COMP->COMP2CTL.bit.INSEL = eCH & 0x0F;
            break;

        case COMP_3_LO:
        case COMP_3_HI:
            COMP->COMP3CTL.bit.INSEL = eCH & 0x0F;
            break;

        case COMP_4_LO:
        case COMP_4_HI:
            COMP->COMP4CTL.bit.INSEL = eCH & 0x0F;
            break;
    }
}




/******************************************************************************
 * @brief      Init DAC Buffer
 *
 * @param[in]  eDACSel : Select the DAC module
 *                       See COMP_DACSelEnum for details
 * @param[in]  bIsOut10: Output to GPIO10
 *                       TRUE or FALSE
 * @param[in]  bIsOut13: Output to GPIO13
 *                       TRUE or FALSE
 *
 * @return     none
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void COMP_DACBufferInit(COMP_DACSelEnum eDACSel, BOOL bIsOut10, BOOL bIsOut13)
{
    /* Enable ADC Bandgap */
    ADC_EnableBandgap();

    COMP->DACBUFCTL.all = 0;

    /* Enable DAC Buffer */
    COMP->DACBUFCTL.bit.EN = DACBUFCTL_BIT_EN_ENABLE;

    /* Select DAC Buffer Input */
    COMP->DACBUFCTL.bit.INSEL = eDACSel;

    if (bIsOut10)
    {
        COMP->DACBUFCTL.bit.OE10 = DACBUFCTL_BIT_OE10_ENABLE;
    }
    if (bIsOut13)
    {
        COMP->DACBUFCTL.bit.OE13 = DACBUFCTL_BIT_OE13_ENABLE;
    }
}


#if defined(__CC_ARM)
    #pragma pop
#endif


/******************* Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd. ***** END OF FILE ****/
