/**
  ******************************************************************************
  * @file    lcm32f06x_opa.c 
  * @author  System R&D Team
  * @version V2.0.2
  * @date    10-April-2025
  * @brief   This file provides all the opa firmware functions.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) Hangzhou Lingxin Microelectronics Co.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "lcm32f06x_opa.h"

/**
  * @name   OPA_DeInit
  * @brief  EN: Deinitializes OPA peripheral registers to their default reset values.
  *         CN: 将 OPA 外设寄存器初始化为其默认重置值。
  * @param  None
  * @retval None
  * @note   EN: DAC ACMP and OPA share the same clock enable which can be used once.
  *         CN: DAC ACMP OPA 共用一个时钟使能，使用一次即可。
  */
void OPA_DeInit(void)
{
  RCC_APB0PeriphResetCmd(RCC_APB0Periph_ANACTRL, ENABLE);
  RCC_APB0PeriphResetCmd(RCC_APB0Periph_ANACTRL, DISABLE);
}

/**
  * @name   OPA_Init
  * @brief  EN: Initializes the OPAx peripheral according to the specified 
  *             parameters in the OPA_InitStruct.
  *         CN: 根据 OPA_InitStruct 中指定的参数初始化 OPAx。
  * @param  OPAx: 
  *         EN: where x can be 0 or 1 or 2 to select the OPA peripheral.
  *         CN: x 可以是0、1或2,以选择 OPA 外设。
  * @param  OPA_InitStruct: 
  *         EN: pointer to a OPA_InitTypeDef structure that contains the 
  *             configuration information for the specified OPA peripheral.
  *         CN: 指向 OPA_InitTypeDef 结构的指针，该结构包含指定 OPA 外设的配置信息。
  * @retval None
  */
void OPA_Init(OPA_TypeDef *OPAx, OPA_InitTypeDef *OPA_InitStruct)
{
  uint32_t tmpreg = 0;

  /* Check the OPA parameters */
  assert_param(IS_OPA_PositiveInput(OPA_InitStruct->OPA_PositiveInput));
  assert_param(IS_OPA_NegativeInput(OPA_InitStruct->OPA_NegativeInput));
  assert_param(IS_OPA_FeedbackResSingal(OPA_InitStruct->OPA_FeedbackResSingal));
  assert_param(IS_OPA_InputRes(OPA_InitStruct->OPA_InputRes));
  assert_param(IS_OPA_PositiveADCSwitch(OPA_InitStruct->OPA_PositiveADCSwitch));
  assert_param(IS_OPA_NegativeADCSwitch(OPA_InitStruct->OPA_NegativeADCSwitch));
  assert_param(IS_OPA_Gain(OPA_InitStruct->OPA_Gain));
  assert_param(IS_OPA_BiasVoltageSource(OPA_InitStruct->OPA_BiasVoltageSource));
  assert_param(IS_OPA_BiasVoltage(OPA_InitStruct->OPA_BiasVoltage));
  assert_param(IS_OPA_FeedbackRes(OPA_InitStruct->OPA_FeedbackRes));
  assert_param(IS_OPA_OppRES_Short(OPA_InitStruct->OPA_OppRES_Short));
  assert_param(IS_OPA_OpnRES_Short(OPA_InitStruct->OPA_OpnRES_Short));

  OPA_TRIM(OPAx);

  tmpreg = OPAx->OPA_CSR;

  tmpreg |= OPA_InitStruct->OPA_PositiveInput | OPA_InitStruct->OPA_NegativeInput |
            OPA_InitStruct->OPA_FeedbackResSingal | OPA_InitStruct->OPA_InputRes |
            OPA_InitStruct->OPA_PositiveADCSwitch | OPA_InitStruct->OPA_NegativeADCSwitch |
            OPA_InitStruct->OPA_Gain | OPA_InitStruct->OPA_BiasVoltageSource |
            OPA_InitStruct->OPA_BiasVoltage | OPA_InitStruct->OPA_FeedbackRes | 
            OPA_InitStruct->OPA_OppRES_Short | OPA_InitStruct->OPA_OpnRES_Short;

  OPAx->OPA_CSR = tmpreg;
}

/**
  * @name   OPA_StructInit
  * @brief  EN: Fills each OPA_InitStruct member with its default value.
  *         CN: 用默认值填充每个 OPA_InitStruct 成员。
  * @param  OPA_InitStruct: 
  *         EN: pointer to a OPA_InitTypeDef structure that contains the 
  *             configuration information for the specified OPA peripheral.
  *         CN: 指向 OPA_InitTypeDef 结构的指针，该结构包含指定 OPA 外设的配置信息。
  * @retval None
  */
void OPA_StructInit(OPA_InitTypeDef *OPA_InitStruct)
{
  OPA_InitStruct->OPA_PositiveInput = OPA_PositiveInput_P0;

  OPA_InitStruct->OPA_NegativeInput = OPA_NegativeInput_N0;

  OPA_InitStruct->OPA_FeedbackResSingal = OPA_FeedbackResSingal_DISEN;

  OPA_InitStruct->OPA_InputRes = OPA_InputRes_36K;

  OPA_InitStruct->OPA_PositiveADCSwitch = OPA_PositiveADCSwitch_DISEN;

  OPA_InitStruct->OPA_NegativeADCSwitch = OPA_NegativeADCSwitch_DISEN;

  OPA_InitStruct->OPA_Gain = OPA_Gain_1;

  OPA_InitStruct->OPA_BiasVoltageSource = OPA_BiasVoltageSource_PMU_AOUT;

  OPA_InitStruct->OPA_BiasVoltage = OPA_BiasVoltage_DISEN;

  OPA_InitStruct->OPA_FeedbackRes = OPA_FeedbackRes_DISEN;

  OPA_InitStruct->OPA_OppRES_Short = OPA_OppRES_Short_DISEN;

  OPA_InitStruct->OPA_OpnRES_Short = OPA_OpnRES_Short_DISEN; 
}

