/**
 ******************************************************************************
 *
 * @file        MG32_OPA_MID.c
 *
 * @brief       This file provides firmware functions to manage the following 
 *              functionalities of the OPA peripheral:
 *
 * @par         Project
 *              MG32
 * @version     V1.01
 * @date        2025/06/06
 * @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_MID.h"



/* Fixed timeout values for OPA settling time  */
#define OPA_Settle_Time     2       /*!< Unit: ms */

/**
 * @name	Initial/Deinitial OPA with OPA_HandleTypeDef
 *   		
 */ 
///@{ 
/**
 *******************************************************************************
 * @brief       Initializes the OPA peripheral and regular group according to
 *              parameters specified in structure "OPA_HandleTypeDef".
 * @param[in]   mOPA : pointer to a OPA_HandleTypeDef
 * @return		MID_StatusTypeDef
 * @note 
 * @par         Example
 * @code
    OPA_HandleTypeDef mOPA;
    
    mOPA.Instance                   = OPA_OP0;
    mOPA.Init.InvertingInput        = OPx_INVERTINGINPUT_OPA_P0;    // Inverting PIN Input from OPA_P0 (UGB)
    mOPA.Init.NonInvertingInput     = OPx_NONINVERTINGINPUT_I0;     // Non-Inverting PIN Input
    mOPA.Init.PowerMode             = OPA_NORMAL;                   // OPA works in normal power mode
    
    MID_OPA_Init(&mOPA);
 
 * @endcode
 *******************************************************************************
 */
MID_StatusTypeDef MID_OPA_Init(OPA_HandleTypeDef* mOPA)
{
    uint32_t tmpREG;
    
    /* Check OPA handle */
    if(mOPA == NULL_0) 
    {
        return MID_FAILURE;
    }
    else
    {
        /* Init the low level hardware : SYSCFG to access comparators */
        MID_OPA_MspInit(mOPA);
        
        /* Change OPA peripheral state */
        mOPA->State = MID_OPA_STATE_BUSY;
    
        // ------------------------------------------------------------------------
        /* Config of OPA parameters:                                              */
        // ------------------------------------------------------------------------
        tmpREG = mOPA->Init.InvertingInput      | 
                 mOPA->Init.NonInvertingInput   | 
                 mOPA->Init.PowerMode; 
        
        mOPA->Instance->CR.W = tmpREG;
        

        /* Initialize the OPA state*/
        mOPA->State = MID_OPA_STATE_READY;
    }
    
    /* OPA offset calibration */
    MID_OPA_Calibration_Start(mOPA);
    
    /* Return function status */
    return MID_SUCCESS;
}

/**
 *******************************************************************************
 * @brief       Deinitialize the OPA peripheral registers to their default reset
 *         		values, with deinitialization of the OPA MSP.
 * @param[in]   mOPA : pointer to a OPA_HandleTypeDef
 * @return		MID_StatusTypeDef
 * @note 
 * @par         Example
 * @code
    OPA_HandleTypeDef mOPA;
 
    MID_OPA_DeInit(&mOPA);
 * @endcode
 *******************************************************************************
 */
MID_StatusTypeDef MID_OPA_DeInit(OPA_HandleTypeDef *mOPA)
{

    /* Process locked */
    __MID_LOCK(mOPA);

    /* Check the OPA handle allocation and lock status */
    if(mOPA == NULL_0) 
    {
        return MID_FAILURE;
    }
    else
    {
        /* 1. Disable OPA */
        __DRV_OPA_DISABLE(mOPA);

        /* 2. Reset OPA register */
        mOPA->Instance->CR.W = (OPA_CR0_default);

        /* DeInit the low level hardware: GPIO, CLOCK and NVIC */
        MID_OPA_MspDeInit(mOPA);

        /* Set OPA state */
        mOPA->State = MID_OPA_STATE_RESET;
    }

    /* Process unlocked */
    __MID_UNLOCK(mOPA);

    /* Return function status */
    return MID_SUCCESS;
}

/**
 *******************************************************************************
 * @brief       Initializes the OPA Base MSP.
 * @param[in]   mOPA : pointer to a OPA_HandleTypeDef
 * @return		None
 *******************************************************************************
 */
__weak void MID_OPA_MspInit(OPA_HandleTypeDef* mOPA)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(mOPA);
  /* NOTE : This function Should not be modified, when the callback is needed,
            the MID_OPA_MspInit could be implemented in the user file
   */
}

/**
 *******************************************************************************
 * @brief       DeInitializes OPA Base MSP.
 * @param[in]   mOPA : pointer to a OPA_HandleTypeDef
 * @return		None
 *******************************************************************************
 */
__weak void MID_OPA_MspDeInit(OPA_HandleTypeDef* mOPA)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(mOPA);
  /* NOTE : This function Should not be modified, when the callback is needed,
            the MID_OPA_MspDeInit could be implemented in the user file
   */
}
///@} 




/**
 * @name	OPA offset calibration functions
 *   		
 */ 
///@{ 

/**
 *******************************************************************************
 * @brief	    OPA calibration function.
 * @param[in]   mOPA : pointer to a OPA_HandleTypeDef.
 * @return		MID_StatusTypeDef
 *******************************************************************************
 */
