/**
 ******************************************************************************
 *
 * @file        MG32_OPA_DRV.c
 *
 * @brief       This file provides firmware functions to manage the following 
 *              functionalities of the OPA peripheral:
 *
 * @par         Project
 *              MG32
 * @version     V1.03
 * @date        2025/12/26
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2016 MegaWin Technology Co., Ltd.
 *              All rights reserved.
 *
 ******************************************************************************
 * @par 		Disclaimer 
 *		The Demo software is provided "AS IS"  without any warranty, either 
 *		expressed or implied, including, but not limited to, the implied warranties 
 *		of merchantability and fitness for a particular purpose.  The author will 
 *		not be liable for any special, incidental, consequential or indirect 
 *		damages due to loss of data or any other reason. 
 *		These statements agree with the world wide and local dictated laws about 
 *		authorship and violence against these laws. 
 ******************************************************************************
 ******************************************************************************
 */ 


#include "MG32.h"

#if defined(ModuleExist_OPA)

#include "MG32_OPA_DRV.h"


/**
 * @brief  	simple define
 *
 */ 

/**
 * @name	Deinitial OPA to default condition
 *   		
 */ 
///@{  
/**
 *******************************************************************************
 * @brief       Deinitializes the OPA peripheral registers to their default reset 
 *			    values.
 * @details     Reset OPAx to default configuration
 * @return		No
 * @note 
 * @par         Example
 * @code
    OPA_DeInit(OPA);
 * @endcode
 *******************************************************************************
 */
void OPA_DeInit(void)
{
     OPA->INT.W = 0x00000000;
//     OPA->ANA.W = 0x00000000;
     OPA->CR0.W = 0x00000000;
     OPA->STA.W = 0xFFFFFFFF;
}
///@}





/**
 * @name	Enable/Disable OPA macro
 *   		
 */ 
///@{ 
/**
 *******************************************************************************
 * @brief	    Enable or Disable OPA macro.
 * @param[in]   OPAx : OPA comparator module.
 * @param[in] 	NewState: new state of OPA.
 *      	    This parameter can be: ENABLE or DISABLE.
 * @return 	    none.
 * @note 
 * @par         Example.
 * @code
    OPA_Cmd(OPA_OP0, ENABLE);
 * @endcode
 *******************************************************************************
 */
void OPA_Cmd(OPA_OP_Struct* OPAx, FunctionalState NewState)
{
    if (NewState == ENABLE)
        OPAx->CR.W |= OPA_CR0_OP0_EN_enable_w;
    else
        OPAx->CR.W &= ~OPA_CR0_OP0_EN_enable_w;
        
}

/**
 *******************************************************************************
 * @brief	    Config OPA power mode.
 * @param[in]   OPAx : OPA comparator module.
 * @param[in] 	PowerLevelSel: the state of response time.
 *  @arg\b	    OPA_NormalPower : High power mode.
 *  @arg\b	    OPA_LowPower : Low power mode.
 * @return 	    none
 * @note 
 * @par         Example
 * @code
    OPA_Power_Select(OPA_OP0, OPA_LowPower);
 * @endcode
 *******************************************************************************
 */
void OPA_Power_Select(OPA_OP_Struct* OPAx, OPA_PowerLevelSel PowerLevelSel)
{
    OPAx->CR.W &= ~OPA_CR0_OP0_LPEN_mask_w;
    OPAx->CR.W |= PowerLevelSel;

}
///@}









/**
 * @name	config input source for OPA module
 *   		
 */ 
///@{ 
/**
 *******************************************************************************
 * @brief       Config OPAx positive input property.
 * @param[in]   OPAx : OPA comparator module.
 * @param[in] 	OP0PPin: select positive pin input.
 *  @arg\b	    OPA_P_VBUF : select internal reference be input 
 *  @arg\b	    OPA_P_OP0_I0 : select PA4 pin be input.
 *  @arg\b	    OPA_P_PGA : PGA output.
 * @return 	    none.
 * @note 
 * @par         Example.
 * @code
    OPA_PositivePin_Select(OPA_OP0, OPA_P_VBUF);
 * @endcode
 *******************************************************************************
 */
void OPA_PositivePin_Select(OPA_OP_Struct* OPAx, OPA_ACPinInputDef OP0PPin)
{
    OPAx->CR.W &= ~OPA_CR0_OP0_PMUX_mask_w;
    OPAx->CR.W |= (uint32_t) (OP0PPin);
    
}