/**
  * @name   OPA_Cmd
  * @brief  EN: Enables or disables the specified OPA peripheral.
  *         CN: 启用或禁用指定的 OPA 外设。
  * @param  OPAx: 
  *         EN: where x can be 0 or 1 or 2 to select the OPA peripheral.
  *         CN: x 可以是0、1或2,以选择 OPA 外设。
  * @param  NewState: 
  *         EN: New state of the OPAx peripheral.
  *         CN: OPAx 外设的新状态。
  * @retval None
  */
void OPA_Cmd(OPA_TypeDef *OPAx, FunctionalState NewState)
{
  if (NewState != DISABLE)
  {
    /* Enable the selected OPA peripheral  */
    OPAx->OPA_CSR |= (uint32_t)OPAX_CSR_OPxEN_Msk;
  }
  else
  {
    /* Disable the selected OPA peripheral  */
    OPAx->OPA_CSR &= (uint32_t)(~OPAX_CSR_OPxEN_Msk);
  }
}

/**
  * @name   Opa_trim
  * @brief  EN: OPA tuning value writing function.
  *         CN: OPA 修调值写入函数。
  * @param  OPAx: 
  *         EN: where x can be 0 or 1 or 2 to select the OPA peripheral.
  *         CN: x 可以是0、1或2,以选择 OPA 外设。
  * @param  trim: 
  *         EN: OPAx adjustment value.
  *         CN: OPAx 修调值。
  * @retval None
  */
void Opa_trim(OPA_TypeDef *OPAx, uint8_t trim)
{
  OPAx->OPA_CSR = (OPAx->OPA_CSR & ~OPAX_CSR_OPxOS_Msk) | (trim << OPAX_CSR_OPxOS_Pos);
}

/**
  * @name   get_opa_trim
  * @brief  EN: Reading OPA tuning values and writing functions.
  *         CN: 读取OPA 修调值函数。
  * @param  OPAx: 
  *         EN: where x can be 0 or 1 or 2 to select the OPA peripheral.
  *         CN: x 可以是0、1或2,以选择 OPA 外设。
  * @retval uint8_t：
  *         EN: OPAx adjustment value.
  *         CN: OPAx 修调值。
  */
uint8_t get_opa_trim(OPA_TypeDef *OPAx)
{
  return ((OPAx->OPA_CSR & OPAX_CSR_OPxOS_Msk) >> OPAX_CSR_OPxOS_Pos);
}

/**
  * @name   get_opa_trim_FLAG
  * @brief  EN: Reading OPA tuning state and writing functions.
  *         CN: 读取OPA 修调状态函数。
  * @param  OPAx: 
  *         EN: where x can be 0 or 1 or 2 to select the OPA peripheral.
  *         CN: x 可以是0、1或2,以选择 OPA 外设。
  * @retval uint8_t：
  *         EN: OPAx adjustment state.
  *         CN: OPAx 修调状态。
  */
uint32_t get_opa_trim_FLAG(OPA_TypeDef *OPAx)
{
  return ((OPAx->OPA_CSR & OPAX_CSR_OPxTRIMO_Msk) >> OPAX_CSR_OPxTRIMO_Pos);
}

/**
  * @name   OPA_TRIM
  * @brief  EN: Operational amplifier calibration function.
  *         CN: 运放校准函数。
  * @param  OPAx: 
  *         EN: where x can be 0 or 1 or 2 to select the OPA peripheral.
  *         CN: x 可以是0、1或2,以选择 OPA 外设。
  * @retval None
  */
void OPA_TRIM(OPA_TypeDef *OPAx)
{
  uint8_t CTRIMOLD = 0;
  uint8_t CTRIM = 0x1f;
  uint8_t direction = 0;
  unsigned char flag = 0;
  uint32_t mode = 0;

  OPAx->OPA_CSR = OPAX_CSR_OPxTRIMEN_Msk | OPAX_CSR_OPxN_PD_Msk | \
                  OPAX_CSR_OPxP_PD_Msk ;
  
  
  OPAx->OPA_CSR |= OPAX_CSR_OPxEN_Msk;

  while (flag == 0)
  {
    Opa_trim(OPAx, CTRIM);
    delay10us(20); // 200uS
    mode = get_opa_trim_FLAG(OPAx);
    switch (direction)
    {
      case 0:
      {
        if (mode == 0)
        {
          CTRIM--;
        }
        if ((mode == 1) || (CTRIM == 0))
        {
          CTRIMOLD = CTRIM;
          CTRIM = 0;
          direction = 1;
        }
        break;
      }
      case 1:
      {
        if (mode == 1)
        {
          CTRIM++;
        }
        if ((CTRIM == 0x1f) || (mode == 0))
        {
          flag = 1;
          if (CTRIMOLD == 0)
          {
            Opa_trim(OPAx, CTRIM);
          }
          else
          {
            CTRIM = (CTRIM + CTRIMOLD) >> 1;
            Opa_trim(OPAx, CTRIM);
          }
        }
        break;
      }
      default:
        Opa_trim(OPAx, 0);
        break;
    }
  }
  OPAx->OPA_CSR &= ~(OPAX_CSR_OPxTRIMEN_Msk | OPAX_CSR_OPxN_PD_Msk | \
                  OPAX_CSR_OPxP_PD_Msk | OPAX_CSR_OPxPS_Msk | \
                  OPAX_CSR_OPxEN_Msk);
}
