From 5b81bc8ccbd342b8566d88fc9f17a73aec03b5b6 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Wed, 29 Jan 2025 21:34:25 -0500 Subject: initial commit --- .../Src/stm32u0xx_hal_opamp_ex.c | 436 +++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 Drivers/STM32U0xx_HAL_Driver/Src/stm32u0xx_hal_opamp_ex.c (limited to 'Drivers/STM32U0xx_HAL_Driver/Src/stm32u0xx_hal_opamp_ex.c') diff --git a/Drivers/STM32U0xx_HAL_Driver/Src/stm32u0xx_hal_opamp_ex.c b/Drivers/STM32U0xx_HAL_Driver/Src/stm32u0xx_hal_opamp_ex.c new file mode 100644 index 0000000..f4f3c8a --- /dev/null +++ b/Drivers/STM32U0xx_HAL_Driver/Src/stm32u0xx_hal_opamp_ex.c @@ -0,0 +1,436 @@ +/** + ****************************************************************************** + * @file stm32u0xx_hal_opamp_ex.c + * @author MCD Application Team + * @brief Extended OPAMP HAL module driver. + * This file provides firmware functions to manage the following + * functionalities of the operational amplifier(s)(OPAMP1, OPAMP2 etc) + * peripheral: + * + Extended Initialization and de-initialization functions + * + Extended Peripheral Control functions + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2021 STMicroelectronics. + * 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 "stm32u0xx_hal.h" + +/** @addtogroup STM32U0xx_HAL_Driver + * @{ + */ + +/** @defgroup OPAMPEx OPAMPEx + * @brief OPAMP Extended HAL module driver + * @{ + */ + +#ifdef HAL_OPAMP_MODULE_ENABLED + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private function prototypes -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +/** @defgroup OPAMP_Exported_Functions OPAMP Exported Functions + * @{ + */ + +#if defined(OPAMP2) +/** @addtogroup OPAMPEx_Exported_Functions_Group1 + * @brief Extended operation functions + * +@verbatim + =============================================================================== + ##### Extended IO operation functions ##### + =============================================================================== + [..] + (+) OPAMP Self calibration. + +@endverbatim + * @{ + */ + +/* 2 OPAMPS available */ +/* 2 OPAMPS can be calibrated in parallel */ + +/** + * @brief Run the self calibration of the 2 OPAMPs in parallel. + * @note Trimming values (PMOS & NMOS) are updated and user trimming is + * enabled is calibration is successful. + * @note Calibration is performed in the mode specified in OPAMP init + * structure (mode normal or low-power). To perform calibration for + * both modes, repeat this function twice after OPAMP init structure + * accordingly updated. + * @note Calibration runs about 10 ms (5 dichotomy steps, repeated for P + * and N transistors: 10 steps with 1 ms for each step). + * @param hopamp1 handle + * @param hopamp2 handle + * @retval HAL status + */ + +HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2) +{ + HAL_StatusTypeDef status = HAL_OK; + + uint32_t trimmingvaluen1; + uint32_t trimmingvaluep1; + uint32_t trimmingvaluen2; + uint32_t trimmingvaluep2; + + /* Selection of register of trimming depending on power mode: OTR or LPOTR */ + __IO uint32_t *tmp_opamp1_reg_trimming; + __IO uint32_t *tmp_opamp2_reg_trimming; + + uint32_t delta; + uint32_t opampmode1; + uint32_t opampmode2; + + if ((hopamp1 == NULL) || (hopamp2 == NULL)) + { + status = HAL_ERROR; + } + /* Check if OPAMP in calibration mode and calibration not yet enable */ + else if (hopamp1->State != HAL_OPAMP_STATE_READY) + { + status = HAL_ERROR; + } + else if (hopamp2->State != HAL_OPAMP_STATE_READY) + { + status = HAL_ERROR; + } + else + { + /* Check the parameter */ + assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance)); + assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance)); + + assert_param(IS_OPAMP_POWERMODE(hopamp1->Init.PowerMode)); + assert_param(IS_OPAMP_POWERMODE(hopamp2->Init.PowerMode)); + + /* Save OPAMP mode as in */ + /* the calibration is not working in PGA mode */ + opampmode1 = READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAMODE); + opampmode2 = READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAMODE); + + /* Use of standalone mode */ + MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_OPAMODE, OPAMP_STANDALONE_MODE); + MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_OPAMODE, OPAMP_STANDALONE_MODE); + + /* user trimming values are used for offset calibration */ + SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_USERTRIM); + SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_USERTRIM); + + /* Select trimming settings depending on power mode */ + if ((hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMALPOWER_NORMALSPEED) || \ + (hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMALPOWER_HIGHSPEED)) + { + tmp_opamp1_reg_trimming = &OPAMP1->OTR; + } + else + { + tmp_opamp1_reg_trimming = &OPAMP1->LPOTR; + } + + if ((hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMALPOWER_NORMALSPEED) || \ + (hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMALPOWER_HIGHSPEED)) + { + tmp_opamp2_reg_trimming = &OPAMP2->OTR; + } + else + { + tmp_opamp2_reg_trimming = &OPAMP2->LPOTR; + } + + /* Enable calibration */ + SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALON); + SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALON); + + /* 1st calibration - N */ + CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL); + CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL); + + /* Enable the selected opamp */ + SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAEN); + SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAEN); + + /* Init trimming counter */ + /* Medium value */ + trimmingvaluen1 = 16U; + trimmingvaluen2 = 16U; + delta = 8U; + + while (delta != 0U) + { + /* Set candidate trimming */ + /* OPAMP_POWERMODE_NORMALPOWER_HIGHSPEED or OPAMP_POWERMODE_NORMALPOWER_NORMALSPEED*/ + MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen1); + MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen2); + + /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */ + /* Offset trim time: during calibration, minimum time needed between */ + /* two steps to have 1 mV accuracy */ + HAL_Delay(OPAMP_TRIMMING_DELAY); + + if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT) != 0U) + { + /* OPAMP_CSR_CALOUT is HIGH try lower trimming */ + trimmingvaluen1 -= delta; + } + else + { + /* OPAMP_CSR_CALOUT is LOW try higher trimming */ + trimmingvaluen1 += delta; + } + + if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT) != 0U) + { + /* OPAMP_CSR_CALOUT is HIGH try lower trimming */ + trimmingvaluen2 -= delta; + } + else + { + /* OPAMP_CSR_CALOUT is LOW try higher trimming */ + trimmingvaluen2 += delta; + } + /* Divide range by 2 to continue dichotomy sweep */ + delta >>= 1U; + } + + /* Still need to check if right calibration is current value or one step below */ + /* Indeed the first value that causes the OUTCAL bit to change from 0 to 1 */ + /* Set candidate trimming */ + MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen1); + MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen2); + + /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */ + /* Offset trim time: during calibration, minimum time needed between */ + /* two steps to have 1 mV accuracy */ + HAL_Delay(OPAMP_TRIMMING_DELAY); + + if ((READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT)) == 0U) + { + /* Trimming value is actually one value more */ + trimmingvaluen1++; + MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen1); + } + + if ((READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT)) == 0U) + { + /* Trimming value is actually one value more */ + trimmingvaluen2++; + MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen2); + } + + /* 2nd calibration - P */ + SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL); + SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL); + + /* Init trimming counter */ + /* Medium value */ + trimmingvaluep1 = 16U; + trimmingvaluep2 = 16U; + delta = 8U; + + while (delta != 0U) + { + /* Set candidate trimming */ + /* OPAMP_POWERMODE_NORMALPOWER_HIGHSPEED or OPAMP_POWERMODE_NORMALPOWER_NORMALSPEED*/ + MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1 << OPAMP_INPUT_NONINVERTING)); + MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2 << OPAMP_INPUT_NONINVERTING)); + + /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */ + /* Offset trim time: during calibration, minimum time needed between */ + /* two steps to have 1 mV accuracy */ + HAL_Delay(OPAMP_TRIMMING_DELAY); + + if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT) != 0U) + { + /* OPAMP_CSR_CALOUT is HIGH try higher trimming */ + trimmingvaluep1 -= delta; + } + else + { + /* OPAMP_CSR_CALOUT is HIGH try lower trimming */ + trimmingvaluep1 += delta; + } + + if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT) != 0U) + { + /* OPAMP_CSR_CALOUT is HIGH try higher trimming */ + trimmingvaluep2 -= delta; + } + else + { + /* OPAMP_CSR_CALOUT is LOW try lower trimming */ + trimmingvaluep2 += delta; + } + /* Divide range by 2 to continue dichotomy sweep */ + delta >>= 1U; + } + + /* Still need to check if right calibration is current value or one step below */ + /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0 */ + /* Set candidate trimming */ + MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1 << OPAMP_INPUT_NONINVERTING)); + MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2 << OPAMP_INPUT_NONINVERTING)); + + /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */ + /* Offset trim time: during calibration, minimum time needed between */ + /* two steps to have 1 mV accuracy */ + HAL_Delay(OPAMP_TRIMMING_DELAY); + + if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT) != 0U) + { + /* Trimming value is actually one value more */ + trimmingvaluep1++; + MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1 << OPAMP_INPUT_NONINVERTING)); + } + + if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT) != 0U) + { + /* Trimming value is actually one value more */ + trimmingvaluep2++; + MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2 << OPAMP_INPUT_NONINVERTING)); + } + + /* Disable the OPAMPs */ + CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAEN); + CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAEN); + + /* Disable calibration & set normal mode (operating mode) */ + CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALON); + CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALON); + + /* Self calibration is successful */ + /* Store calibration (user trimming) results in init structure. */ + + /* Set user trimming mode */ + hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER; + hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER; + + /* Affect calibration parameters depending on mode normal/low power */ + if ((hopamp1->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER_NORMALSPEED) && \ + (hopamp1->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER_HIGHSPEED)) + { + /* Write calibration result N */ + hopamp1->Init.TrimmingValueN = trimmingvaluen1; + /* Write calibration result P */ + hopamp1->Init.TrimmingValueP = trimmingvaluep1; + } + else + { + /* Write calibration result N */ + hopamp1->Init.TrimmingValueNLowPower = trimmingvaluen1; + /* Write calibration result P */ + hopamp1->Init.TrimmingValuePLowPower = trimmingvaluep1; + } + + if ((hopamp2->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER_NORMALSPEED) && \ + (hopamp2->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER_HIGHSPEED)) + { + /* Write calibration result N */ + hopamp2->Init.TrimmingValueN = trimmingvaluen2; + /* Write calibration result P */ + hopamp2->Init.TrimmingValueP = trimmingvaluep2; + } + else + { + /* Write calibration result N */ + hopamp2->Init.TrimmingValueNLowPower = trimmingvaluen2; + /* Write calibration result P */ + hopamp2->Init.TrimmingValuePLowPower = trimmingvaluep2; + } + + /* Update OPAMP state */ + hopamp1->State = HAL_OPAMP_STATE_READY; + hopamp2->State = HAL_OPAMP_STATE_READY; + + /* Restore OPAMP mode after calibration */ + MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_OPAMODE, opampmode1); + MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_OPAMODE, opampmode2); + } + return status; +} + +/** + * @} + */ +#endif /* OPAMP2 */ + +/** @defgroup OPAMPEx_Exported_Functions_Group2 Peripheral Control functions + * @brief Peripheral Control functions + * +@verbatim + =============================================================================== + ##### Peripheral Control functions ##### + =============================================================================== + [..] + (+) OPAMP unlock. + +@endverbatim + * @{ + */ + +/** + * @brief Unlock the selected OPAMP configuration. + * @note This function must be called only when OPAMP is in state "locked". + * @param hopamp: OPAMP handle + * @retval HAL status + */ +HAL_StatusTypeDef HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef *hopamp) +{ + HAL_StatusTypeDef status = HAL_OK; + + /* Check the OPAMP handle allocation */ + /* Check if OPAMP locked */ + if (hopamp == NULL) + { + status = HAL_ERROR; + } + /* Check the OPAMP handle allocation */ + /* Check if OPAMP locked */ + else if (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED) + { + /* Check the parameter */ + assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance)); + + /* OPAMP state changed to locked */ + hopamp->State = HAL_OPAMP_STATE_BUSY; + } + else + { + status = HAL_ERROR; + } + + return status; +} + +/** + * @} + */ + +/** + * @} + */ + +#endif /* HAL_OPAMP_MODULE_ENABLED */ +/** + * @} + */ + +/** + * @} + */ + -- cgit v1.2.3