1 /**
2   ******************************************************************************
3   * @file    stm32f3xx_hal_opamp_ex.c
4   * @author  MCD Application Team
5   * @brief   Extended OPAMP HAL module driver.
6   *
7   *          This file provides firmware functions to manage the following
8   *          functionalities of the operational amplifier(s) peripheral:
9   *           + Extended Initialization and de-initialization  functions
10   *           + Extended Peripheral Control  functions
11   *
12   @verbatim
13   ******************************************************************************
14   * @attention
15   *
16   * Copyright (c) 2016 STMicroelectronics.
17   * All rights reserved.
18   *
19   * This software is licensed under terms that can be found in the LICENSE file
20   * in the root directory of this software component.
21   * If no LICENSE file comes with this software, it is provided AS-IS.
22   *
23   ******************************************************************************
24   */
25 
26 /* Includes ------------------------------------------------------------------*/
27 #include "stm32f3xx_hal.h"
28 
29 /** @addtogroup STM32F3xx_HAL_Driver
30   * @{
31   */
32 
33 #ifdef HAL_OPAMP_MODULE_ENABLED
34 
35 #if defined (OPAMP1) || defined (OPAMP2) || defined (OPAMP3) || defined (OPAMP4)
36 
37 /** @defgroup OPAMPEx OPAMPEx
38   * @brief OPAMP Extended HAL module driver.
39   * @{
40   */
41 
42 
43 /* Private typedef -----------------------------------------------------------*/
44 /* Private define ------------------------------------------------------------*/
45 /* Private macro -------------------------------------------------------------*/
46 /* Private variables ---------------------------------------------------------*/
47 /* Private function prototypes -----------------------------------------------*/
48 /* Exported functions ---------------------------------------------------------*/
49 
50 /** @defgroup OPAMPEx_Exported_Functions OPAMP Extended Exported Functions
51   * @{
52   */
53 
54 
55 /** @defgroup OPAMPEx_Exported_Functions_Group1 Extended Input and Output operation functions
56   * @brief    Extended Self calibration functions
57   *
58 @verbatim
59  ===============================================================================
60               ##### Extended IO operation functions #####
61  ===============================================================================
62   [..]
63 
64 @endverbatim
65   * @{
66   */
67 
68 #if defined(STM32F302xE) || \
69     defined(STM32F302xC)
70 /*  2 OPAMPS available */
71 /*  2 OPAMPS can be calibrated in parallel */
72 
73 /**
74   * @brief  Run the self calibration of 2 OPAMPs in parallel.
75   * @param  hopamp1 handle
76   * @param  hopamp2 handle
77   * @retval HAL status
78   * @note   Updated offset trimming values (PMOS & NMOS), user trimming is enabled
79   * @note   Calibration runs about 25 ms.
80   */
81 
HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef * hopamp1,OPAMP_HandleTypeDef * hopamp2)82 HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2)
83 {
84   HAL_StatusTypeDef status = HAL_OK;
85 
86   uint32_t trimmingvaluen1;
87   uint32_t trimmingvaluep1;
88   uint32_t trimmingvaluen2;
89   uint32_t trimmingvaluep2;
90 
91   uint32_t delta;
92 
93   if ((hopamp1 == NULL) || (hopamp2 == NULL))
94   {
95     status = HAL_ERROR;
96   }
97   /* Check if OPAMP in calibration mode and calibration not yet enable */
98   else if (hopamp1->State !=  HAL_OPAMP_STATE_READY)
99   {
100     status = HAL_ERROR;
101   }
102   else if (hopamp2->State != HAL_OPAMP_STATE_READY)
103   {
104     status = HAL_ERROR;
105   }
106   else
107   {
108     /* Check the parameter */
109     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance));
110     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance));
111 
112     /* Set Calibration mode */
113     /* Non-inverting input connected to calibration reference voltage. */
114     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_FORCEVP);
115     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_FORCEVP);
116 
117     /*  user trimming values are used for offset calibration */
118     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_USERTRIM);
119     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_USERTRIM);
120 
121     /* Enable calibration */
122     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALON);
123     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALON);
124 
125     /* 1st calibration - N */
126     /* Select 90U% VREF */
127     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
128     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
129 
130     /* Enable the opamps */
131     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
132     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
133 
134     /* Init trimming counter */
135     /* Medium value */
136     trimmingvaluen1 = 16U;
137     trimmingvaluen2 = 16U;
138     delta = 8U;
139 
140     while (delta != 0U)
141     {
142       /* Set candidate trimming */
143       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
144       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
145 
146       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
147       /* Offset trim time: during calibration, minimum time needed between */
148       /* two steps to have 1 mV accuracy */
149       HAL_Delay(2U);
150 
151       if (hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL)
152       {
153         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
154         trimmingvaluen1 += delta;
155       }
156       else
157       {
158         /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
159         trimmingvaluen1 -= delta;
160       }
161 
162       if (hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL)
163       {
164         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
165         trimmingvaluen2 += delta;
166       }
167       else
168       {
169         /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
170         trimmingvaluen2 -= delta;
171       }
172 
173       delta >>= 1U;
174     }
175 
176     // Still need to check if right calibration is current value or un step below
177     // Indeed the first value that causes the OUTCAL bit to change from 1 to 0
178     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
179     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
180 
181     /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
182     /* Offset trim time: during calibration, minimum time needed between */
183     /* two steps to have 1 mV accuracy */
184     HAL_Delay(2U);
185 
186     if (hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL)
187     {
188       /* OPAMP_CSR_OUTCAL is actually one value more */
189       trimmingvaluen1++;
190       /* Set right trimming */
191       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
192     }
193 
194     if (hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL)
195     {
196       /* OPAMP_CSR_OUTCAL is actually one value more */
197       trimmingvaluen2++;
198       /* Set right trimming */
199       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
200     }
201 
202     /* 2nd calibration - P */
203     /* Select 10U% VREF */
204     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
205     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
206 
207     /* Init trimming counter */
208     /* Medium value */
209     trimmingvaluep1 = 16U;
210     trimmingvaluep2 = 16U;
211     delta = 8U;
212 
213     while (delta != 0U)
214     {
215       /* Set candidate trimming */
216       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
217       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
218 
219       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
220       /* Offset trim time: during calibration, minimum time needed between */
221       /* two steps to have 1 mV accuracy */
222       HAL_Delay(2U);
223 
224       if (hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL)
225       {
226         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
227         trimmingvaluep1 += delta;
228       }
229       else
230       {
231         trimmingvaluep1 -= delta;
232       }
233 
234       if (hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL)
235       {
236         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
237         trimmingvaluep2 += delta;
238       }
239       else
240       {
241         trimmingvaluep2 -= delta;
242       }
243 
244       delta >>= 1U;
245     }
246 
247     // Still need to check if right calibration is current value or un step below
248     // Indeed the first value that causes the OUTCAL bit to change from 1 to 0
249     /* Set candidate trimming */
250     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
251     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
252 
253     /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
254     /* Offset trim time: during calibration, minimum time needed between */
255     /* two steps to have 1 mV accuracy */
256     HAL_Delay(2U);
257 
258     if (hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL)
259     {
260       /* OPAMP_CSR_OUTCAL is actually one value more */
261       trimmingvaluep1++;
262       /* Set right trimming */
263       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
264     }
265 
266     if (hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL)
267     {
268       /* OPAMP_CSR_OUTCAL is actually one value more */
269       trimmingvaluep2++;
270       /* Set right trimming */
271       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
272     }
273 
274     /* Disable calibration */
275     CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALON);
276     CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALON);
277 
278     /* Disable the OPAMPs */
279     CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
280     CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
281 
282     /* Set operating mode back */
283     CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_FORCEVP);
284     CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_FORCEVP);
285 
286     /* Self calibration is successful  */
287     /* Store calibration(user trimming) results in init structure. */
288     /* Select user trimming mode */
289 
290     /* Write calibration result N */
291     hopamp1->Init.TrimmingValueN = trimmingvaluen1;
292     hopamp2->Init.TrimmingValueN = trimmingvaluen2;
293 
294     /* Write calibration result P */
295     hopamp1->Init.TrimmingValueP = trimmingvaluep1;
296     hopamp2->Init.TrimmingValueP = trimmingvaluep2;
297 
298     /* Calibration */
299     hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
300     hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
301 
302     /* Select user trimming mode */
303     /* And updated with calibrated settings */
304     hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
305     hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
306 
307     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
308     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
309 
310     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
311     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
312 
313   }
314   return status;
315 }
316 #endif /* STM32F302xE || */
317 	   /* STM32F302xC    */
318 
319 #if defined(STM32F303xE) || defined(STM32F398xx) || \
320     defined(STM32F303xC) || defined(STM32F358xx)
321 /*  4 OPAMPS available */
322 /*  4 OPAMPS can be calibrated in parallel */
323 
324 /**
325   * @brief  Run the self calibration of 4 OPAMPs in parallel.
326   * @param  hopamp1 handle
327   * @param  hopamp2 handle
328   * @param  hopamp3 handle
329   * @param  hopamp4 handle
330   * @retval HAL status
331   * @note   Updated offset trimming values (PMOS & NMOS), user trimming is enabled
332   * @note   Calibration runs about 25 ms.
333   */
334 
HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef * hopamp1,OPAMP_HandleTypeDef * hopamp2,OPAMP_HandleTypeDef * hopamp3,OPAMP_HandleTypeDef * hopamp4)335 HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2, OPAMP_HandleTypeDef *hopamp3, OPAMP_HandleTypeDef *hopamp4)
336 {
337   HAL_StatusTypeDef status = HAL_OK;
338 
339   uint32_t trimmingvaluen1;
340   uint32_t trimmingvaluep1;
341   uint32_t trimmingvaluen2;
342   uint32_t trimmingvaluep2;
343   uint32_t trimmingvaluen3;
344   uint32_t trimmingvaluep3;
345   uint32_t trimmingvaluen4;
346   uint32_t trimmingvaluep4;
347 
348   uint32_t delta;
349 
350   if ((hopamp1 == NULL) || (hopamp2 == NULL) || (hopamp3 == NULL) || (hopamp4 == NULL))
351   {
352     status = HAL_ERROR;
353   }
354   /* Check if OPAMP in calibration mode and calibration not yet enable */
355   else if (hopamp1->State !=  HAL_OPAMP_STATE_READY)
356   {
357     status = HAL_ERROR;
358   }
359   else if (hopamp2->State != HAL_OPAMP_STATE_READY)
360   {
361     status = HAL_ERROR;
362   }
363   else if (hopamp3->State != HAL_OPAMP_STATE_READY)
364   {
365     status = HAL_ERROR;
366   }
367   else if (hopamp3->State != HAL_OPAMP_STATE_READY)
368   {
369     status = HAL_ERROR;
370   }
371   else
372   {
373     /* Check the parameter */
374     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance));
375     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance));
376     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp3->Instance));
377     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp4->Instance));
378 
379     /* Set Calibration mode */
380     /* Non-inverting input connected to calibration reference voltage. */
381     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_FORCEVP);
382     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_FORCEVP);
383     SET_BIT(hopamp3->Instance->CSR, OPAMP_CSR_FORCEVP);
384     SET_BIT(hopamp4->Instance->CSR, OPAMP_CSR_FORCEVP);
385 
386     /*  user trimming values are used for offset calibration */
387     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_USERTRIM);
388     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_USERTRIM);
389     SET_BIT(hopamp3->Instance->CSR, OPAMP_CSR_USERTRIM);
390     SET_BIT(hopamp4->Instance->CSR, OPAMP_CSR_USERTRIM);
391 
392     /* Enable calibration */
393     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALON);
394     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALON);
395     SET_BIT(hopamp3->Instance->CSR, OPAMP_CSR_CALON);
396     SET_BIT(hopamp4->Instance->CSR, OPAMP_CSR_CALON);
397 
398     /* 1st calibration - N */
399     /* Select 90U% VREF */
400     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
401     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
402     MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
403     MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
404 
405     /* Enable the opamps */
406     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
407     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
408     SET_BIT(hopamp3->Instance->CSR, OPAMP_CSR_OPAMPxEN);
409     SET_BIT(hopamp4->Instance->CSR, OPAMP_CSR_OPAMPxEN);
410 
411     /* Init trimming counter */
412     /* Medium value */
413     trimmingvaluen1 = 16U;
414     trimmingvaluen2 = 16U;
415     trimmingvaluen3 = 16U;
416     trimmingvaluen4 = 16U;
417     delta = 8U;
418 
419     while (delta != 0U)
420     {
421       /* Set candidate trimming */
422       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
423       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
424       MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3 << OPAMP_INPUT_INVERTING);
425       MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4 << OPAMP_INPUT_INVERTING);
426 
427       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
428       /* Offset trim time: during calibration, minimum time needed between */
429       /* two steps to have 1 mV accuracy */
430       HAL_Delay(2U);
431 
432       if ((hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
433       {
434         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
435         trimmingvaluen1 += delta;
436       }
437       else
438       {
439         /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
440         trimmingvaluen1 -= delta;
441       }
442 
443       if ((hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
444       {
445         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
446         trimmingvaluen2 += delta;
447       }
448       else
449       {
450         /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
451         trimmingvaluen2 -= delta;
452       }
453 
454       if ((hopamp3->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
455       {
456         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
457         trimmingvaluen3 += delta;
458       }
459       else
460       {
461         /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
462         trimmingvaluen3 -= delta;
463       }
464 
465       if ((hopamp4->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
466       {
467         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
468         trimmingvaluen4 += delta;
469       }
470       else
471       {
472         /* OPAMP_CSR_OUTCAL is LOW try lower trimming */
473         trimmingvaluen4 -= delta;
474       }
475 
476       delta >>= 1U;
477     }
478 
479     /* Still need to check if right calibration is current value or un step below */
480     /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0U */
481     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
482     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
483     MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3 << OPAMP_INPUT_INVERTING);
484     MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4 << OPAMP_INPUT_INVERTING);
485 
486     /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
487     /* Offset trim time: during calibration, minimum time needed between */
488     /* two steps to have 1 mV accuracy */
489     HAL_Delay(2U);
490 
491     if ((hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
492     {
493       /* OPAMP_CSR_OUTCAL is actually one value more */
494       trimmingvaluen1++;
495       /* Set right trimming */
496       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
497     }
498 
499     if ((hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
500     {
501       /* OPAMP_CSR_OUTCAL is actually one value more */
502       trimmingvaluen2++;
503       /* Set right trimming */
504       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
505     }
506 
507     if ((hopamp3->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
508     {
509       /* OPAMP_CSR_OUTCAL is actually one value more */
510       trimmingvaluen3++;
511       /* Set right trimming */
512       MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3 << OPAMP_INPUT_INVERTING);
513     }
514 
515     if ((hopamp4->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
516     {
517       /* OPAMP_CSR_OUTCAL is actually one value more */
518       trimmingvaluen4++;
519       /* Set right trimming */
520       MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4 << OPAMP_INPUT_INVERTING);
521     }
522 
523     /* 2nd calibration - P */
524     /* Select 10U% VREF */
525     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
526     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
527     MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
528     MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
529 
530     /* Init trimming counter */
531     /* Medium value */
532     trimmingvaluep1 = 16U;
533     trimmingvaluep2 = 16U;
534     trimmingvaluep3 = 16U;
535     trimmingvaluep4 = 16U;
536 
537     delta = 8U;
538 
539     while (delta != 0U)
540     {
541       /* Set candidate trimming */
542       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
543       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
544       MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3 << OPAMP_INPUT_NONINVERTING);
545       MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4 << OPAMP_INPUT_NONINVERTING);
546 
547       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
548       /* Offset trim time: during calibration, minimum time needed between */
549       /* two steps to have 1 mV accuracy */
550       HAL_Delay(2U);
551 
552       if ((hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
553       {
554         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
555         trimmingvaluep1 += delta;
556       }
557       else
558       {
559         trimmingvaluep1 -= delta;
560       }
561 
562       if ((hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
563       {
564         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
565         trimmingvaluep2 += delta;
566       }
567       else
568       {
569         trimmingvaluep2 -= delta;
570       }
571 
572       if ((hopamp3->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
573       {
574         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
575         trimmingvaluep3 += delta;
576       }
577       else
578       {
579         trimmingvaluep3 -= delta;
580       }
581 
582       if ((hopamp4->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
583       {
584         /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
585         trimmingvaluep4 += delta;
586       }
587       else
588       {
589         trimmingvaluep4 -= delta;
590       }
591 
592       delta >>= 1U;
593     }
594 
595     /* Still need to check if right calibration is current value or un step below */
596     /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0U */
597     /* Set candidate trimming */
598     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
599     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
600     MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3 << OPAMP_INPUT_NONINVERTING);
601     MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4 << OPAMP_INPUT_NONINVERTING);
602 
603     /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
604     /* Offset trim time: during calibration, minimum time needed between */
605     /* two steps to have 1 mV accuracy */
606     HAL_Delay(2U);
607 
608     if ((hopamp1->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
609     {
610       /* Trimming value is actually one value more */
611       trimmingvaluep1++;
612       /* Set right trimming */
613       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
614     }
615 
616     if ((hopamp2->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
617     {
618       /* Trimming value is actually one value more */
619       trimmingvaluep2++;
620       /* Set right trimming */
621       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
622     }
623 
624     if ((hopamp3->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
625     {
626       /* Trimming value is actually one value more */
627       trimmingvaluep3++;
628       /* Set right trimming */
629       MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3 << OPAMP_INPUT_NONINVERTING);
630     }
631 
632     if ((hopamp4->Instance->CSR & OPAMP_CSR_OUTCAL) != RESET)
633     {
634       /* Trimming value is actually one value more */
635       trimmingvaluep4++;
636       /* Set right trimming */
637       MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4 << OPAMP_INPUT_NONINVERTING);
638     }
639 
640     /* Disable calibration */
641     CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALON);
642     CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALON);
643     CLEAR_BIT(hopamp3->Instance->CSR, OPAMP_CSR_CALON);
644     CLEAR_BIT(hopamp4->Instance->CSR, OPAMP_CSR_CALON);
645 
646     /* Disable the OPAMPs */
647     CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
648     CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
649     CLEAR_BIT(hopamp3->Instance->CSR, OPAMP_CSR_OPAMPxEN);
650     CLEAR_BIT(hopamp4->Instance->CSR, OPAMP_CSR_OPAMPxEN);
651 
652     /* Set normal operating mode back */
653     CLEAR_BIT(hopamp1->Instance->CSR, OPAMP_CSR_FORCEVP);
654     CLEAR_BIT(hopamp2->Instance->CSR, OPAMP_CSR_FORCEVP);
655     CLEAR_BIT(hopamp3->Instance->CSR, OPAMP_CSR_FORCEVP);
656     CLEAR_BIT(hopamp4->Instance->CSR, OPAMP_CSR_FORCEVP);
657 
658     /* Self calibration is successful  */
659     /* Store calibration(user trimming) results in init structure. */
660     /* Select user trimming mode */
661 
662     /* Write calibration result N */
663     hopamp1->Init.TrimmingValueN = trimmingvaluen1;
664     hopamp2->Init.TrimmingValueN = trimmingvaluen2;
665     hopamp3->Init.TrimmingValueN = trimmingvaluen3;
666     hopamp4->Init.TrimmingValueN = trimmingvaluen4;
667 
668     /* Write calibration result P */
669     hopamp1->Init.TrimmingValueP = trimmingvaluep1;
670     hopamp2->Init.TrimmingValueP = trimmingvaluep2;
671     hopamp3->Init.TrimmingValueP = trimmingvaluep3;
672     hopamp4->Init.TrimmingValueP = trimmingvaluep4;
673 
674     /* Select user trimming mode */
675     /* And updated with calibrated settings */
676     hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
677     hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
678     hopamp3->Init.UserTrimming = OPAMP_TRIMMING_USER;
679     hopamp4->Init.UserTrimming = OPAMP_TRIMMING_USER;
680 
681     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen1 << OPAMP_INPUT_INVERTING);
682     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen2 << OPAMP_INPUT_INVERTING);
683     MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen3 << OPAMP_INPUT_INVERTING);
684     MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen4 << OPAMP_INPUT_INVERTING);
685 
686     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep1 << OPAMP_INPUT_NONINVERTING);
687     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep2 << OPAMP_INPUT_NONINVERTING);
688     MODIFY_REG(hopamp3->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep3 << OPAMP_INPUT_NONINVERTING);
689     MODIFY_REG(hopamp4->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep4 << OPAMP_INPUT_NONINVERTING);
690   }
691   return status;
692 }
693 #endif /* STM32F303xE || STM32F398xx  || */
694        /* STM32F303xC || STM32F358xx     */
695 
696 /**
697   * @}
698   */
699 
700 /**
701   * @}
702   */
703 
704 /**
705   * @}
706   */
707 
708 #endif /* OPAMP1 || OPAMP2 || OPAMP3 || OPAMP4 */
709 
710 #endif /* HAL_OPAMP_MODULE_ENABLED */
711 
712 /**
713   * @}
714   */
715 
716