/**
  ******************************************************************************
  * @file    stm32f3xx_hal_opamp.c
  * @author  MCD Application Team
  * @version V1.0.1
  * @date    18-June-2014
  * @brief   OPAMP HAL module driver.
  *    
  *          This file provides firmware functions to manage the following 
  *          functionalities of the operational amplifiers (OPAMP1,...OPAMP4) 
  *          peripheral: 
  *           + OPAMP Configuration
  *           + OPAMP calibration
  *
  *          Thanks to
  *           + Initialization/de-initialization functions
  *           + I/O operation functions
  *           + Peripheral Control functions
  *           + Peripheral State functions
  *         
  @verbatim
================================================================================
          ##### OPAMP Peripheral Features #####
================================================================================
           
  [..] The device integrates up to 4 operational amplifiers OPAMP1, OPAMP2,
       OPAMP3 and OPAMP4:
       
       (#) The OPAMP(s) provides several exclusive running modes.
       (+) Standalone mode
       (+) Programmable Gain Amplifier (PGA) mode (Resistor feedback output)
       (+) Follower mode

       (#) The OPAMP(s) provide(s) calibration capabilities.  
       (+) Calibration aims at correcting some offset for running mode.
       (+) The OPAMP uses either factory calibration settings OR user defined 
           calibration (trimming) settings (i.e. trimming mode).
       (+) The user defined settings can be figured out using self calibration 
           handled by HAL_OPAMP_SelfCalibrate, HAL_OPAMPEx_SelfCalibrate2opamp
           or HAL_OPAMPEx_SelfCalibrate4opamp
       (+) HAL_OPAMP_SelfCalibrate:
       (++) Runs automatically the calibration in 2 steps. 
            (90% of VDDA for NMOS transistors, 10% of VDDA for PMOS transistors).
            (As OPAMP is Rail-to-rail input/output, these 2 steps calibration is 
            appropriate and enough in most cases).
       (++) Enables the user trimming mode
       (++) Updates the init structure with trimming values with fresh calibration 
            results. 
            The user may store the calibration results for larger 
            (ex monitoring the trimming as a function of temperature 
            for instance)
       (++) for STM32F3 devices having 2 or 4 OPAMPs
            HAL_OPAMPEx_SelfCalibrate2opamp, HAL_OPAMPEx_SelfCalibrate4opamp
            runs calibration of 2 or 4 OPAMPs in parallel. 
       
       (#) For any running mode, an additional Timer-controlled Mux (multiplexer) 
           mode can be set on top.
       (+) Timer-controlled Mux mode allows Automatic switching between inverting
           and non-inverting input. 
       (+) Hence on top of defaults (primary) inverting and non-inverting inputs,
           the user shall select secondary inverting and non inverting inputs.
       (+) TIM1 CC6 provides the alternate switching tempo between defaults 
           (primary) and secondary inputs. 
             
       (#) Running mode: Standalone mode 
       (+) Gain is set externally (gain depends on external loads).
       (+) Follower mode also possible externally by connecting the inverting input to
           the output.
       
       (#) Running mode: Follower mode
       (+) No Inverting Input is connected.
       
       (#) Running mode: Programmable Gain Amplifier (PGA) mode 
           (Resistor feedback output)
       (+) The OPAMP(s) output(s) can be internally connected to resistor feedback
           output.
       (+) OPAMP gain is either 2, 4, 8 or 16.
        
       (#) The OPAMPs non inverting input (both default and secondary) can be 
           selected among the list shown by table below.
       
       (#) The OPAMPs non inverting input (both default and secondary) can be 
           selected among the list shown by table below.
       
   [..] Table 1.  OPAMPs inverting/non-inverting inputs for the STM32F3 devices:
     
    +--------------------------------------------------------------+     
    |                 |        | OPAMP1 | OPAMP2 | OPAMP3 | OPAMP4 |
    |-----------------|--------|--------|--------|--------|--------|
    |                 | No conn|   X    |   X    |   X    |   X    |
    | Inverting Input | VM0    |  PC5   |  PC5   |  PB10  |  PB10  |
    | (1)             | VM1    |  PA3   |  PA5   |  PB2   |  PD8   |
    |-----------------|--------|--------|--------|--------|--------|
    |                 | VP0    |  PA1   |  PA7   |  PB0   |  PB13  |
    |  Non Inverting  | VP1    |  PA7   |  PD14  |  PB13  |  PD11  |
    |    Input        | VP2    |  PA3   |  PB0   |  PA1   |  PA4   |
    |                 | VP3    |  PA5   |  PB14  |  PA5   |  PB11  |
    +--------------------------------------------------------------+  
    (1): NA in follower mode.
           
   [..] Table 2.  OPAMPs outputs for the STM32F3 devices:

    +--------------------------------------------------------------+     
    |                 |        | OPAMP1 | OPAMP2 | OPAMP3 | OPAMP4 |
    |-----------------|--------|--------|--------|--------|--------|
    | Output          |        |  PA2   |  PA6   |  PB1   |  PB12  |
    |-----------------|--------|--------|--------|--------|--------|

      
            ##### How to use this driver #####
================================================================================
  [..] 
     
    *** Calibration ***
    ============================================
      To run the opamp calibration self calibration:

      (#) Start calibration using HAL_OPAMP_SelfCalibrate. 
           Store the calibration results.

    *** Running mode ***
    ============================================
      
      To use the opamp, perform the following steps:
            
      (#) Fill in the HAL_COMP_MspInit() to
      (+) Configure the opamp input AND output in analog mode using 
          HAL_GPIO_Init() to map the opamp output to the GPIO pin.
  
      (#) Configure the opamp using HAL_OPAMP_Init() function:
      (+) Select the mode
      (+) Select the inverting input
      (+) Select the non-inverting input 
      (+) Select if the Timer controlled Mux mode is enabled/disabled
      (+) If the Timer controlled Mux mode is enabled, select the secondary inverting input
      (+) If the Timer controlled Mux mode is enabled, Select the secondary non-inverting input 
      (+) If PGA mode is enabled, Select if inverting input is connected.
      (+) Select either factory or user defined trimming mode.
      (+) If the user defined trimming mode is enabled, select PMOS & NMOS trimming values
          (typ. settings returned by HAL_OPAMP_SelfCalibrate function).
      
      (#) Enable the opamp using HAL_OPAMP_Start() function.
           
      (#) Disable the opamp using HAL_OPAMP_Stop() function.
      
      (#) Lock the opamp in running mode using HAL_OPAMP_Lock() function. From then The configuration 
          can only be modified after HW reset.

    *** Running mode: change of configuration while OPAMP ON  ***
    ============================================
    To Re-configure OPAMP when OPAMP is ON (change on the fly)
      (#) If needed, Fill in the HAL_OPAMP_MspInit()
      (+) This is the case for instance if youu wish to use new OPAMP I/O

      (#) Configure the opamp using HAL_OPAMP_Init() function:
      (+) As in configure case, selects first the parameters you wish to modify.
      
  @endverbatim
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************  
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f3xx_hal.h"
    
/** @addtogroup STM32F3xx_HAL_Driver
  * @{
  */

