1 /**
2   ******************************************************************************
3   * @file    stm32l1xx_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   ******************************************************************************
13   * @attention
14   *
15   * Copyright (c) 2017 STMicroelectronics.
16   * All rights reserved.
17   *
18   * This software is licensed under terms that can be found in the LICENSE file
19   * in the root directory of this software component.
20   * If no LICENSE file comes with this software, it is provided AS-IS.
21   *
22   ******************************************************************************
23   */
24 
25 /* Includes ------------------------------------------------------------------*/
26 #include "stm32l1xx_hal.h"
27 
28 #ifdef HAL_OPAMP_MODULE_ENABLED
29 
30 #if defined (STM32L151xCA) || defined (STM32L151xD) || defined (STM32L152xCA) || defined (STM32L152xD) || defined (STM32L162xCA) || defined (STM32L162xD) || defined (STM32L151xE) || defined (STM32L151xDX) || defined (STM32L152xE) || defined (STM32L152xDX) || defined (STM32L162xE) || defined (STM32L162xDX) || defined (STM32L162xC) || defined (STM32L152xC) || defined (STM32L151xC)
31 
32 /** @addtogroup STM32L1xx_HAL_Driver
33   * @{
34   */
35 
36 /** @defgroup OPAMPEx OPAMPEx
37   * @brief OPAMP Extended HAL module driver.
38   * @{
39   */
40 
41 /* Private typedef -----------------------------------------------------------*/
42 /* Private define ------------------------------------------------------------*/
43 /* Private macro -------------------------------------------------------------*/
44 /* Private variables ---------------------------------------------------------*/
45 /* Private function prototypes -----------------------------------------------*/
46 /* Exported functions --------------------------------------------------------*/
47 
48 /** @addtogroup OPAMPEx_Exported_Functions OPAMPEx Exported Functions
49   * @{
50   */
51 
52 /** @addtogroup OPAMPEx_Exported_Functions_Group1
53   * @brief    Extended operation functions
54   *
55 @verbatim
56  ===============================================================================
57               ##### Extended IO operation functions #####
58  ===============================================================================
59   [..]
60       (+) OPAMP Self calibration.
61 
62 @endverbatim
63   * @{
64   */
65 
66 #if defined (STM32L151xD) || defined (STM32L152xD) || defined (STM32L162xD)
67 
68 /*  3 OPAMPS available */
69 /*  3 OPAMPS can be calibrated in parallel */
70 
71 /**
72   * @brief  Run the self calibration of the 3 OPAMPs in parallel.
73   * @note   Trimming values (PMOS & NMOS) are updated and user trimming is
74   *         enabled is calibration is successful.
75   * @note   Calibration is performed in the mode specified in OPAMP init
76   *         structure (mode normal or low-power). To perform calibration for
77   *         both modes, repeat this function twice after OPAMP init structure
78   *         accordingly updated.
79   * @note   Calibration runs about 10 ms (5 dichotmy steps, repeated for P
80   *         and N transistors: 10 steps with 1 ms for each step).
81   * @param  hopamp1 handle
82   * @param  hopamp2 handle
83   * @param  hopamp3 handle
84   * @retval HAL status
85   */
HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef * hopamp1,OPAMP_HandleTypeDef * hopamp2,OPAMP_HandleTypeDef * hopamp3)86 HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2, OPAMP_HandleTypeDef *hopamp3)
87 {
88   HAL_StatusTypeDef status = HAL_OK;
89 
90   uint32_t* opamp1_trimmingvalue;
91   uint32_t opamp1_trimmingvaluen = 0;
92   uint32_t opamp1_trimmingvaluep = 0;
93 
94   uint32_t* opamp2_trimmingvalue;
95   uint32_t opamp2_trimmingvaluen = 0;
96   uint32_t opamp2_trimmingvaluep = 0;
97 
98   uint32_t* opamp3_trimmingvalue;
99   uint32_t opamp3_trimmingvaluen = 0;
100   uint32_t opamp3_trimmingvaluep = 0;
101 
102   uint32_t trimming_diff_pair;              /* Selection of differential transistors pair high or low */
103 
104   __IO uint32_t* tmp_opamp1_reg_trimming;   /* Selection of register of trimming depending on power mode: OTR or LPOTR */
105   __IO uint32_t* tmp_opamp2_reg_trimming;
106   __IO uint32_t* tmp_opamp3_reg_trimming;
107   uint32_t tmp_opamp1_otr_otuser;           /* Selection of bit OPAMP_OTR_OT_USER depending on trimming register pointed: OTR or LPOTR */
108   uint32_t tmp_opamp2_otr_otuser;
109   uint32_t tmp_opamp3_otr_otuser;
110 
111   uint32_t tmp_Opa1calout_DefaultSate;      /* Bit OPAMP_CSR_OPA1CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */
112   uint32_t tmp_Opa2calout_DefaultSate;      /* Bit OPAMP_CSR_OPA2CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */
113   uint32_t tmp_Opa3calout_DefaultSate;      /* Bit OPAMP_CSR_OPA3CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */
114 
115   uint32_t tmp_OpaxSwitchesContextBackup = 0x0U;
116 
117   uint8_t trimming_diff_pair_iteration_count = 0x0U;    /* For calibration loop algorithm: to repeat the calibration loop for both differential transistors pair high and low */
118   uint8_t delta;                                        /* For calibration loop algorithm: Variable for dichotomy steps value */
119   uint8_t final_step_check = 0x0U;                      /* For calibration loop algorithm: Flag for additional check of last trimming step */
120 
121 
122   if((hopamp1 == NULL) || (hopamp2 == NULL) || (hopamp3 == NULL))
123   {
124     status = HAL_ERROR;
125   }
126   /* Check if OPAMP in calibration mode and calibration not yet enable */
127   else if(hopamp1->State !=  HAL_OPAMP_STATE_READY)
128   {
129     status = HAL_ERROR;
130   }
131   else if(hopamp2->State != HAL_OPAMP_STATE_READY)
132   {
133     status = HAL_ERROR;
134   }
135   else if(hopamp3->State != HAL_OPAMP_STATE_READY)
136   {
137     status = HAL_ERROR;
138   }
139 
140   else
141   {
142       /* Check the parameter */
143       assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance));
144       assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance));
145       assert_param(IS_OPAMP_ALL_INSTANCE(hopamp3->Instance));
146       assert_param(IS_OPAMP_POWERMODE(hopamp1->Init.PowerMode));
147       assert_param(IS_OPAMP_POWERMODE(hopamp2->Init.PowerMode));
148       assert_param(IS_OPAMP_POWERMODE(hopamp3->Init.PowerMode));
149 
150       /* Update OPAMP state */
151       hopamp1->State = HAL_OPAMP_STATE_CALIBBUSY;
152       hopamp2->State = HAL_OPAMP_STATE_CALIBBUSY;
153       hopamp3->State = HAL_OPAMP_STATE_CALIBBUSY;
154 
155       /* Backup of switches configuration to restore it at the end of the     */
156       /* calibration.                                                         */
157       tmp_OpaxSwitchesContextBackup = READ_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES_ALL_OPAMPS);
158 
159       /* Open all switches on non-inverting input, inverting input and output */
160       /* feedback.                                                            */
161       CLEAR_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES_ALL_OPAMPS);
162 
163       /* Set calibration mode to user programmed trimming values */
164       SET_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER);
165 
166       /* Select trimming settings depending on power mode */
167       if (hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
168       {
169         tmp_opamp1_otr_otuser = OPAMP_OTR_OT_USER;
170         tmp_opamp1_reg_trimming = &OPAMP->OTR;
171       }
172       else
173       {
174         tmp_opamp1_otr_otuser = 0x00000000;
175         tmp_opamp1_reg_trimming = &OPAMP->LPOTR;
176       }
177 
178       if (hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
179       {
180         tmp_opamp2_otr_otuser = OPAMP_OTR_OT_USER;
181         tmp_opamp2_reg_trimming = &OPAMP->OTR;
182       }
183       else
184       {
185         tmp_opamp2_otr_otuser = 0x00000000;
186         tmp_opamp2_reg_trimming = &OPAMP->LPOTR;
187       }
188 
189       if (hopamp3->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
190       {
191         tmp_opamp3_otr_otuser = OPAMP_OTR_OT_USER;
192         tmp_opamp3_reg_trimming = &OPAMP->OTR;
193       }
194       else
195       {
196         tmp_opamp3_otr_otuser = 0x00000000;
197         tmp_opamp3_reg_trimming = &OPAMP->LPOTR;
198       }
199 
200       /* Enable the selected opamp */
201       CLEAR_BIT (OPAMP->CSR, OPAMP_CSR_OPAXPD_ALL);
202 
203       /* Perform trimming for both differential transistors pair high and low */
204       for (trimming_diff_pair_iteration_count = 0U; trimming_diff_pair_iteration_count <= 1U; trimming_diff_pair_iteration_count++)
205       {
206         if (trimming_diff_pair_iteration_count == 0U)
207         {
208           /* Calibration of transistors differential pair high (NMOS) */
209           trimming_diff_pair = OPAMP_FACTORYTRIMMING_N;
210           opamp1_trimmingvalue = &opamp1_trimmingvaluen;
211           opamp2_trimmingvalue = &opamp2_trimmingvaluen;
212           opamp3_trimmingvalue = &opamp3_trimmingvaluen;
213 
214           /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value   */
215           /* is 00000b. Used to detect the bit toggling during trimming.      */
216           tmp_Opa1calout_DefaultSate = RESET;
217           tmp_Opa2calout_DefaultSate = RESET;
218           tmp_Opa3calout_DefaultSate = RESET;
219 
220           /* Enable calibration for N differential pair */
221           MODIFY_REG(OPAMP->CSR, OPAMP_CSR_OPAXCAL_L_ALL,
222                                  OPAMP_CSR_OPAXCAL_H_ALL);
223         }
224         else /* (trimming_diff_pair_iteration_count == 1) */
225         {
226           /* Calibration of transistors differential pair low (PMOS) */
227           trimming_diff_pair = OPAMP_FACTORYTRIMMING_P;
228           opamp1_trimmingvalue = &opamp1_trimmingvaluep;
229           opamp2_trimmingvalue = &opamp2_trimmingvaluep;
230           opamp3_trimmingvalue = &opamp3_trimmingvaluep;
231 
232           /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value   */
233           /* is 00000b. Used to detect the bit toggling during trimming.      */
234           tmp_Opa1calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp1);
235           tmp_Opa2calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp2);
236           tmp_Opa3calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp3);
237 
238           /* Enable calibration for P differential pair */
239           MODIFY_REG(OPAMP->CSR, OPAMP_CSR_OPAXCAL_H_ALL,
240                                  OPAMP_CSR_OPAXCAL_L_ALL);
241         }
242 
243 
244         /* Perform calibration parameter search by dichotomy sweep */
245         /*  - Delta initial value 16: for 5 dichotomy steps: 16 for the       */
246         /*    initial range, then successive delta sweeps (8, 4, 2, 1).       */
247         /*    can extend the search range to +/- 15 units.                    */
248         /*  - Trimming initial value 15: search range will go from 0 to 30    */
249         /*    (Trimming value 31 is forbidden).                               */
250         /* Note: After dichotomy sweep, the trimming result is determined.    */
251         /*       However, the final trimming step is deduced from previous    */
252         /*       trimming steps tested but is not effectively tested.         */
253         /*       An additional test step (using variable "final_step_check")  */
254         /*       allow to Test the final trimming step.                       */
255         *opamp1_trimmingvalue = 15U;
256         *opamp2_trimmingvalue = 15U;
257         *opamp3_trimmingvalue = 15U;
258         delta = 16U;
259 
260         while ((delta != 0U) || (final_step_check == 1U))
261         {
262           /* Set candidate trimming */
263           MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
264                                                OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, *opamp1_trimmingvalue) | tmp_opamp1_otr_otuser);
265 
266           MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
267                                                OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, *opamp2_trimmingvalue) | tmp_opamp2_otr_otuser);
268 
269           MODIFY_REG(*tmp_opamp3_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
270                                                OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, *opamp3_trimmingvalue) | tmp_opamp3_otr_otuser);
271 
272           /* Offset trimming time: during calibration, minimum time needed    */
273           /* between two steps to have 1 mV accuracy.                         */
274           HAL_Delay(OPAMP_TRIMMING_DELAY);
275 
276           /* Set flag for additional check of last trimming step equal to     */
277           /* dichotomy step before its division by 2 (equivalent to previous  */
278           /* value of dichotomy step).                                        */
279           final_step_check = delta;
280 
281           /* Divide range by 2 to continue dichotomy sweep */
282           delta >>= 1U;
283 
284           /* Set trimming values for next iteration in function of trimming   */
285           /* result toggle (versus initial state).                            */
286           /* Trimming values update with dichotomy delta of previous          */
287           /* iteration.                                                       */
288           /* Note: on the last trimming loop, delta is equal to 0 and         */
289           /*       therefore has no effect.                                   */
290           if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp1)) != tmp_Opa1calout_DefaultSate)
291           {
292             /* If calibration output is has toggled, try lower trimming */
293             *opamp1_trimmingvalue -= delta;
294           }
295           else
296           {
297             /* If calibration output is has not toggled, try higher trimming */
298             *opamp1_trimmingvalue += delta;
299           }
300 
301           if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp2)) != tmp_Opa2calout_DefaultSate)
302           {
303             /* If calibration output is has toggled, try lower trimming */
304             *opamp2_trimmingvalue -= delta;
305           }
306           else
307           {
308             /* If calibration output is has not toggled, try higher trimming */
309             *opamp2_trimmingvalue += delta;
310           }
311 
312         if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp3)) != tmp_Opa3calout_DefaultSate)
313         {
314           /* If calibration output is has toggled, try lower trimming */
315           *opamp3_trimmingvalue -= delta;
316         }
317         else
318         {
319           /* If calibration output is has not toggled, try higher trimming */
320           *opamp3_trimmingvalue += delta;
321         }
322       }
323 
324       /* Check trimming result of the selected step and perform final fine  */
325       /* trimming.                                                          */
326       /*  - If calibration output is has toggled: the current step is       */
327       /*    already optimized.                                              */
328       /*  - If calibration output is has not toggled: the current step can  */
329       /*    be optimized by incrementing it of one step.                    */
330       if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp1)) == tmp_Opa1calout_DefaultSate)
331       {
332         *opamp1_trimmingvalue += 1U;
333 
334         /* Set final fine trimming */
335         MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
336                                              OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, *opamp1_trimmingvalue) | tmp_opamp1_otr_otuser);
337       }
338       if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp2)) == tmp_Opa2calout_DefaultSate)
339       {
340         *opamp2_trimmingvalue += 1U;
341 
342         /* Set final fine trimming */
343         MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
344                                              OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, *opamp2_trimmingvalue) | tmp_opamp2_otr_otuser);
345       }
346       if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp3)) == tmp_Opa3calout_DefaultSate)
347       {
348         *opamp3_trimmingvalue += 1U;
349 
350         /* Set final fine trimming */
351         MODIFY_REG(*tmp_opamp3_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
352                                              OPAMP_OFFSET_TRIM_SET(hopamp3, trimming_diff_pair, *opamp3_trimmingvalue) | tmp_opamp3_otr_otuser);
353       }
354 
355     }
356 
357 
358     /* Disable calibration for P and N differential pairs */
359     /* Disable the selected opamp */
360     CLEAR_BIT (OPAMP->CSR, (OPAMP_CSR_OPAXCAL_H_ALL |
361                             OPAMP_CSR_OPAXCAL_L_ALL |
362                             OPAMP_CSR_OPAXPD_ALL     ));
363 
364     /* Backup of switches configuration to restore it at the end of the     */
365     /* calibration.                                                         */
366     SET_BIT(OPAMP->CSR, tmp_OpaxSwitchesContextBackup);
367 
368     /* Self calibration is successful */
369     /* Store calibration (user trimming) results in init structure. */
370 
371     /* Set user trimming mode */
372     hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
373     hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
374     hopamp3->Init.UserTrimming = OPAMP_TRIMMING_USER;
375 
376     /* Affect calibration parameters depending on mode normal/low power */
377     if (hopamp1->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
378     {
379       /* Write calibration result N */
380       hopamp1->Init.TrimmingValueN = opamp1_trimmingvaluen;
381       /* Write calibration result P */
382       hopamp1->Init.TrimmingValueP = opamp1_trimmingvaluep;
383     }
384     else
385     {
386       /* Write calibration result N */
387       hopamp1->Init.TrimmingValueNLowPower = opamp1_trimmingvaluen;
388       /* Write calibration result P */
389       hopamp1->Init.TrimmingValuePLowPower = opamp1_trimmingvaluep;
390     }
391 
392     if (hopamp2->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
393     {
394       /* Write calibration result N */
395       hopamp2->Init.TrimmingValueN = opamp2_trimmingvaluen;
396       /* Write calibration result P */
397       hopamp2->Init.TrimmingValueP = opamp2_trimmingvaluep;
398     }
399     else
400     {
401       /* Write calibration result N */
402       hopamp2->Init.TrimmingValueNLowPower = opamp2_trimmingvaluen;
403       /* Write calibration result P */
404       hopamp2->Init.TrimmingValuePLowPower = opamp2_trimmingvaluep;
405     }
406 
407     if (hopamp3->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
408     {
409       /* Write calibration result N */
410       hopamp3->Init.TrimmingValueN = opamp3_trimmingvaluen;
411       /* Write calibration result P */
412       hopamp3->Init.TrimmingValueP = opamp3_trimmingvaluep;
413     }
414     else
415     {
416       /* Write calibration result N */
417       hopamp3->Init.TrimmingValueNLowPower = opamp3_trimmingvaluen;
418       /* Write calibration result P */
419       hopamp3->Init.TrimmingValuePLowPower = opamp3_trimmingvaluep;
420     }
421 
422     /* Update OPAMP state */
423     hopamp1->State = HAL_OPAMP_STATE_READY;
424     hopamp2->State = HAL_OPAMP_STATE_READY;
425     hopamp3->State = HAL_OPAMP_STATE_READY;
426   }
427   return status;
428 }
429 
430 #else
431 
432 /*  2 OPAMPS available */
433 /*  2 OPAMPS can be calibrated in parallel */
434 
435 /**
436   * @brief  Run the self calibration of the 2 OPAMPs in parallel.
437   * @note   Trimming values (PMOS & NMOS) are updated and user trimming is
438   *         enabled is calibration is successful.
439   * @note   Calibration is performed in the mode specified in OPAMP init
440   *         structure (mode normal or low-power). To perform calibration for
441   *         both modes, repeat this function twice after OPAMP init structure
442   *         accordingly updated.
443   * @note   Calibration runs about 10 ms (5 dichotmy steps, repeated for P
444   *         and N transistors: 10 steps with 1 ms for each step).
445   * @param  hopamp1 handle
446   * @param  hopamp2 handle
447   * @retval HAL status
448   */
HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef * hopamp1,OPAMP_HandleTypeDef * hopamp2)449 HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2)
450 {
451   HAL_StatusTypeDef status = HAL_OK;
452 
453   uint32_t* opamp1_trimmingvalue;
454   uint32_t opamp1_trimmingvaluen = 0;
455   uint32_t opamp1_trimmingvaluep = 0;
456 
457   uint32_t* opamp2_trimmingvalue;
458   uint32_t opamp2_trimmingvaluen = 0;
459   uint32_t opamp2_trimmingvaluep = 0;
460 
461   uint32_t trimming_diff_pair;          /* Selection of differential transistors pair high or low */
462 
463   __IO uint32_t* tmp_opamp1_reg_trimming;   /* Selection of register of trimming depending on power mode: OTR or LPOTR */
464   __IO uint32_t* tmp_opamp2_reg_trimming;
465   uint32_t tmp_opamp1_otr_otuser;       /* Selection of bit OPAMP_OTR_OT_USER depending on trimming register pointed: OTR or LPOTR */
466   uint32_t tmp_opamp2_otr_otuser;
467 
468   uint32_t tmp_Opa1calout_DefaultSate;  /* Bit OPAMP_CSR_OPA1CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */
469   uint32_t tmp_Opa2calout_DefaultSate;  /* Bit OPAMP_CSR_OPA2CALOUT default state when trimming value is 00000b. Used to detect the bit toggling */
470 
471   uint32_t tmp_OpaxSwitchesContextBackup;
472 
473   uint8_t trimming_diff_pair_iteration_count;          /* For calibration loop algorithm: to repeat the calibration loop for both differential transistors pair high and low */
474   uint8_t delta;                                       /* For calibration loop algorithm: Variable for dichotomy steps value */
475   uint8_t final_step_check = 0x0U;                     /* For calibration loop algorithm: Flag for additional check of last trimming step */
476 
477 
478   if((hopamp1 == NULL) || (hopamp2 == NULL))
479   {
480     status = HAL_ERROR;
481   }
482   /* Check if OPAMP in calibration mode and calibration not yet enable */
483   else if(hopamp1->State !=  HAL_OPAMP_STATE_READY)
484   {
485     status = HAL_ERROR;
486   }
487   else if(hopamp2->State != HAL_OPAMP_STATE_READY)
488   {
489     status = HAL_ERROR;
490   }
491   else
492   {
493     /* Check the parameter */
494     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance));
495     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance));
496     assert_param(IS_OPAMP_POWERMODE(hopamp1->Init.PowerMode));
497     assert_param(IS_OPAMP_POWERMODE(hopamp2->Init.PowerMode));
498 
499     /* Update OPAMP state */
500     hopamp1->State = HAL_OPAMP_STATE_CALIBBUSY;
501     hopamp2->State = HAL_OPAMP_STATE_CALIBBUSY;
502 
503     /* Backup of switches configuration to restore it at the end of the     */
504     /* calibration.                                                         */
505     tmp_OpaxSwitchesContextBackup = READ_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES_ALL_OPAMPS);
506 
507     /* Open all switches on non-inverting input, inverting input and output */
508     /* feedback.                                                            */
509     CLEAR_BIT(OPAMP->CSR, OPAMP_CSR_ALL_SWITCHES_ALL_OPAMPS);
510 
511     /* Set calibration mode to user programmed trimming values */
512     SET_BIT(OPAMP->OTR, OPAMP_OTR_OT_USER);
513 
514     /* Select trimming settings depending on power mode */
515     if (hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
516     {
517       tmp_opamp1_otr_otuser = OPAMP_OTR_OT_USER;
518       tmp_opamp1_reg_trimming = &OPAMP->OTR;
519     }
520     else
521     {
522       tmp_opamp1_otr_otuser = 0x00000000U;
523       tmp_opamp1_reg_trimming = &OPAMP->LPOTR;
524     }
525 
526     if (hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
527     {
528       tmp_opamp2_otr_otuser = OPAMP_OTR_OT_USER;
529       tmp_opamp2_reg_trimming = &OPAMP->OTR;
530     }
531     else
532     {
533       tmp_opamp2_otr_otuser = 0x00000000U;
534       tmp_opamp2_reg_trimming = &OPAMP->LPOTR;
535     }
536 
537     /* Enable the selected opamp */
538     CLEAR_BIT (OPAMP->CSR, OPAMP_CSR_OPAXPD_ALL);
539 
540     /* Perform trimming for both differential transistors pair high and low */
541     for (trimming_diff_pair_iteration_count = 0U; trimming_diff_pair_iteration_count <= 1U; trimming_diff_pair_iteration_count++)
542     {
543       if (trimming_diff_pair_iteration_count == 0U)
544       {
545         /* Calibration of transistors differential pair high (NMOS) */
546         trimming_diff_pair = OPAMP_FACTORYTRIMMING_N;
547         opamp1_trimmingvalue = &opamp1_trimmingvaluen;
548         opamp2_trimmingvalue = &opamp2_trimmingvaluen;
549 
550         /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value   */
551         /* is 00000b. Used to detect the bit toggling during trimming.      */
552         tmp_Opa1calout_DefaultSate = 0U;
553         tmp_Opa2calout_DefaultSate = 0U;
554 
555         /* Enable calibration for N differential pair */
556         MODIFY_REG(OPAMP->CSR, OPAMP_CSR_OPAXCAL_L_ALL,
557                                OPAMP_CSR_OPAXCAL_H_ALL);
558       }
559       else /* (trimming_diff_pair_iteration_count == 1) */
560       {
561         /* Calibration of transistors differential pair low (PMOS) */
562         trimming_diff_pair = OPAMP_FACTORYTRIMMING_P;
563         opamp1_trimmingvalue = &opamp1_trimmingvaluep;
564         opamp2_trimmingvalue = &opamp2_trimmingvaluep;
565 
566         /* Set bit OPAMP_CSR_OPAXCALOUT default state when trimming value   */
567         /* is 00000b. Used to detect the bit toggling during trimming.      */
568         tmp_Opa1calout_DefaultSate = (uint32_t) OPAMP_CSR_OPAXCALOUT(hopamp1);
569         tmp_Opa2calout_DefaultSate = OPAMP_CSR_OPAXCALOUT(hopamp2);
570 
571         /* Enable calibration for P differential pair */
572         MODIFY_REG(OPAMP->CSR, OPAMP_CSR_OPAXCAL_H_ALL,
573                                OPAMP_CSR_OPAXCAL_L_ALL);
574       }
575 
576 
577       /* Perform calibration parameter search by dichotomy sweep */
578       /*  - Delta initial value 16: for 5 dichotomy steps: 16 for the       */
579       /*    initial range, then successive delta sweeps (8, 4, 2, 1).       */
580       /*    can extend the search range to +/- 15 units.                    */
581       /*  - Trimming initial value 15: search range will go from 0 to 30    */
582       /*    (Trimming value 31 is forbidden).                               */
583       /* Note: After dichotomy sweep, the trimming result is determined.    */
584       /*       However, the final trimming step is deduced from previous    */
585       /*       trimming steps tested but is not effectively tested.         */
586       /*       An additional test step (using variable "final_step_check")  */
587       /*       allow to Test the final trimming step.                       */
588       *opamp1_trimmingvalue = 15U;
589       *opamp2_trimmingvalue = 15U;
590       delta = 16U;
591 
592       while ((delta != 0U) || (final_step_check == 1U))
593       {
594         /* Set candidate trimming */
595         MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
596                                              OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, *opamp1_trimmingvalue) | tmp_opamp1_otr_otuser);
597 
598         MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
599                                              OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, *opamp2_trimmingvalue) | tmp_opamp2_otr_otuser);
600 
601 
602         /* Offset trimming time: during calibration, minimum time needed    */
603         /* between two steps to have 1 mV accuracy.                         */
604         HAL_Delay(OPAMP_TRIMMING_DELAY);
605 
606         /* Set flag for additional check of last trimming step equal to     */
607         /* dichotomy step before its division by 2 (equivalent to previous  */
608         /* value of dichotomy step).                                        */
609         final_step_check = delta;
610 
611         /* Divide range by 2 to continue dichotomy sweep */
612         delta >>= 1U;
613 
614         /* Set trimming values for next iteration in function of trimming   */
615         /* result toggle (versus initial state).                            */
616         /* Trimming values update with dichotomy delta of previous          */
617         /* iteration.                                                       */
618         /* Note: on the last trimming loop, delta is equal to 0 and         */
619         /*       therefore has no effect.                                   */
620         if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp1)) != tmp_Opa1calout_DefaultSate)
621         {
622           /* If calibration output is has toggled, try lower trimming */
623           *opamp1_trimmingvalue -= delta;
624         }
625         else
626         {
627           /* If calibration output is has not toggled, try higher trimming */
628           *opamp1_trimmingvalue += delta;
629         }
630 
631         if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp2)) != tmp_Opa2calout_DefaultSate)
632         {
633           /* If calibration output is has toggled, try lower trimming */
634           *opamp2_trimmingvalue -= delta;
635         }
636         else
637         {
638           /* If calibration output is has not toggled, try higher trimming */
639           *opamp2_trimmingvalue += delta;
640         }
641       }
642 
643       /* Check trimming result of the selected step and perform final fine  */
644       /* trimming.                                                          */
645       /*  - If calibration output is has toggled: the current step is       */
646       /*    already optimized.                                              */
647       /*  - If calibration output is has not toggled: the current step can  */
648       /*    be optimized by incrementing it of one step.                    */
649       if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp1)) == tmp_Opa1calout_DefaultSate)
650       {
651         *opamp1_trimmingvalue += 1U;
652 
653         /* Set final fine trimming */
654         MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
655                                              OPAMP_OFFSET_TRIM_SET(hopamp1, trimming_diff_pair, *opamp1_trimmingvalue) | tmp_opamp1_otr_otuser);
656       }
657       if (READ_BIT(OPAMP->CSR, OPAMP_CSR_OPAXCALOUT(hopamp2)) == tmp_Opa2calout_DefaultSate)
658       {
659         *opamp2_trimmingvalue += 1U;
660 
661         /* Set final fine trimming */
662         MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, OPAMP_TRIM_VALUE_MASK) ,
663                                              OPAMP_OFFSET_TRIM_SET(hopamp2, trimming_diff_pair, *opamp2_trimmingvalue) | tmp_opamp2_otr_otuser);
664 
665       }
666 
667     }
668 
669 
670     /* Disable calibration for P and N differential pairs */
671     /* Disable the selected opamp */
672     CLEAR_BIT (OPAMP->CSR, (OPAMP_CSR_OPAXCAL_H_ALL |
673                             OPAMP_CSR_OPAXCAL_L_ALL |
674                             OPAMP_CSR_OPAXPD_ALL     ));
675 
676     /* Backup of switches configuration to restore it at the end of the     */
677     /* calibration.                                                         */
678     SET_BIT(OPAMP->CSR, tmp_OpaxSwitchesContextBackup);
679 
680     /* Self calibration is successful */
681     /* Store calibration (user trimming) results in init structure. */
682 
683     /* Set user trimming mode */
684     hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
685     hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
686 
687     /* Affect calibration parameters depending on mode normal/low power */
688     if (hopamp1->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
689     {
690       /* Write calibration result N */
691       hopamp1->Init.TrimmingValueN = opamp1_trimmingvaluen;
692       /* Write calibration result P */
693       hopamp1->Init.TrimmingValueP = opamp1_trimmingvaluep;
694     }
695     else
696     {
697       /* Write calibration result N */
698       hopamp1->Init.TrimmingValueNLowPower = opamp1_trimmingvaluen;
699       /* Write calibration result P */
700       hopamp1->Init.TrimmingValuePLowPower = opamp1_trimmingvaluep;
701     }
702 
703     if (hopamp2->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
704     {
705       /* Write calibration result N */
706       hopamp2->Init.TrimmingValueN = opamp2_trimmingvaluen;
707       /* Write calibration result P */
708       hopamp2->Init.TrimmingValueP = opamp2_trimmingvaluep;
709     }
710     else
711     {
712       /* Write calibration result N */
713       hopamp2->Init.TrimmingValueNLowPower = opamp2_trimmingvaluen;
714       /* Write calibration result P */
715       hopamp2->Init.TrimmingValuePLowPower = opamp2_trimmingvaluep;
716     }
717 
718     /* Update OPAMP state */
719     hopamp1->State = HAL_OPAMP_STATE_READY;
720     hopamp2->State = HAL_OPAMP_STATE_READY;
721   }
722   return status;
723 }
724 
725 #endif /* STM32L151xD || STM32L152xD || STM32L162xD */
726 
727 /**
728   * @}
729   */
730 
731 /** @defgroup OPAMPEx_Exported_Functions_Group2 Extended Peripheral Control functions
732  *  @brief   Extended peripheral control functions
733  *
734 @verbatim
735  ===============================================================================
736              ##### Peripheral Control functions #####
737  ===============================================================================
738     [..]
739       (+) OPAMP unlock.
740 
741 @endverbatim
742   * @{
743   */
744 
745 /**
746   * @brief  Unlock the selected OPAMP configuration.
747   *         This function must be called only when OPAMP is in state "locked".
748   * @param  hopamp OPAMP handle
749   * @retval HAL status
750   */
HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef * hopamp)751 HAL_StatusTypeDef HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef* hopamp)
752 {
753   HAL_StatusTypeDef status = HAL_OK;
754 
755   /* Check the OPAMP handle allocation */
756   /* Check if OPAMP locked */
757   if(hopamp == NULL)
758   {
759     status = HAL_ERROR;
760   }
761   /* Check the OPAMP handle allocation */
762   /* Check if OPAMP locked */
763   else if(hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED)
764   {
765     /* Check the parameter */
766     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
767 
768    /* OPAMP state changed to locked */
769     hopamp->State = HAL_OPAMP_STATE_BUSY;
770   }
771   else
772   {
773     status = HAL_ERROR;
774   }
775 
776   return status;
777 }
778 
779 /**
780   * @}
781   */
782 
783 /**
784   * @}
785   */
786 
787 #endif /* STM32L151xCA || STM32L151xD || STM32L152xCA || STM32L152xD || STM32L162xCA || STM32L162xD || STM32L151xE || STM32L151xDX || STM32L152xE || STM32L152xDX || STM32L162xE || STM32L162xDX || STM32L162xC || STM32L152xC || STM32L151xC */
788 
789 #endif /* HAL_OPAMP_MODULE_ENABLED */
790 /**
791   * @}
792   */
793 
794 /**
795   * @}
796   */
797 
798