/**
 *******************************************************************************
 * @brief       Config OPAx negative input property.
 * @param[in]   OPAx : OPA comparator module.
 * @param[in] 	OP0NPin: select positive pin input.
 *  @arg\b	    OPA_N_VBUF : select internal reference be input.
 *  @arg\b	    OPA_N_OP0_I1 : select PA5 pin be input.
 *  @arg\b	    OPA_N_OPA_P0 : OPA output.
 * @return 	    none.
 * @note 
 * @par         Example.
 * @code
    OPA_NegativePin_Select(OPA_OP0, OPA_N_OPA_P0);
 * @endcode
 *******************************************************************************
 */
void OPA_NegativePin_Select(OPA_OP_Struct* OPAx, OPA_ACNPinInputDef OP0NPin)
{
    OPAx->CR.W &= ~OPA_CR0_OP0_NMUX_mask_w;
    OPAx->CR.W |= (uint32_t) (OP0NPin);
    
}

/**
 *******************************************************************************
 * @brief       Config OPA offset level.
 * @param[in]   OPAx : OPA comparator module.
 * @param[in] 	Val: -16~15
 * @return 	    none.
 * @note 
 * @par         Example.
 * @code
    OPA_SetOffset(OPA_OP0, 3);
 * @endcode
 *******************************************************************************
 */
void OPA_SetOffset(OPA_OP_Struct* OPAx, int8_t Val)
{
    OPAx->CR.W &= ~OPA_CR0_OP0_OFFT_mask_w;
    OPAx->CR.W |= (uint32_t) (Val << 24);
    
}

///@}










/**
 * @name	Calibrate OPA offset
 *   		
 */ 
///@{ 
//
/**
 *******************************************************************************
 * @brief       Calibre OPA offset.
 * @param[in]   OPAx : OPA module.
 * @param[in] 	NewState: new state of the OPAx all interrupt key.
 *      	    This parameter can be: ENABLE or DISABLE.
 * @return 	    none
 * @note        Needs ADC convert function to calibrate OPA offset.
 * @par         Example
 * @code
    OPA_OffsetCalibration_Cmd(OPA_OP0, ENABLE);
 * @endcode
 *******************************************************************************
 */