/** @defgroup OPAMP 
  * @brief OPAMP HAL module driver
  * @{
  */

#ifdef HAL_OPAMP_MODULE_ENABLED

#if defined (STM32F303xC) || defined (STM32F358xx) || defined (STM32F302xC) || \
    defined (STM32F303x8) || defined (STM32F328xx) || defined (STM32F302x8) || \
    defined (STM32F301x8) || defined (STM32F318xx) || defined (STM32F334x8)

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* CSR register reset value */ 
#define OPAMP_CSR_RESET_VALUE             ((uint32_t)0x00000000)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup OPAMP_Private_Functions
  * @{
  */

/** @defgroup HAL_OPAMP_Group1 Initialization/de-initialization functions 
 *  @brief    Initialization and Configuration functions 
 *
@verbatim    
 ===============================================================================
              ##### Initialization/de-initialization  functions #####
 ===============================================================================
    [..]  This section provides functions allowing to:
 
@endverbatim
  * @{
  */

/**
  * @brief  Initializes the OPAMP according to the specified
  *         parameters in the OPAMP_InitTypeDef and create the associated handle.
  * @note   If the selected opamp is locked, initialization can't be performed.
  *         To unlock the configuration, perform a system reset.
  * @param  hopamp: OPAMP handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPAMP_Init(OPAMP_HandleTypeDef *hopamp)

{ 
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPAMP handle allocation and lock status */
  /* Init not allowed if calibration is ongoing */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED) \
                      || (hopamp->State == HAL_OPAMP_STATE_CALIBBUSY))
  {
    return HAL_ERROR;
  }
  else
  {
      
    /* Check the parameter */
    assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
       
    /* Set OPAMP parameters */
    assert_param(IS_OPAMP_FUNCTIONAL_NORMALMODE(hopamp->Init.Mode));
    assert_param(IS_OPAMP_NONINVERTING_INPUT(hopamp->Init.NonInvertingInput));
    if (((hopamp->Init.Mode) == OPAMP_STANDALONE_MODE) || ((hopamp->Init.Mode) == OPAMP_PGA_MODE))
    {
      assert_param(IS_OPAMP_INVERTING_INPUT(hopamp->Init.InvertingInput));
    }
  
    assert_param(IS_OPAMP_TIMERCONTROLLED_MUXMODE(hopamp->Init.TimerControlledMuxmode));

    if ((hopamp->Init.TimerControlledMuxmode) == OPAMP_TIMERCONTROLLEDMUXMODE_ENABLE)
    {
      assert_param(IS_OPAMP_SEC_NONINVERTINGINPUT(hopamp->Init.NonInvertingInputSecondary));
      if (((hopamp->Init.Mode) == OPAMP_STANDALONE_MODE) || ((hopamp->Init.Mode) == OPAMP_PGA_MODE))
      {
        assert_param(IS_OPAMP_SEC_INVERTINGINPUT(hopamp->Init.InvertingInputSecondary));
      }
    }
    
    if ((hopamp->Init.Mode) == OPAMP_PGA_MODE)
    {
      assert_param(IS_OPAMP_PGACONNECT(hopamp->Init.PgaConnect));
      assert_param(IS_OPAMP_PGA_GAIN(hopamp->Init.PgaGain));
    }
    
    assert_param(IS_OPAMP_TRIMMING(hopamp->Init.UserTrimming)); 
    if ((hopamp->Init.UserTrimming) == OPAMP_TRIMMING_USER)
    {
      assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueP));
      assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueN));
    }
 
    /* Init SYSCFG and the low level hardware to access opamp */
    __SYSCFG_CLK_ENABLE();
    
    /* Call MSP init function */
    HAL_OPAMP_MspInit(hopamp);
                                          
    /* Set OPAMP parameters */
    /*     Set  bits according to hopamp->hopamp->Init.Mode value                                 */
    /*     Set  bits according to hopamp->hopamp->Init.InvertingInput value                       */
    /*     Set  bits according to hopamp->hopamp->Init.NonInvertingInput value                    */
    /*     Set  bits according to hopamp->hopamp->Init.TimerControlledMuxmode value               */
    /*     Set  bits according to hopamp->hopamp->Init.InvertingInputSecondary  value             */
    /*     Set  bits according to hopamp->hopamp->Init.NonInvertingInputSecondary value           */
    /*     Set  bits according to hopamp->hopamp->Init.PgaConnect value                           */
    /*     Set  bits according to hopamp->hopamp->Init.PgaGain value                              */
    /*     Set  bits according to hopamp->hopamp->Init.UserTrimming value                         */
    /*     Set  bits according to hopamp->hopamp->Init.TrimmingValueP value                       */
    /*     Set  bits according to hopamp->hopamp->Init.TrimmingValueN value                       */
    
    
    /* check if OPAMP_PGA_MODE & in Follower mode */
    /*   - InvertingInput                         */
    /*   - InvertingInputSecondary                */
    /* are Not Applicable                         */
    
    if ((hopamp->Init.Mode == OPAMP_PGA_MODE) || (hopamp->Init.Mode == OPAMP_FOLLOWER_MODE))
    {
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_UPDATE_PARAMETERS_INIT_MASK, \
                                        hopamp->Init.Mode | \
                                        hopamp->Init.NonInvertingInput | \
                                        hopamp->Init.TimerControlledMuxmode | \
                                        hopamp->Init.NonInvertingInputSecondary  | \
                                        hopamp->Init.PgaConnect | \
                                        hopamp->Init.PgaGain | \
                                        hopamp->Init.UserTrimming | \
                                        (hopamp->Init.TrimmingValueP << OPAMP_INPUT_NONINVERTING) | \
                                        (hopamp->Init.TrimmingValueN << OPAMP_INPUT_INVERTING));  
    }    
    else
    {
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_UPDATE_PARAMETERS_INIT_MASK, \
                                        hopamp->Init.Mode | \
                                        hopamp->Init.InvertingInput    | \
                                        hopamp->Init.NonInvertingInput | \
                                        hopamp->Init.TimerControlledMuxmode | \
                                        hopamp->Init.InvertingInputSecondary  | \
                                        hopamp->Init.NonInvertingInputSecondary  | \
                                        hopamp->Init.PgaConnect | \
                                        hopamp->Init.PgaGain | \
                                        hopamp->Init.UserTrimming | \
                                        (hopamp->Init.TrimmingValueP << OPAMP_INPUT_NONINVERTING) | \
                                        (hopamp->Init.TrimmingValueN << OPAMP_INPUT_INVERTING));     
    } 
    /* Update the OPAMP state*/
    if (hopamp->State == HAL_OPAMP_STATE_RESET)
    {
      /* From RESET state to READY State */
      hopamp->State = HAL_OPAMP_STATE_READY;
    }
    /* else: remain in READY or BUSY state (no update) */

    return status;
    }
}

