aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c')
-rw-r--r--os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c473
1 files changed, 452 insertions, 21 deletions
diff --git a/os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c b/os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c
index 7251002..db988aa 100644
--- a/os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c
+++ b/os/hal/ports/STM32/LLD/OPAMPv1/hal_opamp_lld.c
@@ -1,6 +1,7 @@
/*
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.
@@ -98,29 +99,29 @@ void opamp_lld_init(void) {
#if STM32_OPAMP_USE_OPAMP1
/* Driver initialization.*/
opampObjectInit(&OPAMPD1);
- OPAMPD1.reg = OPAMP;
- OPAMPD1.reg->CSR = 0;
+ OPAMPD1.opamp = OPAMP;
+ OPAMPD1.opamp->CSR = 0;
#endif
#if STM32_OPAMP_USE_OPAMP2
/* Driver initialization.*/
opampObjectInit(&OPAMPD2);
- OPAMPD2.reg = OPAMP2;
- OPAMPD2.reg->CSR = 0;
+ OPAMPD2.opamp = OPAMP2;
+ OPAMPD2.opamp->CSR = 0;
#endif
#if STM32_OPAMP_USE_OPAMP3
/* Driver initialization.*/
opampObjectInit(&OPAMPD3);
- OPAMPD3.reg = OPAMP3;
- OPAMPD3.reg->CSR = 0;
+ OPAMPD3.opamp = OPAMP3;
+ OPAMPD3.opamp->CSR = 0;
#endif
#if STM32_OPAMP_USE_OPAMP4
/* Driver initialization.*/
opampObjectInit(&OPAMPD4);
- OPAMPD4.reg = OPAMP4;
- OPAMPD4.reg->CSR = 0;
+ OPAMPD4.opamp = OPAMP4;
+ OPAMPD4.opamp->CSR = 0;
#endif
}
@@ -128,29 +129,29 @@ void opamp_lld_init(void) {
/**
* @brief Configures and activates the OPAMP peripheral.
*
- * @param[in] compp pointer to the @p OPAMPDriver object
+ * @param[in] opampp pointer to the @p OPAMPDriver object
*
* @notapi
*/
-void opamp_lld_start(OPAMPDriver *compp) {
+void opamp_lld_start(OPAMPDriver *opampp) {
// Apply CSR Execpt the enable bit.
- compp->reg->CSR = compp->config->csr & ~OPAMP_CSR_OPAMPxEN;
+ opampp->opamp->CSR = opampp->config->csr & ~OPAMP_CSR_OPAMPxEN;
}
/**
* @brief Deactivates the comp peripheral.
*
- * @param[in] compp pointer to the @p OPAMPDriver object
+ * @param[in] opampp pointer to the @p OPAMPDriver object
*
* @notapi
*/
-void opamp_lld_stop(OPAMPDriver *compp) {
+void opamp_lld_stop(OPAMPDriver *opampp) {
- if (compp->state == OPAMP_READY) {
+ if (opampp->state == OPAMP_ACTIVE) {
- compp->reg->CSR = 0;
+ opampp->opamp->CSR = 0;
}
}
@@ -158,27 +159,457 @@ void opamp_lld_stop(OPAMPDriver *compp) {
/**
* @brief Enables the output.
*
- * @param[in] compp pointer to the @p OPAMPDriver object
+ * @param[in] opampp pointer to the @p OPAMPDriver object
*
* @notapi
*/
-void opamp_lld_enable(OPAMPDriver *compp) {
+void opamp_lld_enable(OPAMPDriver *opampp) {
- compp->reg->CSR |= OPAMP_CSR_OPAMPxEN; /* Enable */
+ opampp->opamp->CSR |= OPAMP_CSR_OPAMPxEN; /* Enable */
}
/**
* @brief Disables the output.
*
- * @param[in] compp pointer to the @p OPAMPDriver object
+ * @param[in] opampp pointer to the @p OPAMPDriver object
*
* @notapi
*/
-void opamp_lld_disable(OPAMPDriver *compp) {
+void opamp_lld_disable(OPAMPDriver *opampp) {
- compp->reg->CSR &= ~OPAMP_CSR_OPAMPxEN; /* Disable */
+ 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<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP2
+ MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen2<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP3
+ MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen3<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP4
+ MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen4<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+
+ /* 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 */
+ chSysPolledDelayX(MS2RTC(STM32_SYSCLK, 2));
+
+#if STM32_OPAMP_USE_OPAMP1
+ if (OPAMPD1.opamp->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<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP2
+ MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen2<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP3
+ MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen3<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP4
+ MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen4<<OPAMP_CSR_TRIMOFFSETN_Pos);
+#endif
+
+ /* 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 */
+ chSysPolledDelayX(MS2RTC(STM32_SYSCLK, 2));
+
+#if STM32_OPAMP_USE_OPAMP1
+ if (OPAMPD1.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
+ trimmingvaluen1 += (trimmingvaluen1 != 31);
+ MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen1<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP2
+ if (OPAMPD2.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ trimmingvaluen2 += (trimmingvaluen2 != 31);
+ MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen2<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP3
+ if (OPAMPD3.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ trimmingvaluen3 += (trimmingvaluen3 != 31);
+ MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen3<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP4
+ if (OPAMPD4.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ trimmingvaluen4 += (trimmingvaluen4 != 31);
+ MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETN,
+ trimmingvaluen4<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ }
+#endif
+
+ /* 2nd calibration - P */
+ /* Select 10U% VREF */
+#if STM32_OPAMP_USE_OPAMP1
+ MODIFY_REG(OPAMPD1.opamp->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<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP2
+ MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep2<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP3
+ MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep3<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP4
+ MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep4<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+
+
+ /* 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 */
+ chSysPolledDelayX(MS2RTC(STM32_SYSCLK, 2));
+#if STM32_OPAMP_USE_OPAMP1
+ if (OPAMPD1.opamp->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<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP2
+ MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep2<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP3
+ MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep3<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+#if STM32_OPAMP_USE_OPAMP4
+ MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep4<<OPAMP_CSR_TRIMOFFSETP_Pos);
+#endif
+
+ /* 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 */
+ chSysPolledDelayX(MS2RTC(STM32_SYSCLK, 2));
+
+#if STM32_OPAMP_USE_OPAMP1
+ if (OPAMPD1.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
+ trimmingvaluep1 += (trimmingvaluep1 != 31);
+ MODIFY_REG(OPAMPD1.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep1<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP2
+ if (OPAMPD2.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ trimmingvaluep2 += (trimmingvaluep2 != 31);
+ MODIFY_REG(OPAMPD2.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep2<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP3
+ if (OPAMPD3.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ trimmingvaluep3 += (trimmingvaluep3 != 31);
+ MODIFY_REG(OPAMPD3.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep3<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP4
+ if (OPAMPD4.opamp->CSR & OPAMP_CSR_OUTCAL) {
+ trimmingvaluep4 += (trimmingvaluep4 != 31);
+ MODIFY_REG(OPAMPD4.opamp->CSR, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep4<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ }
+#endif
+
+#if STM32_OPAMP_USE_OPAMP1
+#define CSRm OPAMPD1.opamp->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<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep1<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ OPAMPD1.state = OPAMP_STOP;
+#undef CSRm
+#endif
+
+#if STM32_OPAMP_USE_OPAMP2
+#define CSRm OPAMPD2.opamp->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<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep2<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ OPAMPD2.state = OPAMP_STOP;
+#undef CSRm
+#endif
+
+#if STM32_OPAMP_USE_OPAMP3
+#define CSRm OPAMPD3.opamp->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<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep3<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ OPAMPD3.state = OPAMP_STOP;
+#undef CSRm
+#endif
+
+#if STM32_OPAMP_USE_OPAMP4
+#define CSRm OPAMPD4.opamp->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<<OPAMP_CSR_TRIMOFFSETN_Pos);
+ MODIFY_REG(CSRm, OPAMP_CSR_TRIMOFFSETP,
+ trimmingvaluep4<<OPAMP_CSR_TRIMOFFSETP_Pos);
+ OPAMPD4.state = OPAMP_STOP;
+#undef CSRm
+#endif
+}
+
+void opamp_lld_calibrate(void)
+{
+ uint8_t trim_n[4] = {255};
+ uint8_t trim_p[4] = {255};
+ bool done;
+ do {
+ done = true;
+ opamp_lld_calibrate_once();
+#if STM32_OPAMP_USE_OPAMP1
+ done = done && (OPAMPD1.trim_n == trim_n[0]) && (OPAMPD1.trim_p == trim_p[0]);
+ trim_n[0] = OPAMPD1.trim_n;
+ trim_p[0] = OPAMPD1.trim_p;
+#endif
+#if STM32_OPAMP_USE_OPAMP2
+ done = done && (OPAMPD2.trim_n == trim_n[1]) && (OPAMPD2.trim_p == trim_p[1]);
+ trim_n[1] = OPAMPD2.trim_n;
+ trim_p[1] = OPAMPD2.trim_p;
+#endif
+#if STM32_OPAMP_USE_OPAMP3
+ done = done && (OPAMPD3.trim_n == trim_n[2]) && (OPAMPD3.trim_p == trim_p[2]);
+ trim_n[2] = OPAMPD3.trim_n;
+ trim_p[2] = OPAMPD3.trim_p;
+#endif
+#if STM32_OPAMP_USE_OPAMP4
+ done = done && (OPAMPD4.trim_n == trim_n[3]) && (OPAMPD4.trim_p == trim_p[3]);
+ trim_n[3] = OPAMPD4.trim_n;
+ trim_p[3] = OPAMPD4.trim_p;
+#endif
+ } while (!done);
+
+}
+#endif // STM32_OPAMP_USER_TRIM_ENABLED
#endif /* HAL_USE_OPAMP */
/** @} */