void OPA_OffsetCalibration_Cmd (OPA_OP_Struct* OPAx, FunctionalState NewState)
{
    //
    __IO uint16_t Base_line, CompareVal;
    __IO uint8_t i;
     uint16_t Temp16bit;
   
    uint32_t rADC_REG[10];
    uint32_t rOPA_CR0;
   
    // ------------------------------------------------------------------------
    // check input parameter
    if (NewState == DISABLE)
        return;

    // ------------------------------------------------------------------------
    // Enable VBUF14
    UnProtectModuleReg(PWprotect);
    PW_VoltageBuffer(ENABLE);    
    ProtectModuleReg(PWprotect);
    
    // ------------------------------------------------------------------------
    // Resvae ADC control register
    rADC_REG[0] = ADC0->ANA.W;
    rADC_REG[1] = ADC0->MSK.W;
    rADC_REG[2] = ADC0->START.W;
    rADC_REG[3] = ADC0->CR0.W;  
    rADC_REG[4] = ADC0->CR1.W;  
    rADC_REG[5] = ADC0->CLK.W;
    rADC_REG[6] = ADC0->INT.W;
    rADC_REG[7] = ADC0->SUM0.W;
    rADC_REG[8] = ADC0->SUM1.W;
    rADC_REG[9] = ADC0->SUM2.W;
    
    // ------------------------------------------------------------------------
    // Resvae OPA control register
    rOPA_CR0    = OPAx->CR.W;
    
    // ------------------------------------------------------------------------
    // reset SUM_NUM & window detect
    ADC0->CR1.W &= ~(ADC_CR1_SUM_NUM_mask_w | ADC_CR1_WIND_EN_enable_w);

    // Enable ADC
    ADC_Cmd(ADC0, ENABLE);

    // Config ADC clock source and divider    
    ADC_ClockSource_Select(ADC0, ADC_CKADC);    
    ADC_SetInternalClockDivider(ADC0, ADC_IntDIV16);
    
    // ADC convert mode and trigger source
    ADC_ConversionMode_Select(ADC0, ADCMode);   
    ADC_TriggerSource_Select(ADC0, ADC_START);
    ADC_DataResolution_Select(ADC0, ADC_12BitData);
    
    // Disable ADC all interrupt
    ADC_ITEA_Cmd(ADC0, DISABLE);
    
    ADC_SetConversionTime(ADC0, ADC_FastCONV);
    ADC_Dischrge_Cmd(ADC0, DISABLE);
    ADC_PGA_Cmd(ADC0, DISABLE);

    // ------------------------------------------------------------------------
    // config OPA mode (Buffer mode and source from VBIF14)
    OPA_PositivePin_Select(OPA_OP0, OPA_P_VBUF);
    OPA_NegativePin_Select(OPA_OP0, OPA_N_OPA_P0);
    OPA_Cmd(OPA_OP0, ENABLE);

    // ------------------------------------------------------------------------
    // Base line from VBUF14 (Average)
    ADC_InternalChannel_Select(ADC0, ADC_INT_VBUF);
    
    ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
    while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
    ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
    Base_line = (uint16_t) ADC_GetDAT0Data(ADC0);

    ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
    while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
    ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
    Base_line += (uint16_t) ADC_GetDAT0Data(ADC0);

    ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
    while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
    ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
    Base_line += (uint16_t) ADC_GetDAT0Data(ADC0);
    
    ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
    while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
    ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
    Base_line += (uint16_t) ADC_GetDAT0Data(ADC0);
    
    // Average
    Base_line = Base_line >> 2;

    // ------------------------------------------------------------------------
    // Compare ADC (internal channel OPA_P0) v.s Base_line
    ADC_InternalChannel_Select(ADC0, ADC_INT_OPA_P0);

    for (i=0; i<64; i++)
    {
        // --------------------------------------------------------------------
//        OPA_SetOffset(OPAx, i);              // trimm OPA offset
        OPAx->CR.MBIT.OFFT = i;
        
        // --------------------------------------------------------------------
        // ADC convert internal channel from OPA
        ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
        while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
        ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
        CompareVal = (uint16_t) ADC_GetDAT0Data(ADC0);
    
        ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
        while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
        ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
        CompareVal += (uint16_t) ADC_GetDAT0Data(ADC0);
    
        ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
        while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
        ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
        CompareVal += (uint16_t) ADC_GetDAT0Data(ADC0);
    
        ADC_SoftwareConversion_Cmd(ADC0, ENABLE);
        while(ADC_GetSingleFlagStatus(ADC0, ADC_E1CNVF) == DRV_UnHappened);
        ADC_ClearFlag(ADC0, ADC_E1CNVF | ADC_ESMPF);
        CompareVal += (uint16_t) ADC_GetDAT0Data(ADC0);
        
        // Average
        CompareVal = CompareVal >> 2;
    
        // --------------------------------------------------------------------
        // Exit condition : 1. (CompareVal == Base_line) or 2.(CompareVal > Base_line)
        Temp16bit = CompareVal;
        if (Temp16bit == Base_line)
            break;
        if (Temp16bit > Base_line)
        {
            if (i != 0)  i = i -1;
            break;
        }
        
    }
    
    // ------------------------------------------------------------------------
    // Modify rOPA_CR0 (Find the best offset trimming value)
    rOPA_CR0        = (rOPA_CR0 & ~OPA_CR0_OP0_OFFT_mask_w) | (uint32_t) (i << OPA_CR0_OP0_OFFT_shift_w);
    
    // ------------------------------------------------------------------------
    // restore ADC control register
    ADC0->ANA.W     = rADC_REG[0];
    ADC0->MSK.W     = rADC_REG[1];
    ADC0->START.W   = rADC_REG[2];
    ADC0->CR0.W     = rADC_REG[3];
    ADC0->CR1.W     = rADC_REG[4];
    ADC0->CLK.W     = rADC_REG[5];
    ADC0->INT.W     = rADC_REG[6];
    ADC0->SUM0.W    = rADC_REG[7];
    ADC0->SUM1.W    = rADC_REG[8];
    ADC0->SUM2.W    = rADC_REG[9];
    
    // ------------------------------------------------------------------------
    // restore OPA control register
    OPA->CR0.W      = rOPA_CR0; 
    
}

///@}

#endif