/**
  * @brief  DeInitializes the OPAMP peripheral 
  * @note   Deinitialization can't be performed if the OPAMP configuration is locked.
  *         To unlock the configuration, perform a system reset.
  * @param  hopamp: OPAMP handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPAMP_DeInit(OPAMP_HandleTypeDef *hopamp)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPAMP handle allocation */
  /* Check if OPAMP locked */
  /* DeInit not allowed if calibration is ongoing */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED) \
                      || (hopamp->State == HAL_OPAMP_STATE_CALIBBUSY))
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));

    /* Set OPAMP_CSR register to reset value */
    WRITE_REG(hopamp->Instance->CSR, OPAMP_CSR_RESET_VALUE);

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

    /* Update the OPAMP state*/
    hopamp->State = HAL_OPAMP_STATE_RESET;
  }
  return status;
}

/**
  * @brief  Initializes the OPAMP MSP.
  * @param  hopamp: OPAMP handle
  * @retval None
  */
__weak void HAL_OPAMP_MspInit(OPAMP_HandleTypeDef *hopamp)
{
  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_OPAMP_MspInit could be implemented in the user file
   */

   /* Example */ 
}

/**
  * @brief  DeInitializes OPAMP MSP.
  * @param  hopamp: OPAMP handle
  * @retval None
  */
