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 */ CLEAR_BIT (OPAMP->CSR, OPAMP_CSR_OPAXPD(hopamp)); /* 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 DeInitialize the OPAMP peripheral. * @note Deinitialization can be performed if the OPAMP configuration is locked. * (the lock is SW in L4) * @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 */ /* DeInit not allowed if calibration is ongoing */ if((hopamp == NULL) || (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 */ /* Mind that OPAMP1_CSR_OPARANGE of CSR of OPAMP1 remains unchanged (applies to both OPAMPs) */ /* OPAMP shall be disabled first separately */ CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN); MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_RESET_BITS, 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; /* Process unlocked */ __HAL_UNLOCK(hopamp); } 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 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 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)); /* Open all switches on non-inverting input, inverting input and output */ /* feedback. */ CLEAR_BIT(OPAMP->CSR, __OPAMP_CSR_ALL_SWITCHES(hopamp)); /* DeInit the low level hardware */ HAL_OPAMP_MspDeInit(hopamp); /* Update the OPAMP state*/ hopamp->State = HAL_OPAMP_STATE_RESET; } /* Process unlocked */ __HAL_UNLOCK(hopamp); return status; }
/** * @brief Initialize some features of OPAMP instance. * @note This function reset bit of calibration mode to ensure * to be in functional mode, in order to have OPAMP parameters * (inputs selection, ...) set with the corresponding OPAMP mode * to be effective. * @param OPAMPx OPAMP instance * @param OPAMP_InitStruct Pointer to a @ref LL_OPAMP_InitTypeDef structure * @retval An ErrorStatus enumeration value: * - SUCCESS: OPAMP registers are initialized * - ERROR: OPAMP registers are not initialized */ ErrorStatus LL_OPAMP_Init(OPAMP_TypeDef *OPAMPx, LL_OPAMP_InitTypeDef *OPAMP_InitStruct) { ErrorStatus status = SUCCESS; /* Check the parameters */ assert_param(IS_OPAMP_ALL_INSTANCE(OPAMPx)); assert_param(IS_LL_OPAMP_FUNCTIONAL_MODE(OPAMP_InitStruct->FunctionalMode)); assert_param(IS_LL_OPAMP_INPUT_NONINVERTING(OPAMPx, OPAMP_InitStruct->InputNonInverting)); /* Note: OPAMP inverting input can be used with OPAMP in mode standalone */ /* or PGA with external capacitors for filtering circuit. */ /* Otherwise (OPAMP in mode follower), OPAMP inverting input is */ /* not used (not connected to GPIO pin). */ if(OPAMP_InitStruct->FunctionalMode != LL_OPAMP_MODE_FOLLOWER) { assert_param(IS_LL_OPAMP_INPUT_INVERTING(OPAMPx, OPAMP_InitStruct->InputInverting)); } /* Note: Hardware constraint (refer to description of this function): */ /* OPAMP instance must not be locked. */ if(LL_OPAMP_IsLocked(OPAMPx) == 0U) { /* Configuration of OPAMP instance : */ /* - Functional mode */ /* - Input non-inverting */ /* - Input inverting */ /* Note: Bit OPAMP_CSR_CALON reset to ensure to be in functional mode. */ if(OPAMP_InitStruct->FunctionalMode != LL_OPAMP_MODE_FOLLOWER) { MODIFY_REG(OPAMPx->CSR, OPAMP_CSR_CALON | OPAMP_CSR_VMSEL | OPAMP_CSR_VPSEL , OPAMP_InitStruct->FunctionalMode | OPAMP_InitStruct->InputNonInverting | OPAMP_InitStruct->InputInverting ); } else { MODIFY_REG(OPAMPx->CSR, OPAMP_CSR_CALON | OPAMP_CSR_VMSEL | OPAMP_CSR_VPSEL , LL_OPAMP_MODE_FOLLOWER | OPAMP_InitStruct->InputNonInverting ); } } else { /* Initialization error: OPAMP instance is locked. */ status = ERROR; } return status; }
/** * @brief Initialize some features of OPAMP instance. * @note This function reset bit of calibration mode to ensure * to be in functional mode, in order to have OPAMP parameters * (inputs selection, ...) set with the corresponding OPAMP mode * to be effective. * @note This function configures features of the selected OPAMP instance. * Some features are also available at scope OPAMP common instance * (common to several OPAMP instances). * Refer to functions having argument "OPAMPxy_COMMON" as parameter. * @param OPAMPx OPAMP instance * @param OPAMP_InitStruct Pointer to a @ref LL_OPAMP_InitTypeDef structure * @retval An ErrorStatus enumeration value: * - SUCCESS: OPAMP registers are initialized * - ERROR: OPAMP registers are not initialized */ ErrorStatus LL_OPAMP_Init(OPAMP_TypeDef *OPAMPx, LL_OPAMP_InitTypeDef *OPAMP_InitStruct) { /* Check the parameters */ assert_param(IS_OPAMP_ALL_INSTANCE(OPAMPx)); assert_param(IS_LL_OPAMP_POWER_MODE(OPAMP_InitStruct->PowerMode)); assert_param(IS_LL_OPAMP_FUNCTIONAL_MODE(OPAMP_InitStruct->FunctionalMode)); assert_param(IS_LL_OPAMP_INPUT_NONINVERTING(OPAMPx, OPAMP_InitStruct->InputNonInverting)); /* Note: OPAMP inverting input can be used with OPAMP in mode standalone */ /* or PGA with external capacitors for filtering circuit. */ /* Otherwise (OPAMP in mode follower), OPAMP inverting input is */ /* not used (not connected to GPIO pin). */ if(OPAMP_InitStruct->FunctionalMode != LL_OPAMP_MODE_FOLLOWER) { assert_param(IS_LL_OPAMP_INPUT_INVERTING(OPAMPx, OPAMP_InitStruct->InputInverting)); } /* Configuration of OPAMP instance : */ /* - PowerMode */ /* - Functional mode */ /* - Input non-inverting */ /* - Input inverting */ /* Note: Bit OPAMP_CSR_CALON reset to ensure to be in functional mode. */ if(OPAMP_InitStruct->FunctionalMode != LL_OPAMP_MODE_FOLLOWER) { MODIFY_REG(OPAMPx->CSR, OPAMP_CSR_OPALPM | OPAMP_CSR_OPAMODE | OPAMP_CSR_CALON | OPAMP_CSR_VMSEL | OPAMP_CSR_VPSEL , (OPAMP_InitStruct->PowerMode & OPAMP_POWERMODE_CSR_BIT_MASK) | OPAMP_InitStruct->FunctionalMode | OPAMP_InitStruct->InputNonInverting | OPAMP_InitStruct->InputInverting ); } else { MODIFY_REG(OPAMPx->CSR, OPAMP_CSR_OPALPM | OPAMP_CSR_OPAMODE | OPAMP_CSR_CALON | OPAMP_CSR_VMSEL | OPAMP_CSR_VPSEL , (OPAMP_InitStruct->PowerMode & OPAMP_POWERMODE_CSR_BIT_MASK) | LL_OPAMP_MODE_FOLLOWER | OPAMP_InitStruct->InputNonInverting | LL_OPAMP_INPUT_INVERT_CONNECT_NO ); } return SUCCESS; }
/** * @brief De-initialize registers of the selected OPAMP instance * to their default reset values. * @param OPAMPx OPAMP instance * @retval An ErrorStatus enumeration value: * - SUCCESS: OPAMP registers are de-initialized * - ERROR: OPAMP registers are not de-initialized */ ErrorStatus LL_OPAMP_DeInit(OPAMP_TypeDef* OPAMPx) { ErrorStatus status = SUCCESS; /* Check the parameters */ assert_param(IS_OPAMP_ALL_INSTANCE(OPAMPx)); LL_OPAMP_WriteReg(OPAMPx, CSR, 0x00000000U); return status; }
/** * @brief DeInitializes the OPAMP peripheral * @note Deinitialization can be performed if the OPAMP configuration is locked. * (the OPAMP lock is SW in STM32L1) * @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 */ /* DeInit not allowed if calibration is ongoing */ if((hopamp == NULL) || (hopamp->State == HAL_OPAMP_STATE_CALIBBUSY)) { status = HAL_ERROR; } else { /* Check the parameter */ assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance)); /* Disable the selected opamp */ SET_BIT (OPAMP->CSR, OPAMP_CSR_OPAXPD(hopamp)); /* Open all switches on non-inverting input, inverting input and output */ /* feedback. */ /* Note: OPAMP register CSR is written directly, independently of OPAMP */ /* instance, because all OPAMP settings are dispatched in the same */ /* register. */ /* Settings of bits for each OPAMP instances are managed case by */ /* case using macro (OPAMP_CSR_S3SELX(), OPAMP_CSR_ANAWSELX(), ...) */ CLEAR_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES(hopamp)); /* Note: Registers and bits shared with other OPAMP instances are kept */ /* unchanged, to not impact other OPAMP while operating on the */ /* selected OPAMP. */ /* Unchanged: bit OPAMP_OTR_OT_USER (parameter "UserTrimming") */ /* bit OPAMP_CSR_AOP_RANGE (parameter "PowerSupplyRange")*/ /* DeInit the low level hardware: GPIO, CLOCK and NVIC */ HAL_OPAMP_MspDeInit(hopamp); /* Update the OPAMP state*/ hopamp->State = HAL_OPAMP_STATE_RESET; } /* Process unlocked */ __HAL_UNLOCK(hopamp); 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 */ /* DeInit not allowed if calibration is ongoing */ if((hopamp == NULL) || (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 */ /* When OPAMP is locked, unlocking can be achieved thanks to */ /* __HAL_RCC_SYSCFG_CLK_DISABLE() call within HAL_OPAMP_MspDeInit */ /* Note that __HAL_RCC_SYSCFG_CLK_DISABLE() also disables comparator */ HAL_OPAMP_MspDeInit(hopamp); if (OPAMP_CSR_RESET_VALUE == hopamp->Instance->CSR) { /* Update the OPAMP state */ hopamp->State = HAL_OPAMP_STATE_RESET; } else /* RESET STATE */ { /* DeInit not complete */ /* It can be the case if OPAMP was formerly locked */ status = HAL_ERROR; /* The OPAMP state is NOT updated */ } } /* Process unlocked */ __HAL_UNLOCK(hopamp); return status; }
/** * @brief De-initialize registers of the selected OPAMP instance * to their default reset values. * @note If comparator is locked, de-initialization by software is * not possible. * The only way to unlock the comparator is a device hardware reset. * @param OPAMPx OPAMP instance * @retval An ErrorStatus enumeration value: * - SUCCESS: OPAMP registers are de-initialized * - ERROR: OPAMP registers are not de-initialized */ ErrorStatus LL_OPAMP_DeInit(OPAMP_TypeDef* OPAMPx) { ErrorStatus status = SUCCESS; /* Check the parameters */ assert_param(IS_OPAMP_ALL_INSTANCE(OPAMPx)); /* Note: Hardware constraint (refer to description of this function): */ /* OPAMP instance must not be locked. */ if(LL_OPAMP_IsLocked(OPAMPx) == 0U) { LL_OPAMP_WriteReg(OPAMPx, CSR, 0x00000000U); } else { /* OPAMP instance is locked: de-initialization by software is */ /* not possible. */ /* The only way to unlock the OPAMP is a device hardware reset. */ status = ERROR; } return status; }
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 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 {
/** * @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; uint32_t tmp_csr = 0; /* Temporary variable to update register CSR, except bits ANAWSSELx, S7SEL2, OPA_RANGE, OPAxCALOUT */ /* 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) ) { status = HAL_ERROR; } else { /* Check the parameter */ assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance)); /* Set OPAMP parameters */ assert_param(IS_OPAMP_POWER_SUPPLY_RANGE(hopamp->Init.PowerSupplyRange)); assert_param(IS_OPAMP_POWERMODE(hopamp->Init.PowerMode)); assert_param(IS_OPAMP_FUNCTIONAL_NORMALMODE(hopamp->Init.Mode)); assert_param(IS_OPAMP_NONINVERTING_INPUT_CHECK_INSTANCE(hopamp, hopamp->Init.NonInvertingInput)); assert_param(IS_OPAMP_TRIMMING(hopamp->Init.UserTrimming)); if (hopamp->Init.Mode != OPAMP_FOLLOWER_MODE) { assert_param(IS_OPAMP_INVERTING_INPUT(hopamp->Init.InvertingInput)); } if (hopamp->Init.UserTrimming == OPAMP_TRIMMING_USER) { if (hopamp->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueP)); assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueN)); } else { assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValuePLowPower)); assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueNLowPower)); } } if(hopamp->State == HAL_OPAMP_STATE_RESET) { /* Allocate lock resource and initialize it */ hopamp->Lock = HAL_UNLOCKED; } /* Call MSP init function */ HAL_OPAMP_MspInit(hopamp); /* Set OPAMP parameters */ /* - Set internal switches in function of: */ /* - OPAMP selected mode: standalone or follower. */ /* - Non-inverting input connection */ /* - Inverting input connection */ /* - Set power supply range */ /* - Set power mode and associated calibration parameters */ /* Get OPAMP CSR register into temporary variable */ /* Note: OPAMP register CSR is written directly, independently of OPAMP */ /* instance, because all OPAMP settings are dispatched in the same */ /* register. */ /* Settings of bits for each OPAMP instances are managed case by */ /* case using macro (OPAMP_CSR_S3SELX(), OPAMP_CSR_ANAWSELX(), ...) */ tmp_csr = OPAMP->CSR; /* Open all switches on non-inverting input, inverting input and output */ /* feedback. */ CLEAR_BIT(tmp_csr, OPAMP_CSR_ALL_SWITCHES(hopamp)); /* Set internal switches in function of OPAMP mode selected: standalone */ /* or follower. */ /* If follower mode is selected, feedback switch S3 is closed and */ /* inverting inputs switches are let opened. */ /* If standalone mode is selected, feedback switch S3 is let opened and */ /* the selected inverting inputs switch is closed. */ if (hopamp->Init.Mode == OPAMP_FOLLOWER_MODE) { /* Follower mode: Close switches S3 and SanB */ SET_BIT(tmp_csr, OPAMP_CSR_S3SELX(hopamp)); } else { /* Set internal switches in function of inverting input selected: */ /* Close switch to connect OPAMP inverting input to the selected */ /* input: dedicated IO pin or alternative IO pin available on some */ /* device packages. */ if (hopamp->Init.InvertingInput == OPAMP_INVERTINGINPUT_IO0) { /* Close switch to connect OPAMP non-inverting input to */ /* dedicated IO pin low-leakage. */ SET_BIT(tmp_csr, OPAMP_CSR_S4SELX(hopamp)); } else { /* Close switch to connect OPAMP inverting input to alternative */ /* IO pin available on some device packages. */ SET_BIT(tmp_csr, OPAMP_CSR_ANAWSELX(hopamp)); } } /* Set internal switches in function of non-inverting input selected: */ /* Close switch to connect OPAMP non-inverting input to the selected */ /* input: dedicated IO pin or DAC channel. */ if (hopamp->Init.NonInvertingInput == OPAMP_NONINVERTINGINPUT_IO0) { /* Close switch to connect OPAMP non-inverting input to */ /* dedicated IO pin low-leakage. */ SET_BIT(tmp_csr, OPAMP_CSR_S5SELX(hopamp)); } else if (hopamp->Init.NonInvertingInput == OPAMP_NONINVERTINGINPUT_DAC_CH1) { /* Particular case for connection to DAC channel 1: */ /* OPAMP_NONINVERTINGINPUT_DAC_CH1 available on OPAMP1 and OPAMP2 only */ /* (OPAMP3 availability depends on device category). */ if ((hopamp->Instance == OPAMP1) || (hopamp->Instance == OPAMP2)) { /* Close switch to connect OPAMP non-inverting input to */ /* DAC channel 1. */ SET_BIT(tmp_csr, OPAMP_CSR_S6SELX(hopamp)); } else { /* Set HAL status to error if another OPAMP instance as OPAMP1 or */ /* OPAMP2 is intended to be connected to DAC channel 2. */ status = HAL_ERROR; } } else /* if (hopamp->Init.NonInvertingInput == */ /* OPAMP_NONINVERTINGINPUT_DAC_CH2 ) */ { /* Particular case for connection to DAC channel 2: */ /* OPAMP_NONINVERTINGINPUT_DAC_CH2 available on OPAMP2 and OPAMP3 only */ /* (OPAMP3 availability depends on device category). */ if (hopamp->Instance == OPAMP2) { /* Close switch to connect OPAMP non-inverting input to */ /* DAC channel 2. */ SET_BIT(tmp_csr, OPAMP_CSR_S7SEL2); } /* If OPAMP3 is selected (if available) */ else if (hopamp->Instance != OPAMP1) { /* Close switch to connect OPAMP non-inverting input to */ /* DAC channel 2. */ SET_BIT(tmp_csr, OPAMP_CSR_S6SELX(hopamp)); } else { /* Set HAL status to error if another OPAMP instance as OPAMP2 or */ /* OPAMP3 (if available) is intended to be connected to DAC channel 2.*/ status = HAL_ERROR; } } /* Continue OPAMP configuration if settings of switches are correct */ if (status != HAL_ERROR) { /* Set power mode and associated calibration parameters */ if (hopamp->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* Set normal mode */ CLEAR_BIT(tmp_csr, OPAMP_CSR_OPAXLPM(hopamp)); if (hopamp->Init.UserTrimming == OPAMP_TRIMMING_USER) { /* Set calibration mode (factory or user) and values for */ /* transistors differential pair high (PMOS) and low (NMOS) for */ /* normal mode. */ MODIFY_REG(OPAMP->OTR, OPAMP_OTR_OT_USER | OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_N, OPAMP_TRIM_VALUE_MASK) | OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_P, OPAMP_TRIM_VALUE_MASK) , hopamp->Init.UserTrimming | OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_N, hopamp->Init.TrimmingValueN) | OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_P, hopamp->Init.TrimmingValueP) ); } else { /* Set calibration mode to factory */ CLEAR_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER); } } else { /* Set low power mode */ SET_BIT(tmp_csr, OPAMP_CSR_OPAXLPM(hopamp)); if (hopamp->Init.UserTrimming == OPAMP_TRIMMING_USER) { /* Set calibration mode to user trimming */ SET_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER); /* Set values for transistors differential pair high (PMOS) and low */ /* (NMOS) for low power mode. */ MODIFY_REG(OPAMP->LPOTR, OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_N, OPAMP_TRIM_VALUE_MASK) | OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_P, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_N, hopamp->Init.TrimmingValueNLowPower) | OPAMP_OFFSET_TRIM_SET(hopamp, OPAMP_FACTORYTRIMMING_P, hopamp->Init.TrimmingValuePLowPower) ); } else { /* Set calibration mode to factory trimming */ CLEAR_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER); } } /* Configure the power supply range */ MODIFY_REG(tmp_csr, OPAMP_CSR_AOP_RANGE, hopamp->Init.PowerSupplyRange); /* Set OPAMP CSR register from temporary variable */ /* This allows to apply all changes on one time, in case of update on */ /* the fly with OPAMP previously set and running: */ /* - to avoid hazardous transient switches settings (risk of short */ /* circuit) */ /* - to avoid interruption of input signal */ OPAMP->CSR = tmp_csr; /* Update the OPAMP state */ /* If coming from state reset: Update from state RESET to state READY */ if (hopamp->State == HAL_OPAMP_STATE_RESET) { hopamp->State = HAL_OPAMP_STATE_READY; } /* else: OPAMP state remains READY or BUSY state (no update) */ } } return status; }
/** * @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) { 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) { 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 */ __HAL_RCC_SYSCFG_CLK_ENABLE(); if(hopamp->State == HAL_OPAMP_STATE_RESET) { /* Allocate lock resource and initialize it */ hopamp->Lock = HAL_UNLOCKED; } /* 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 /* OPAMP_STANDALONE_MODE */ { 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 Run the self calibration of the 3 OPAMPs in parallel. * @note Trimming values (PMOS & NMOS) are updated and user trimming is * enabled is calibration is succesful. * @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 dichotmy steps, repeated for P * and N transistors: 10 steps with 1 ms for each step). * @param hopamp1 handle * @param hopamp2 handle * @param hopamp3 handle * @retval HAL status */ HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2, OPAMP_HandleTypeDef *hopamp3) { HAL_StatusTypeDef status = HAL_OK; uint32_t* opamp1_trimmingvalue = 0; uint32_t opamp1_trimmingvaluen = 0; uint32_t opamp1_trimmingvaluep = 0; uint32_t* opamp2_trimmingvalue = 0; uint32_t opamp2_trimmingvaluen = 0; uint32_t opamp2_trimmingvaluep = 0; uint32_t* opamp3_trimmingvalue = 0; uint32_t opamp3_trimmingvaluen = 0; uint32_t opamp3_trimmingvaluep = 0; uint32_t trimming_diff_pair = 0; /* Selection of differential transistors pair high or low */ __IO uint32_t* tmp_opamp1_reg_trimming; /* Selection of register of trimming depending on power mode: OTR or LPOTR */ __IO uint32_t* tmp_opamp2_reg_trimming; __IO uint32_t* tmp_opamp3_reg_trimming; uint32_t tmp_opamp1_otr_otuser = 0; /* Selection of bit OPAMP_OTR_OT_USER depending on trimming register pointed: OTR or LPOTR */ uint32_t tmp_opamp2_otr_otuser = 0; uint32_t tmp_opamp3_otr_otuser = 0; uint32_t tmp_Opa1calout_DefaultSate = 0; /* Bit OPAMP_CSR_OPA1CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */ uint32_t tmp_Opa2calout_DefaultSate = 0; /* Bit OPAMP_CSR_OPA2CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */ uint32_t tmp_Opa3calout_DefaultSate = 0; /* Bit OPAMP_CSR_OPA3CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */ uint32_t tmp_OpaxSwitchesContextBackup = 0; uint8_t trimming_diff_pair_iteration_count = 0; /* For calibration loop algorithm: to repeat the calibration loop for both differential transistors pair high and low */ uint8_t delta = 0; /* For calibration loop algorithm: Variable for dichotomy steps value */ uint8_t final_step_check = 0; /* For calibration loop algorithm: Flag for additional check of last trimming step */ /* Check the OPAMP handle allocation */ /* Check if OPAMP locked */ if((hopamp1 == NULL) || (hopamp1->State == HAL_OPAMP_STATE_BUSYLOCKED) || (hopamp2 == NULL) || (hopamp2->State == HAL_OPAMP_STATE_BUSYLOCKED) || (hopamp3 == NULL) || (hopamp3->State == HAL_OPAMP_STATE_BUSYLOCKED) ) { status = HAL_ERROR; } else { /* Check if OPAMP in calibration mode and calibration not yet enable */ if((hopamp1->State == HAL_OPAMP_STATE_READY) && (hopamp2->State == HAL_OPAMP_STATE_READY) && (hopamp3->State == HAL_OPAMP_STATE_READY) ) { /* Check the parameter */ assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance)); assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance)); assert_param(IS_OPAMP_ALL_INSTANCE(hopamp3->Instance)); assert_param(IS_OPAMP_POWERMODE(hopamp1->Init.PowerMode)); assert_param(IS_OPAMP_POWERMODE(hopamp2->Init.PowerMode)); assert_param(IS_OPAMP_POWERMODE(hopamp3->Init.PowerMode)); /* Update OPAMP state */ hopamp1->State = HAL_OPAMP_STATE_CALIBBUSY; hopamp2->State = HAL_OPAMP_STATE_CALIBBUSY; hopamp3->State = HAL_OPAMP_STATE_CALIBBUSY; /* Backup of switches configuration to restore it at the end of the */ /* calibration. */ tmp_OpaxSwitchesContextBackup = READ_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES_ALL_OPAMPS); /* Open all switches on non-inverting input, inverting input and output */ /* feedback. */ CLEAR_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES_ALL_OPAMPS); /* Set calibration mode to user programmed trimming values */ SET_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER); /* Select trimming settings depending on power mode */ if (hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { tmp_opamp1_otr_otuser = OPAMP_OTR_OT_USER; tmp_opamp1_reg_trimming = &OPAMP->OTR; } else { tmp_opamp1_otr_otuser = 0x00000000; tmp_opamp1_reg_trimming = &OPAMP->LPOTR; } if (hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { tmp_opamp2_otr_otuser = OPAMP_OTR_OT_USER; tmp_opamp2_reg_trimming = &OPAMP->OTR; } else { tmp_opamp2_otr_otuser = 0x00000000; tmp_opamp2_reg_trimming = &OPAMP->LPOTR; } if (hopamp3->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { tmp_opamp3_otr_otuser = OPAMP_OTR_OT_USER; tmp_opamp3_reg_trimming = &OPAMP->OTR; } else { tmp_opamp3_otr_otuser = 0x00000000; tmp_opamp3_reg_trimming = &OPAMP->LPOTR; } /* Enable the selected opamp */ CLEAR_BIT (OPAMP->CSR, OPAMP_CSR_OPAXPD_ALL); /* Perform trimming for both differential transistors pair high and low */ for (trimming_diff_pair_iteration_count = 0; trimming_diff_pair_iteration_count <=1; trimming_diff_pair_iteration_count++) { if (trimming_diff_pair_iteration_count == 0) { /* Calibration of transistors differential pair high (NMOS) */ trimming_diff_pair = OPAMP_FACTORYTRIMMING_N; opamp1_trimmingvalue = &opamp1_trimmingvaluen; opamp2_trimmingvalue = &opamp2_trimmingvaluen; opamp3_trimmingvalue = &opamp3_trimmingvaluen; /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value */ /* is 00000b. Used to detect the bit toggling during trimming. */ tmp_Opa1calout_DefaultSate = RESET; tmp_Opa2calout_DefaultSate = RESET; tmp_Opa3calout_DefaultSate = RESET; /* Enable calibration for N differential pair */ MODIFY_REG(OPAMP->CSR, OPAMP_CSR_OPAXCAL_L_ALL, OPAMP_CSR_OPAXCAL_H_ALL); } else /* (trimming_diff_pair_iteration_count == 1) */ { /* Calibration of transistors differential pair low (PMOS) */ trimming_diff_pair = OPAMP_FACTORYTRIMMING_P; opamp1_trimmingvalue = &opamp1_trimmingvaluep; opamp2_trimmingvalue = &opamp2_trimmingvaluep; opamp3_trimmingvalue = &opamp3_trimmingvaluep; /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value */ /* is 00000b. Used to detect the bit toggling during trimming. */ tmp_Opa1calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp1); tmp_Opa2calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp2); tmp_Opa3calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp3); /* Enable calibration for P differential pair */ MODIFY_REG(OPAMP->CSR, OPAMP_CSR_OPAXCAL_H_ALL, OPAMP_CSR_OPAXCAL_L_ALL); } /* Perform calibration parameter search by dichotomy sweep */ /* - Delta initial value 16: for 5 dichotomy steps: 16 for the */ /* initial range, then successive delta sweeps (8, 4, 2, 1). */ /* can extend the search range to +/- 15 units. */ /* - Trimming initial value 15: search range will go from 0 to 30 */ /* (Trimming value 31 is forbidden). */ /* Note: After dichotomy sweep, the trimming result is determined. */ /* However, the final trimming step is deduced from previous */ /* trimming steps tested but is not effectively tested. */ /* An additional test step (using variable "final_step_check") */ /* allow to Test the final trimming step. */ *opamp1_trimmingvalue = 15; *opamp2_trimmingvalue = 15; *opamp3_trimmingvalue = 15; delta = 16; while ((delta != 0) || (final_step_check == 1)) { /* Set candidate trimming */ MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, *opamp1_trimmingvalue) | tmp_opamp1_otr_otuser); MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, *opamp2_trimmingvalue) | tmp_opamp2_otr_otuser); MODIFY_REG(*tmp_opamp3_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, *opamp3_trimmingvalue) | tmp_opamp3_otr_otuser); /* Offset trimming time: during calibration, minimum time needed */ /* between two steps to have 1 mV accuracy. */ HAL_Delay(OPAMP_TRIMMING_DELAY); /* Set flag for additional check of last trimming step equal to */ /* dichotomy step before its division by 2 (equivalent to previous */ /* value of dichotomy step). */ final_step_check = delta; /* Divide range by 2 to continue dichotomy sweep */ delta >>= 1; /* Set trimming values for next iteration in function of trimming */ /* result toggle (versus initial state). */ /* Trimming values update with dichotomy delta of previous */ /* iteration. */ /* Note: on the last trimming loop, delta is equal to 0 and */ /* therefore has no effect. */ if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp1)) != tmp_Opa1calout_DefaultSate) { /* If calibration output is has toggled, try lower trimming */ *opamp1_trimmingvalue -= delta; } else { /* If calibration output is has not toggled, try higher trimming */ *opamp1_trimmingvalue += delta; } if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp2)) != tmp_Opa2calout_DefaultSate) { /* If calibration output is has toggled, try lower trimming */ *opamp2_trimmingvalue -= delta; } else { /* If calibration output is has not toggled, try higher trimming */ *opamp2_trimmingvalue += delta; } if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp3)) != tmp_Opa3calout_DefaultSate) { /* If calibration output is has toggled, try lower trimming */ *opamp3_trimmingvalue -= delta; } else { /* If calibration output is has not toggled, try higher trimming */ *opamp3_trimmingvalue += delta; } } /* Check trimming result of the selected step and perform final fine */ /* trimming. */ /* - If calibration output is has toggled: the current step is */ /* already optimized. */ /* - If calibration output is has not toggled: the current step can */ /* be optimized by incrementing it of one step. */ if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp1)) == tmp_Opa1calout_DefaultSate) { *opamp1_trimmingvalue += 1; /* Set final fine trimming */ MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, *opamp1_trimmingvalue) | tmp_opamp1_otr_otuser); } if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp2)) == tmp_Opa2calout_DefaultSate) { *opamp2_trimmingvalue += 1; /* Set final fine trimming */ MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, *opamp2_trimmingvalue) | tmp_opamp2_otr_otuser); } if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp3)) == tmp_Opa3calout_DefaultSate) { *opamp3_trimmingvalue += 1; /* Set final fine trimming */ MODIFY_REG(*tmp_opamp3_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, *opamp3_trimmingvalue) | tmp_opamp3_otr_otuser); } } /* Disable calibration for P and N differential pairs */ /* Disable the selected opamp */ CLEAR_BIT (OPAMP->CSR, (OPAMP_CSR_OPAXCAL_H_ALL | OPAMP_CSR_OPAXCAL_L_ALL | OPAMP_CSR_OPAXPD_ALL )); /* Backup of switches configuration to restore it at the end of the */ /* calibration. */ SET_BIT(OPAMP->CSR, tmp_OpaxSwitchesContextBackup); /* 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; hopamp3->Init.UserTrimming = OPAMP_TRIMMING_USER; /* Affect calibration parameters depending on mode normal/low power */ if (hopamp1->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* Write calibration result N */ hopamp1->Init.TrimmingValueN = opamp1_trimmingvaluen; /* Write calibration result P */ hopamp1->Init.TrimmingValueP = opamp1_trimmingvaluep; } else { /* Write calibration result N */ hopamp1->Init.TrimmingValueNLowPower = opamp1_trimmingvaluen; /* Write calibration result P */ hopamp1->Init.TrimmingValuePLowPower = opamp1_trimmingvaluep; } if (hopamp2->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* Write calibration result N */ hopamp2->Init.TrimmingValueN = opamp2_trimmingvaluen; /* Write calibration result P */ hopamp2->Init.TrimmingValueP = opamp2_trimmingvaluep; } else { /* Write calibration result N */ hopamp2->Init.TrimmingValueNLowPower = opamp2_trimmingvaluen; /* Write calibration result P */ hopamp2->Init.TrimmingValuePLowPower = opamp2_trimmingvaluep; } if (hopamp3->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* Write calibration result N */ hopamp3->Init.TrimmingValueN = opamp3_trimmingvaluen; /* Write calibration result P */ hopamp3->Init.TrimmingValueP = opamp3_trimmingvaluep; } else { /* Write calibration result N */ hopamp3->Init.TrimmingValueNLowPower = opamp3_trimmingvaluen; /* Write calibration result P */ hopamp3->Init.TrimmingValuePLowPower = opamp3_trimmingvaluep; } /* Update OPAMP state */ hopamp1->State = HAL_OPAMP_STATE_READY; hopamp2->State = HAL_OPAMP_STATE_READY; hopamp3->State = HAL_OPAMP_STATE_READY; } else {
/** * @brief Initializes the OPAMP according to the specified * parameters in the OPAMP_InitTypeDef and initialize 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; uint32_t updateotrlpotr = 0; /* 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_POWER_SUPPLY_RANGE(hopamp->Init.PowerSupplyRange)); assert_param(IS_OPAMP_POWERMODE(hopamp->Init.PowerMode)); 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) { assert_param(IS_OPAMP_INVERTING_INPUT_STANDALONE(hopamp->Init.InvertingInput)); } if ((hopamp->Init.Mode) == OPAMP_PGA_MODE) { assert_param(IS_OPAMP_INVERTING_INPUT_PGA(hopamp->Init.InvertingInput)); } if ((hopamp->Init.Mode) == OPAMP_PGA_MODE) { assert_param(IS_OPAMP_PGA_GAIN(hopamp->Init.PgaGain)); } assert_param(IS_OPAMP_TRIMMING(hopamp->Init.UserTrimming)); if ((hopamp->Init.UserTrimming) == OPAMP_TRIMMING_USER) { if (hopamp->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueP)); assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueN)); } else { assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValuePLowPower)); assert_param(IS_OPAMP_TRIMMINGVALUE(hopamp->Init.TrimmingValueNLowPower)); } } if(hopamp->State == HAL_OPAMP_STATE_RESET) { /* Allocate lock resource and initialize it */ hopamp->Lock = HAL_UNLOCKED; } /* Call MSP init function */ HAL_OPAMP_MspInit(hopamp); /* Set operating mode */ CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_CALON); if (hopamp->Init.Mode == OPAMP_PGA_MODE) { MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_INIT_MASK_PGA, \ hopamp->Init.PowerMode | \ hopamp->Init.Mode | \ hopamp->Init.PgaGain | \ hopamp->Init.InvertingInput | \ hopamp->Init.NonInvertingInput | \ hopamp->Init.UserTrimming); } if (hopamp->Init.Mode == OPAMP_FOLLOWER_MODE) { /* In Follower mode InvertingInput is Not Applicable */ MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_INIT_MASK_FOLLOWER, \ hopamp->Init.PowerMode | \ hopamp->Init.Mode | \ hopamp->Init.NonInvertingInput | \ hopamp->Init.UserTrimming); } if (hopamp->Init.Mode == OPAMP_STANDALONE_MODE) { MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_INIT_MASK_STANDALONE, \ hopamp->Init.PowerMode | \ hopamp->Init.Mode | \ hopamp->Init.InvertingInput | \ hopamp->Init.NonInvertingInput | \ hopamp->Init.UserTrimming); } if (hopamp->Init.UserTrimming == OPAMP_TRIMMING_USER) { /* Set power mode and associated calibration parameters */ if (hopamp->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* OPAMP_POWERMODE_NORMAL */ /* Set calibration mode (factory or user) and values for */ /* transistors differential pair high (PMOS) and low (NMOS) for */ /* normal mode. */ updateotrlpotr = (((hopamp->Init.TrimmingValueP) << (OPAMP_INPUT_NONINVERTING)) \ | (hopamp->Init.TrimmingValueN)); MODIFY_REG(hopamp->Instance->OTR, OPAMP_OTR_TRIMOFFSETN | OPAMP_OTR_TRIMOFFSETP, updateotrlpotr); } else { /* OPAMP_POWERMODE_LOWPOWER */ /* transistors differential pair high (PMOS) and low (NMOS) for */ /* low power mode. */ updateotrlpotr = (((hopamp->Init.TrimmingValuePLowPower) << (OPAMP_INPUT_NONINVERTING)) \ | (hopamp->Init.TrimmingValueNLowPower)); MODIFY_REG(hopamp->Instance->LPOTR, OPAMP_OTR_TRIMOFFSETN | OPAMP_OTR_TRIMOFFSETP, updateotrlpotr); } } /* Configure the power supply range */ /* The OPAMP_CSR_OPARANGE is common configuration for all OPAMPs */ /* bit OPAMP1_CSR_OPARANGE is used for both OPAMPs */ MODIFY_REG(OPAMP1->CSR, OPAMP1_CSR_OPARANGE, hopamp->Init.PowerSupplyRange); /* 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 Run the self calibration of one OPAMP * @note Trimming values (PMOS & NMOS) are updated and user trimming is * enabled is calibration is succesful. * @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 dichotmy steps, repeated for P * and N transistors: 10 steps with 1 ms for each step). * @param hopamp: handle * @retval Updated offset trimming values (PMOS & NMOS), user trimming is enabled * @retval HAL status */ HAL_StatusTypeDef HAL_OPAMP_SelfCalibrate(OPAMP_HandleTypeDef* hopamp) { HAL_StatusTypeDef status = HAL_OK; uint32_t* opamp_trimmingvalue = 0; uint32_t opamp_trimmingvaluen = 0; uint32_t opamp_trimmingvaluep = 0; uint32_t trimming_diff_pair = 0; /* Selection of differential transistors pair high or low */ __IO uint32_t* tmp_opamp_reg_trimming; /* Selection of register of trimming depending on power mode: OTR or LPOTR */ uint32_t tmp_opamp_otr_otuser = 0; /* Selection of bit OPAMP_OTR_OT_USER depending on trimming register pointed: OTR or LPOTR */ uint32_t tmp_Opaxcalout_DefaultSate = 0; /* Bit OPAMP_CSR_OPAXCALOUT default state when trimming value is 00000b. Used to detect the bit toggling */ uint32_t tmp_OpaxSwitchesContextBackup = 0; uint8_t trimming_diff_pair_iteration_count = 0; uint8_t delta = 0; /* 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)); assert_param(IS_OPAMP_POWERMODE(hopamp->Init.PowerMode)); /* Update OPAMP state */ hopamp->State = HAL_OPAMP_STATE_CALIBBUSY; /* Backup of switches configuration to restore it at the end of the */ /* calibration. */ tmp_OpaxSwitchesContextBackup = READ_BIT(OPAMP->CSR, __OPAMP_CSR_ALL_SWITCHES(hopamp)); /* Open all switches on non-inverting input, inverting input and output */ /* feedback. */ CLEAR_BIT(OPAMP->CSR, __OPAMP_CSR_ALL_SWITCHES(hopamp)); /* Set calibration mode to user programmed trimming values */ SET_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER); /* Select trimming settings depending on power mode */ if (hopamp->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { tmp_opamp_otr_otuser = OPAMP_OTR_OT_USER; tmp_opamp_reg_trimming = &OPAMP->OTR; } else { tmp_opamp_otr_otuser = 0x00000000; tmp_opamp_reg_trimming = &OPAMP->LPOTR; } /* Enable the selected opamp */ CLEAR_BIT (OPAMP->CSR, __OPAMP_CSR_OPAXPD(hopamp)); /* Perform trimming for both differential transistors pair high and low */ for (trimming_diff_pair_iteration_count = 0; trimming_diff_pair_iteration_count <=1; trimming_diff_pair_iteration_count++) { if (trimming_diff_pair_iteration_count == 0) { /* Calibration of transistors differential pair high (NMOS) */ trimming_diff_pair = OPAMP_FACTORYTRIMMING_N; opamp_trimmingvalue = &opamp_trimmingvaluen; /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value */ /* is 00000b. Used to detect the bit toggling during trimming. */ tmp_Opaxcalout_DefaultSate = RESET; /* Enable calibration for N differential pair */ MODIFY_REG(OPAMP->CSR, __OPAMP_CSR_OPAXCAL_L(hopamp), __OPAMP_CSR_OPAXCAL_H(hopamp) ); } else /* (trimming_diff_pair_iteration_count == 1) */ { /* Calibration of transistors differential pair low (PMOS) */ trimming_diff_pair = OPAMP_FACTORYTRIMMING_P; opamp_trimmingvalue = &opamp_trimmingvaluep; /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value */ /* is 00000b. Used to detect the bit toggling during trimming. */ tmp_Opaxcalout_DefaultSate = __OPAMP_CSR_OPAXCALOUT(hopamp); /* Enable calibration for P differential pair */ MODIFY_REG(OPAMP->CSR, __OPAMP_CSR_OPAXCAL_H(hopamp), __OPAMP_CSR_OPAXCAL_L(hopamp) ); } /* Perform calibration parameter search by dichotomy sweep */ /* - Delta initial value 16: for 5 dichotomy steps: 16 for the */ /* initial range, then successive delta sweeps (8, 4, 2, 1). */ /* can extend the search range to +/- 15 units. */ /* - Trimming initial value 15: search range will go from 0 to 30 */ /* (Trimming value 31 is forbidden). */ *opamp_trimmingvalue = 15; delta = 16; while (delta != 0) { /* Set candidate trimming */ MODIFY_REG(*tmp_opamp_reg_trimming, __OPAMP_OFFSET_TRIM_SET(hopamp, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) , __OPAMP_OFFSET_TRIM_SET(hopamp, trimming_diff_pair, *opamp_trimmingvalue) | tmp_opamp_otr_otuser); /* Offset trimming time: during calibration, minimum time needed */ /* between two steps to have 1 mV accuracy. */ HAL_Delay(OPAMP_TRIMMING_DELAY); /* Divide range by 2 to continue dichotomy sweep */ delta >>= 1; /* Set trimming values for next iteration in function of trimming */ /* result toggle (versus initial state). */ if (READ_BIT(OPAMP->CSR, __OPAMP_CSR_OPAXCALOUT(hopamp)) != tmp_Opaxcalout_DefaultSate) { /* If calibration output is has toggled, try lower trimming */ *opamp_trimmingvalue -= delta; } else { /* If calibration output is has not toggled, try higher trimming */ *opamp_trimmingvalue += delta; } } } /* Disable calibration for P and N differential pairs */ /* Disable the selected opamp */ CLEAR_BIT (OPAMP->CSR, (__OPAMP_CSR_OPAXCAL_H(hopamp) | __OPAMP_CSR_OPAXCAL_L(hopamp) | __OPAMP_CSR_OPAXPD(hopamp)) ); /* Backup of switches configuration to restore it at the end of the */ /* calibration. */ SET_BIT(OPAMP->CSR, tmp_OpaxSwitchesContextBackup); /* Self calibration is successful */ /* Store calibration (user trimming) results in init structure. */ /* Set user trimming mode */ hopamp->Init.UserTrimming = OPAMP_TRIMMING_USER; /* Affect calibration parameters depending on mode normal/low power */ if (hopamp->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* Write calibration result N */ hopamp->Init.TrimmingValueN = opamp_trimmingvaluen; /* Write calibration result P */ hopamp->Init.TrimmingValueP = opamp_trimmingvaluep; } else { /* Write calibration result N */ hopamp->Init.TrimmingValueNLowPower = opamp_trimmingvaluen; /* Write calibration result P */ hopamp->Init.TrimmingValuePLowPower = opamp_trimmingvaluep; } /* Update OPAMP state */ hopamp->State = HAL_OPAMP_STATE_READY; } else {
HAL_StatusTypeDef HAL_OPAMP_SelfCalibrate(OPAMP_HandleTypeDef *hopamp) { HAL_StatusTypeDef status = HAL_OK; uint32_t trimmingvaluen = 0; uint32_t trimmingvaluep = 0; uint32_t delta; uint32_t opampmode; __IO uint32_t* tmp_opamp_reg_trimming; /* Selection of register of trimming depending on power mode: OTR or LPOTR */ /* 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)); assert_param(IS_OPAMP_POWERMODE(hopamp->Init.PowerMode)); /* Save OPAMP mode as in */ /* STM32L471xx STM32L475xx STM32L476xx STM32L485xx STM32L486xx */ /* the calibration is not working in PGA mode */ opampmode = READ_BIT(hopamp->Instance->CSR,OPAMP_CSR_OPAMODE); /* Use of standalone mode */ MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_OPAMODE, OPAMP_STANDALONE_MODE); /* user trimming values are used for offset calibration */ SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_USERTRIM); /* Select trimming settings depending on power mode */ if (hopamp->Init.PowerMode == OPAMP_POWERMODE_NORMAL) { tmp_opamp_reg_trimming = &hopamp->Instance->OTR; } else { tmp_opamp_reg_trimming = &hopamp->Instance->LPOTR; } /* Enable calibration */ SET_BIT (hopamp->Instance->CSR, OPAMP_CSR_CALON); /* 1st calibration - N */ CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_CALSEL); /* 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 */ /* OPAMP_POWERMODE_NORMAL */ MODIFY_REG(*tmp_opamp_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen); /* 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(hopamp->Instance->CSR, OPAMP_CSR_CALOUT) != RESET) { /* OPAMP_CSR_CALOUT is HIGH try higher trimming */ trimmingvaluen -= delta; } else { /* OPAMP_CSR_CALOUT is LOW try lower trimming */ trimmingvaluen += delta; } /* Divide range by 2 to continue dichotomy sweep */ delta >>= 1; } /* 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_opamp_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen); /* 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(hopamp->Instance->CSR, OPAMP_CSR_CALOUT)) == 0) { /* Trimming value is actually one value more */ trimmingvaluen++; /* Set right trimming */ MODIFY_REG(*tmp_opamp_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen); } /* 2nd calibration - P */ SET_BIT (hopamp->Instance->CSR, OPAMP_CSR_CALSEL); /* Init trimming counter */ /* Medium value */ trimmingvaluep = 16; delta = 8; while (delta != 0) { /* Set candidate trimming */ /* OPAMP_POWERMODE_NORMAL */ MODIFY_REG(*tmp_opamp_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep<<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(hopamp->Instance->CSR, OPAMP_CSR_CALOUT) != RESET) { /* OPAMP_CSR_CALOUT is HIGH try higher trimming */ trimmingvaluep += delta; } else { /* OPAMP_CSR_CALOUT is LOW try lower trimming */ trimmingvaluep -= delta; } /* Divide range by 2 to continue dichotomy sweep */ delta >>= 1; } /* 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_opamp_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep<<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(hopamp->Instance->CSR, OPAMP_CSR_CALOUT) != RESET) { /* Trimming value is actually one value more */ trimmingvaluep++; MODIFY_REG(*tmp_opamp_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep<<OPAMP_INPUT_NONINVERTING)); } /* Disable the OPAMP */ CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN); /* Disable calibration & set normal mode (operating mode) */ CLEAR_BIT (hopamp->Instance->CSR, OPAMP_CSR_CALON); /* Self calibration is successful */ /* Store calibration(user trimming) results in init structure. */ /* Set user trimming mode */ hopamp->Init.UserTrimming = OPAMP_TRIMMING_USER; /* Affect calibration parameters depending on mode normal/low power */ if (hopamp->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER) { /* Write calibration result N */ hopamp->Init.TrimmingValueN = trimmingvaluen; /* Write calibration result P */ hopamp->Init.TrimmingValueP = trimmingvaluep; } else { /* Write calibration result N */ hopamp->Init.TrimmingValueNLowPower = trimmingvaluen; /* Write calibration result P */ hopamp->Init.TrimmingValuePLowPower = trimmingvaluep; } /* Restore OPAMP mode after calibration */ MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_OPAMODE, opampmode); } else {