MID_StatusTypeDef MID_OPA_Calibration_Start(OPA_HandleTypeDef *mOPA)
{
    __IO uint16_t Base_line, CompareVal;
    __IO uint8_t i;
    uint16_t Temp16bit;
    
    uint32_t rADC_REG[10];
    uint32_t rOPA_CR0, tempReg;
   
    // ------------------------------------------------------------------------
    /* Check the OPA handle allocation */
    if(mOPA == NULL_0)
    {
        return MID_FAILURE;
    }

    // ------------------------------------------------------------------------
    // Enable VBUF14
    MID_UnProtectModuleReg(PWmodule);
    __DRV_PW_ENANLE_VBUF();
    MID_ProtectModuleReg(PWmodule);
        
    // ------------------------------------------------------------------------
    // 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    = OPA->CR0.W;
    
    // ------------------------------------------------------------------------
    // reset SUM_NUM & window detect
    ADC0->CR1.W &= ~(ADC_CR1_SUM_NUM_mask_w | ADC_CR1_WIND_EN_enable_w);

    // Enable ADC
    ADC0->CR0.MBIT.EN = 1;

    // Config ADC clock source and divider    
    ADC0->CLK.W = (ADC0->CLK.W & ~(ADC_CLK_CK_SEL2_mask_w | ADC_CLK_CK_DIV_mask_w)) |
                  (ADC_CLOCKPROC_DIV16);
    
    // ADC convert mode (ADC one shot mode) and trigger source
    ADC0->START.W = ADC0->START.W &             \
                    ~(ADC_START_CONV_MDS_mask_w | ADC_START_TRG_CONT_mask_w | ADC_START_START_SEL_mask_w);
                    
    ADC0->CR0.W &= ~(ADC_CR0_RES_SEL_mask_w);   // 12 bit    
    
    // Disable ADC all interrupt
    ADC0->INT.W = 0;
    
    // CONVT = 24clks / Disable PGA / (VREF from VREF pin) / Disable PGAOUT_EN
    ADC0->ANA.W &= ~(ADC_ANA_CONV_TIME_24adck_w | ADC_ANA_IVREF_SEL_mask_w | ADC_ANA_PGA_EN_mask_w | ADC_ANA_PGAOUT_EN_mask_w);
    
    // DISCHR_EN 
    ADC0->ANA.W |= ADC_ANA_DISCHR_EN_enable_w;
    
    // ------------------------------------------------------------------------
    // config OPA mode (Buffer mode)
    //  (PMUX = Source from VBIF14)
    //  (NMUX = OPA_P0)
    tempReg = mOPA->Instance->CR.W;
    
    // ------------------------------------------------------------------------
    tempReg &= ~(OPA_CR0_OP0_PMUX_mask_w            |\
                 OPA_CR0_OP0_NMUX_mask_w);
                 
    tempReg |= (OPx_INVERTINGINPUT_OPA_P0           |\
                OPx_NONINVERTINGINPUT_VBUF          |\
                OPA_CR0_OP0_EN_enable_w);

    // ------------------------------------------------------------------------
    mOPA->Instance->CR.W = tempReg;

    // ------------------------------------------------------------------------
    // Base line from VBUF14 (Average)
    ADC0->START.W &= ~(ADC_START_CH_SEL_ext_w | ADC_START_CH_MUX_mask_w);
    ADC0->START.W |= (ADC_CHANNEL_19);
    
    ADC0->START.W |= ADC_START_START_mask_w;
    while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
    ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
    Base_line = ADC0->DAT0.H[0];

    ADC0->START.W |= ADC_START_START_mask_w;
    while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
    ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
    Temp16bit = ADC0->DAT0.H[0];
    Base_line += Temp16bit;

    ADC0->START.W |= ADC_START_START_mask_w;
    while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
    ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
    Temp16bit = ADC0->DAT0.H[0];
    Base_line += Temp16bit;
    
    ADC0->START.W |= ADC_START_START_mask_w;
    while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
    ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
    Temp16bit = ADC0->DAT0.H[0];
    Base_line += Temp16bit;
    
    // Average
    Base_line = Base_line >> 2;

    // ------------------------------------------------------------------------
    // select internal channel from OP0_P0
    // ------------------------------------------------------------------------
    ADC0->START.W &= ~(ADC_START_CH_SEL_ext_w | ADC_START_CH_MUX_mask_w);
    ADC0->START.W |= (ADC_CHANNEL_27);
    
    for (i=0; i<64; i++)
    {
        // --------------------------------------------------------------------
        mOPA->Instance->CR.MBIT.OFFT = i;               // trimm OPA offset
        
        // --------------------------------------------------------------------
        // ADC convert internal channel from OPA
        ADC0->START.W |= ADC_START_START_mask_w;    
        while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
        ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
        CompareVal = ADC0->DAT0.H[0];
    
        ADC0->START.W |= ADC_START_START_mask_w;
        while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
        ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
        Temp16bit = ADC0->DAT0.H[0];
        CompareVal += Temp16bit;

        ADC0->START.W |= ADC_START_START_mask_w;
        while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
        ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
        Temp16bit = ADC0->DAT0.H[0];
        CompareVal += Temp16bit;
        
        ADC0->START.W |= ADC_START_START_mask_w;
        while ((ADC0->STA.W & ADC_FLAG_E1CNV) == 0x00);
        ADC0->STA.W = (ADC_FLAG_E1CNV | ADC_FLAG_ESMP);
        Temp16bit = ADC0->DAT0.H[0];
        CompareVal += Temp16bit;
        
        // 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; 
    
    /* Return function status */
    return MID_SUCCESS;
}

///@}


#endif