__weak void HAL_OPAMP_MspDeInit(OPAMP_HandleTypeDef *hopamp)
{
  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_OPAMP_MspDeInit could be implemented in the user file
   */

}

/**
  * @}
  */


/** @defgroup HAL_OPAMP_Group2 I/O operation functions 
 *  @brief   Data transfers functions 
 *
@verbatim   
 ===============================================================================
                      ##### IO operation  functions #####
 ===============================================================================  
    [..]
    This subsection provides a set of functions allowing to manage the OPAMP data 
    transfers.

@endverbatim
  * @{
  */

/**
  * @brief  Start the opamp
  * @param  hopamp: OPAMP handle
  * @retval HAL status
  */

HAL_StatusTypeDef HAL_OPAMP_Start(OPAMP_HandleTypeDef *hopamp)
{ 
  HAL_StatusTypeDef status = HAL_OK;
  
  /* Check the OPAMP handle allocation */
  /* Check if OPAMP locked */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED))
                      
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
    
    if(hopamp->State == HAL_OPAMP_STATE_READY)
    {
      /* Enable the selected opamp */
      SET_BIT (hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);

      /* Update the OPAMP state*/     
      /* From HAL_OPAMP_STATE_READY to HAL_OPAMP_STATE_BUSY */
      hopamp->State = HAL_OPAMP_STATE_BUSY;   
    }
    else
    {
      status = HAL_ERROR;
    }
    
    
   }
  return status;
}

