/* ChibiOS - Copyright (C) 2006..2019 Giovanni Di Sirio Copyright (C) 2019 Fabien Poussin (fabien.poussin (at) google's mail) Copyright (C) 2019 Alexandre Bustico Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * @file STM32/hal_opamp_lld.c * @brief STM32 Operational Amplifier subsystem low level driver header. * * @addtogroup OPAMP * @{ */ #include "hal.h" #if HAL_USE_OPAMP || defined(__DOXYGEN__) #include "hal_opamp.h" /*===========================================================================*/ /* Driver local definitions. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ /** * @brief OPAMPD1 driver identifier. * @note The driver OPAMPD1 allocates the comparator OPAMP1 when enabled. */ #if STM32_OPAMP_USE_OPAMP1 || defined(__DOXYGEN__) OPAMPDriver OPAMPD1; #endif /** * @brief OPAMPD2 driver identifier. * @note The driver OPAMPD2 allocates the comparator OPAMP2 when enabled. */ #if STM32_OPAMP_USE_OPAMP2 || defined(__DOXYGEN__) OPAMPDriver OPAMPD2; #endif /** * @brief OPAMPD3 driver identifier. * @note The driver OPAMPD3 allocates the comparator OPAMP3 when enabled. */ #if STM32_OPAMP_USE_OPAMP3 || defined(__DOXYGEN__) OPAMPDriver OPAMPD3; #endif /** * @brief OPAMPD4 driver identifier. * @note The driver OPAMPD4 allocates the comparator OPAMP4 when enabled. */ #if STM32_OPAMP_USE_OPAMP4 || defined(__DOXYGEN__) OPAMPDriver OPAMPD4; #endif /*===========================================================================*/ /* Driver local variables and types. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ /** * @brief Low level OPAMP driver initialization. * * @notapi */ void opamp_lld_init(void) { #if STM32_OPAMP_USE_OPAMP1 /* Driver initialization.*/ opampObjectInit(&OPAMPD1); OPAMPD1.opamp = OPAMP; OPAMPD1.opamp->CSR = 0; #endif #if STM32_OPAMP_USE_OPAMP2 /* Driver initialization.*/ opampObjectInit(&OPAMPD2); OPAMPD2.opamp = OPAMP2; OPAMPD2.opamp->CSR = 0; #endif #if STM32_OPAMP_USE_OPAMP3 /* Driver initialization.*/ opampObjectInit(&OPAMPD3); OPAMPD3.opamp = OPAMP3; OPAMPD3.opamp->CSR = 0; #endif #if STM32_OPAMP_USE_OPAMP4 /* Driver initialization.*/ opampObjectInit(&OPAMPD4); OPAMPD4.opamp = OPAMP4; OPAMPD4.opamp->CSR = 0; #endif } /** * @brief Configures and activates the OPAMP peripheral. * * @param[in] opampp pointer to the @p OPAMPDriver object * * @notapi */ void opamp_lld_start(OPAMPDriver *opampp) { // Apply CSR Execpt the enable bit. opampp->opamp->CSR = opampp->config->csr & ~OPAMP_CSR_OPAMPxEN; } /** * @brief Deactivates the comp peripheral. * * @param[in] opampp pointer to the @p OPAMPDriver object * * @notapi */ void opamp_lld_stop(OPAMPDriver *opampp) { if (opampp->state == OPAMP_ACTIVE) { opampp->opamp->CSR = 0; } } /** * @brief Enables the output. * * @param[in] opampp pointer to the @p OPAMPDriver object * * @notapi */ void opamp_lld_enable(OPAMPDriver *opampp) { opampp->opamp->CSR |= OPAMP_CSR_OPAMPxEN; /* Enable */ } /** * @brief Disables the output. * * @param[in] opampp pointer to the @p OPAMPDriver object * * @notapi */ void opamp_lld_disable(OPAMPDriver *opampp) { opampp->opamp->CSR &= ~OPAMP_CSR_OPAMPxEN; /* Disable */ } #if STM32_OPAMP_USER_TRIM_ENABLED void opamp_lld_calibrate_once(void) { #if STM32_OPAMP_USE_OPAMP1 uint32_t trimmingvaluen1 = 16U; uint32_t trimmingvaluep1 = 16U; OPAMPD1.state = OPAMP_CALIBRATING; #define CSRm OPAMPD1.opamp->CSR /* Set Calibration mode */ /* Non-inverting input connected to calibration reference voltage. */ CSRm |= OPAMP_CSR_FORCEVP; /* user trimming values are used for offset calibration */ CSRm |= OPAMP_CSR_USERTRIM; /* Enable calibration */ CSRm |= OPAMP_CSR_CALON; /* 1st calibration - N Select 90U% VREF */ MODIFY_REG(CSRm, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_90P); /* Enable the opamps */ CSRm |= OPAMP_CSR_OPAMPxEN; #undef CSRm #endif #if STM32_OPAMP_USE_OPAMP2 uint32_t trimmingvaluen2 = 16U; uint32_t trimmingvaluep2 = 16U; OPAMPD2.state = OPAMP_CALIBRATING; #define CSRm OPAMPD2.opamp->CSR CSRm |= OPAMP_CSR_FORCEVP; CSRm |= OPAMP_CSR_USERTRIM; CSRm |= OPAMP_CSR_CALON; MODIFY_REG(CSRm, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_90P); CSRm |= OPAMP_CSR_OPAMPxEN; #undef CSRm #endif #if STM32_OPAMP_USE_OPAMP3 uint32_t trimmingvaluen3 = 16U; uint32_t trimmingvaluep3 = 16U; OPAMPD3.state = OPAMP_CALIBRATING; #define CSRm OPAMPD3.opamp->CSR CSRm |= OPAMP_CSR_FORCEVP; CSRm |= OPAMP_CSR_USERTRIM; CSRm |= OPAMP_CSR_CALON; MODIFY_REG(CSRm, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_90P); CSRm |= OPAMP_CSR_OPAMPxEN; #undef CSRm #endif #if STM32_OPAMP_USE_OPAMP4 uint32_t trimmingvaluen4 = 16U; uint32_t trimmingvaluep4 = 16U; OPAMPD4.state = OPAMP_CALIBRATING; #define CSRm OPAMPD4.opamp->CSR CSRm |= OPAMP_CSR_FORCEVP; CSRm |= OPAMP_CSR_USERTRIM; CSRm |= OPAMP_CSR_CALON; MODIFY_REG(CSRm, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_90P); CSRm |= OPAMP_CSR_OPAMPxEN; #undef CSRm #endif chSysPolledDelayX(MS2RTC(STM32_SYSCLK, 20)); uint32_t delta = 8U; while (delta != 0U) { #if STM32_OPAMP_USE_OPAMP1 MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1<CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2<CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3<CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4<CSR & OPAMP_CSR_OUTCAL) { /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */ trimmingvaluen1 += delta; } else { /* OPAMP_CSR_OUTCAL is LOW try lower trimming */ trimmingvaluen1 -= delta; } #endif #if STM32_OPAMP_USE_OPAMP2 if (OPAMPD2.opamp->CSR & OPAMP_CSR_OUTCAL) { trimmingvaluen2 += delta; } else { trimmingvaluen2 -= delta; } #endif #if STM32_OPAMP_USE_OPAMP3 if (OPAMPD3.opamp->CSR & OPAMP_CSR_OUTCAL) { trimmingvaluen3 += delta; } else { trimmingvaluen3 -= delta; } #endif #if STM32_OPAMP_USE_OPAMP4 if (OPAMPD4.opamp->CSR & OPAMP_CSR_OUTCAL) { trimmingvaluen4 += delta; } else { trimmingvaluen4 -= delta; } #endif delta >>= 1U; } /* 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 0U */ #if STM32_OPAMP_USE_OPAMP1 MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1<CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2<CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3<CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4<CSR & OPAMP_CSR_OUTCAL) { /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */ trimmingvaluen1 += (trimmingvaluen1 != 31); MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1<CSR & OPAMP_CSR_OUTCAL) { trimmingvaluen2 += (trimmingvaluen2 != 31); MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2<CSR & OPAMP_CSR_OUTCAL) { trimmingvaluen3 += (trimmingvaluen3 != 31); MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3<CSR & OPAMP_CSR_OUTCAL) { trimmingvaluen4 += (trimmingvaluen4 != 31); MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4<CSR, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_10P); #endif #if STM32_OPAMP_USE_OPAMP2 MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_10P); #endif #if STM32_OPAMP_USE_OPAMP3 MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_10P); #endif #if STM32_OPAMP_USE_OPAMP4 MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_CALSEL, OPAMPx_CSR_CALSEL_10P); #endif delta = 8U; while (delta != 0U) { #if STM32_OPAMP_USE_OPAMP1 MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1<CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2<CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3<CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4<CSR & OPAMP_CSR_OUTCAL) { /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */ trimmingvaluep1 += delta; } else { /* OPAMP_CSR_OUTCAL is LOW try lower trimming */ trimmingvaluep1 -= delta; } #endif #if STM32_OPAMP_USE_OPAMP2 if (OPAMPD2.opamp->CSR & OPAMP_CSR_OUTCAL) { trimmingvaluep2 += delta; } else { trimmingvaluep2 -= delta; } #endif #if STM32_OPAMP_USE_OPAMP3 if (OPAMPD3.opamp->CSR & OPAMP_CSR_OUTCAL) { trimmingvaluep3 += delta; } else { trimmingvaluep3 -= delta; } #endif #if STM32_OPAMP_USE_OPAMP4 if (OPAMPD4.opamp->CSR & OPAMP_CSR_OUTCAL) { trimmingvaluep4 += delta; } else { trimmingvaluep4 -= delta; } #endif delta >>= 1U; } /* 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 0U */ #if STM32_OPAMP_USE_OPAMP1 MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1<CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2<CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3<CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4<CSR & OPAMP_CSR_OUTCAL) { /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */ trimmingvaluep1 += (trimmingvaluep1 != 31); MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1<CSR & OPAMP_CSR_OUTCAL) { trimmingvaluep2 += (trimmingvaluep2 != 31); MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2<CSR & OPAMP_CSR_OUTCAL) { trimmingvaluep3 += (trimmingvaluep3 != 31); MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3<CSR & OPAMP_CSR_OUTCAL) { trimmingvaluep4 += (trimmingvaluep4 != 31); MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4<CSR /* Disable calibration */ CSRm &= ~OPAMP_CSR_CALON; /* Disable the OPAMPs */ CSRm &= ~OPAMP_CSR_OPAMPxEN; /* Set normal operating mode back */ CSRm &= ~OPAMP_CSR_FORCEVP; /* Write calibration result N */ OPAMPD1.trim_n = trimmingvaluen1; /* Write calibration result P */ OPAMPD1.trim_p = trimmingvaluep1; MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1<CSR /* Disable calibration */ CSRm &= ~OPAMP_CSR_CALON; /* Disable the OPAMPs */ CSRm &= ~OPAMP_CSR_OPAMPxEN; /* Set normal operating mode back */ CSRm &= ~OPAMP_CSR_FORCEVP; /* Write calibration result N */ OPAMPD2.trim_n = trimmingvaluen2; /* Write calibration result P */ OPAMPD2.trim_p = trimmingvaluep2; MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2<CSR /* Disable calibration */ CSRm &= ~OPAMP_CSR_CALON; /* Disable the OPAMPs */ CSRm &= ~OPAMP_CSR_OPAMPxEN; /* Set normal operating mode back */ CSRm &= ~OPAMP_CSR_FORCEVP; /* Write calibration result N */ OPAMPD3.trim_n = trimmingvaluen3; /* Write calibration result P */ OPAMPD3.trim_p = trimmingvaluep3; MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3<CSR /* Disable calibration */ CSRm &= ~OPAMP_CSR_CALON; /* Disable the OPAMPs */ CSRm &= ~OPAMP_CSR_OPAMPxEN; /* Set normal operating mode back */ CSRm &= ~OPAMP_CSR_FORCEVP; /* Write calibration result N */ OPAMPD4.trim_n = trimmingvaluen4; /* Write calibration result P */ OPAMPD4.trim_p = trimmingvaluep4; MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4<