/**
  * @brief  Stop the opamp 
  * @param  hopamp: OPAMP handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPAMP_Stop(OPAMP_HandleTypeDef *hopamp)
{ 
  HAL_StatusTypeDef status = HAL_OK;
    
  /* Check the OPAMP handle allocation */
  /* Check if OPAMP locked */
  /* Check if OPAMP calibration ongoing */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED) \
                      || (hopamp->State == HAL_OPAMP_STATE_CALIBBUSY))  
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));

    if(hopamp->State == HAL_OPAMP_STATE_BUSY)
    {
      /* Disable the selected opamp */
      CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN); 
    
      /* Update the OPAMP state*/     
      /* From  HAL_OPAMP_STATE_BUSY to HAL_OPAMP_STATE_READY*/
      hopamp->State = HAL_OPAMP_STATE_READY;
    }
    else
    {
      status = HAL_ERROR;
    }
  }
  return status;
}

/**
  * @brief  Run the self calibration of one OPAMP
  * @param  hopamp handle
  * @retval Updated offset trimming values (PMOS & NMOS), user trimming is enabled
  * @retval HAL status
  * @note   Calibration runs about 25 ms.
  */

HAL_StatusTypeDef HAL_OPAMP_SelfCalibrate(OPAMP_HandleTypeDef *hopamp)
{ 

  HAL_StatusTypeDef status = HAL_OK;
  
  uint32_t trimmingvaluen = 0;
  uint32_t trimmingvaluep = 0;
  uint32_t delta;
  
  /* Check the OPAMP handle allocation */
  /* Check if OPAMP locked */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED))
  {
    status = HAL_ERROR;
  }
  else
  {
  
    /* Check if OPAMP in calibration mode and calibration not yet enable */
    if(hopamp->State ==  HAL_OPAMP_STATE_READY)
    {
      /* Check the parameter */
      assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));

      /* Set Calibration mode */
      /* Non-inverting input connected to calibration reference voltage. */
      SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);

      /*  user trimming values are used for offset calibration */
      SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_USERTRIM);
      
      /* Enable calibration */
      SET_BIT (hopamp->Instance->CSR, OPAMP_CSR_CALON);
  
      /* 1st calibration - N */
      /* Select 90% VREF */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
      
      /* Enable the selected opamp */
      SET_BIT (hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);
      
      /* Init trimming counter */    
      /* Medium value */
      trimmingvaluen = 16; 
      delta = 8;
      
      while (delta != 0)
      {
        /* Set candidate trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen<<OPAMP_INPUT_INVERTING);
              
        /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */ 
        /* Offset trim time: during calibration, minimum time needed between */
        /* two steps to have 1 mV accuracy */
        HAL_Delay(2);

        if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
        { 
          /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
          trimmingvaluen += delta;
        }
        else
        {
          /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
          trimmingvaluen -= delta;
        }
                      
        delta >>= 1;
      }

      /* Still need to check if righ calibration is current value or un step below */
      /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0  */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen<<OPAMP_INPUT_INVERTING);
      
       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */ 
       /* Offset trim time: during calibration, minimum time needed between */
       /* two steps to have 1 mV accuracy */
       HAL_Delay(2);
      
      if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET) 
      { 
        /* OPAMP_CSR_OUTCAL is actually one value more */
        trimmingvaluen++;
        /* Set right trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen<<OPAMP_INPUT_INVERTING);
      }
       
      /* 2nd calibration - P */
      /* Select 10% VREF */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
      
      /* Init trimming counter */    
      /* Medium value */
      trimmingvaluep = 16; 
      delta = 8;
      
      while (delta != 0)
      {
        /* Set candidate trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep<<OPAMP_INPUT_NONINVERTING);
               
        /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */ 
        /* Offset trim time: during calibration, minimum time needed between */
        /* two steps to have 1 mV accuracy */
        HAL_Delay(2);

        if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET) 
        { 
          /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
          trimmingvaluep += delta;
        }
        else
        {
          trimmingvaluep -= delta;
        }
                      
        delta >>= 1;
      }
      
      /* Still need to check if righ calibration is current value or un step below */
      /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0 */
      /* Set candidate trimming */
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep<<OPAMP_INPUT_NONINVERTING);

       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */ 
       /* Offset trim time: during calibration, minimum time needed between */
       /* two steps to have 1 mV accuracy */
       HAL_Delay(2);
      
      if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
      { 
        /* OPAMP_CSR_OUTCAL is actually one value more */
        trimmingvaluep++;
        /* Set right trimming */
        MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep<<OPAMP_INPUT_NONINVERTING);
      }
           
      /* Disable calibration */
      CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_CALON);

      /* Disable the OPAMP */
      CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);
                 
      /* Set normale operating mode  */
      /* Non-inverting input connected to calibration reference voltage. */
      CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);
            
      /* Set normale operating mode  */
      /* Non-inverting input connected to calibration reference voltage. */
      CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);
            
      /* Self calibration is successful  */
      /* Store calibration(user timming) results in init structure. */

      /* Write calibration result N */
      hopamp->Init.TrimmingValueN = trimmingvaluen;
     
      /* Write calibration result P */
      hopamp->Init.TrimmingValueP = trimmingvaluep;

      /* Select user timming mode */      
      /* And updated with calibrated settings */
      hopamp->Init.UserTrimming = OPAMP_TRIMMING_USER;
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep<<OPAMP_INPUT_NONINVERTING);
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen<<OPAMP_INPUT_INVERTING);
    }

    else
    {
      /* OPAMP can not be calibrated from this mode */ 
      status = HAL_ERROR;
    }   
  }
  return status;
}

/**
  * @}
  */

/** @defgroup HAL_OPAMP_Group3 Peripheral Control functions 
 *  @brief   management functions 
 *
@verbatim   
 ===============================================================================
                      ##### Peripheral Control  functions #####
 ===============================================================================  
    [..]
    This subsection provides a set of functions allowing to control the OPAMP data 
    transfers.



@endverbatim
  * @{
  */

/**
  * @brief  Lock the selected opamp configuration. 
  * @param  hopamp: OPAMP handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPAMP_Lock(OPAMP_HandleTypeDef *hopamp)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPAMP handle allocation */
  /* Check if OPAMP locked */
  /* OPAMP can be locked when enabled and running in normal mode */ 
  /*   It is meaningless otherwise */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_RESET) \
                      || (hopamp->State == HAL_OPAMP_STATE_READY) \
                      || (hopamp->State == HAL_OPAMP_STATE_CALIBBUSY)\
                      || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED))
  
  {
    status = HAL_ERROR;
  }
  
  else
  {
    /* Check the parameter */
    assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
    
   /* Lock OPAMP */
    SET_BIT (hopamp->Instance->CSR, OPAMP_CSR_LOCK);
  
   /* OPAMP state changed to locked */
    hopamp->State = HAL_OPAMP_STATE_BUSYLOCKED;
  }
  return status; 
}

/**
  * @}
  */

/** @defgroup HAL_OPAMP_Group4 Peripheral State functions 
 *  @brief   Peripheral State functions 
 *
@verbatim   
 ===============================================================================
                      ##### Peripheral State  functions #####
 ===============================================================================  
    [..]
    This subsection permit to get in run-time the status of the peripheral 
    and the data flow.

@endverbatim
  * @{
  */

/**
  * @brief  Return the OPAMP state
  * @param  hopamp : OPAMP handle
  * @retval HAL state
  */
HAL_OPAMP_StateTypeDef HAL_OPAMP_GetState(OPAMP_HandleTypeDef *hopamp)
{
  /* Check the OPAMP handle allocation */
  if(hopamp == NULL)
  {
    return HAL_OPAMP_STATE_RESET;
  }

  /* Check the parameter */
  assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));

  return hopamp->State;
}

/**
  * @}
  */

/**
  * @brief  Return the OPAMP factory trimming value
  * @param  hopamp : OPAMP handle
  * @param  trimmingoffset : Trimming offset (P or N)
  * @retval Trimming value (P or N): range: 0->31
  *         or OPAMP_FACTORYTRIMMING_DUMMY if trimming value is not available
 */

OPAMP_TrimmingValueTypeDef HAL_OPAMP_GetTrimOffset (OPAMP_HandleTypeDef *hopamp, uint32_t trimmingoffset)
{
  uint32_t oldusertrimming = 0;
  OPAMP_TrimmingValueTypeDef  oldtrimmingvaluep = 0, oldtrimmingvaluen = 0, trimmingvalue = 0;
  
  /* Check the OPAMP handle allocation */
  /* Value can be retrieved in HAL_OPAMP_STATE_READY state */
  if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_RESET) \
                      || (hopamp->State == HAL_OPAMP_STATE_BUSY) \
                      || (hopamp->State == HAL_OPAMP_STATE_CALIBBUSY)\
                      || (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED))
  {
    return OPAMP_FACTORYTRIMMING_DUMMY;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
    assert_param(IS_OPAMP_FACTORYTRIMMING(trimmingoffset));
    
    /* Check the trimming mode */
    if ((READ_BIT(hopamp->Instance->CSR,OPAMP_CSR_USERTRIM)) != RESET) 
    {
      /* User trimming is used */
      oldusertrimming = OPAMP_TRIMMING_USER;
      /* Store the TrimmingValueP & TrimmingValueN */
      oldtrimmingvaluep = (hopamp->Instance->CSR & OPAMP_CSR_TRIMOFFSETP) >> OPAMP_INPUT_NONINVERTING;
      oldtrimmingvaluen = (hopamp->Instance->CSR & OPAMP_CSR_TRIMOFFSETN) >> OPAMP_INPUT_INVERTING;
    }
    
    /* Set factory timming mode */
    CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_USERTRIM);
    
    /* Get factory trimming  */
    if (trimmingoffset == OPAMP_FACTORYTRIMMING_P)
    {
      /* Return TrimOffsetP */
     trimmingvalue = ((hopamp->Instance->CSR & OPAMP_CSR_TRIMOFFSETP) >> OPAMP_INPUT_NONINVERTING);
    }
    else 
    {
      /* Return TrimOffsetN */
      trimmingvalue = ((hopamp->Instance->CSR & OPAMP_CSR_TRIMOFFSETN) >> OPAMP_INPUT_INVERTING);
    }
    
    /* Restore user trimming configuration if it was formerly set */
    /* Check if user trimming was used */
    if (oldusertrimming == OPAMP_TRIMMING_USER) 
    {
      /* Restore user trimming */
      SET_BIT(hopamp->Instance->CSR,OPAMP_CSR_USERTRIM);
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, oldtrimmingvaluep<<OPAMP_INPUT_NONINVERTING);
      MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, oldtrimmingvaluen<<OPAMP_INPUT_INVERTING);
    }
  }  
  return trimmingvalue;
}
/**
  * @}
  */

/**
  * @}
  */

#endif /* STM32F303xC STM32F358xx STM32F302xC STM32F303x8 STM32F328xx
          STM32F302x8 STM32F301x8 STM32F318xx STM32F334x8 */

#endif /* HAL_OPAMP_MODULE_ENABLED */
/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
