1 /**
2   ******************************************************************************
3   * @file    stm32l5xx_hal_opamp_ex.c
4   * @author  MCD Application Team
5   * @brief   Extended OPAMP HAL module driver.
6   *          This file provides firmware functions to manage the following
7   *          functionalities of the operational amplifier(s) peripheral:
8   *           + Extended Initialization and de-initialization functions
9   *           + Extended Peripheral Control functions
10   *
11   @verbatim
12   ******************************************************************************
13   * @attention
14   *
15   * Copyright (c) 2019 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 "stm32l5xx_hal.h"
27 
28 /** @addtogroup STM32L5xx_HAL_Driver
29   * @{
30   */
31 
32 /** @defgroup OPAMPEx OPAMPEx
33   * @brief OPAMP Extended HAL module driver
34   * @{
35   */
36 
37 #ifdef HAL_OPAMP_MODULE_ENABLED
38 
39 /* Private typedef -----------------------------------------------------------*/
40 /* Private define ------------------------------------------------------------*/
41 /* Private macro -------------------------------------------------------------*/
42 /* Private variables ---------------------------------------------------------*/
43 /* Private function prototypes -----------------------------------------------*/
44 /* Exported functions --------------------------------------------------------*/
45 
46 /** @defgroup OPAMPEx_Exported_Functions OPAMPEx Extended Exported Functions
47   * @{
48   */
49 
50 /** @defgroup OPAMPEx_Exported_Functions_Group1 Extended operation functions
51   * @brief    Extended operation functions
52   *
53 @verbatim
54  ===============================================================================
55               ##### Extended IO operation functions #####
56  ===============================================================================
57   [..]
58       (+) OPAMP Self calibration.
59 
60 @endverbatim
61   * @{
62   */
63 
64 /*  2 OPAMPS available */
65 /*  2 OPAMPS can be calibrated in parallel */
66 
67 /**
68   * @brief  Run the self calibration of the 2 OPAMPs in parallel.
69   * @note   Trimming values (PMOS & NMOS) are updated and user trimming is
70   *         enabled is calibration is successful.
71   * @note   Calibration is performed in the mode specified in OPAMP init
72   *         structure (mode normal or low-power). To perform calibration for
73   *         both modes, repeat this function twice after OPAMP init structure
74   *         accordingly updated.
75   * @note   Calibration runs about 10 ms (5 dichotomy steps, repeated for P
76   *         and N transistors: 10 steps with 1 ms for each step).
77   * @param  hopamp1 handle
78   * @param  hopamp2 handle
79   * @retval HAL status
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 /* Selection of register of trimming depending on power mode: OTR or LPOTR */
92   __IO uint32_t* tmp_opamp1_reg_trimming;
93   __IO uint32_t* tmp_opamp2_reg_trimming;
94 
95   uint32_t delta;
96   uint32_t opampmode1;
97   uint32_t opampmode2;
98 
99   if((hopamp1 == NULL) || (hopamp2 == NULL))
100   {
101     status = HAL_ERROR;
102   }
103   /* Check if OPAMP in calibration mode and calibration not yet enable */
104   else if(hopamp1->State !=  HAL_OPAMP_STATE_READY)
105   {
106     status = HAL_ERROR;
107   }
108   else if(hopamp2->State != HAL_OPAMP_STATE_READY)
109   {
110     status = HAL_ERROR;
111   }
112   else
113   {
114     /* Check the parameter */
115     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance));
116     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance));
117 
118     assert_param(IS_OPAMP_POWERMODE(hopamp1->Init.PowerMode));
119     assert_param(IS_OPAMP_POWERMODE(hopamp2->Init.PowerMode));
120 
121     /* Save OPAMP mode as                                          */
122     /* the calibration is not working in PGA mode                  */
123     opampmode1 = READ_BIT(hopamp1->Instance->CSR,OPAMP_CSR_OPAMODE);
124     opampmode2 = READ_BIT(hopamp2->Instance->CSR,OPAMP_CSR_OPAMODE);
125 
126     /* Use of standalone mode */
127     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_OPAMODE, OPAMP_STANDALONE_MODE);
128     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_OPAMODE, OPAMP_STANDALONE_MODE);
129 
130     /*  user trimming values are used for offset calibration */
131     SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_USERTRIM);
132     SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_USERTRIM);
133 
134     /* Select trimming settings depending on power mode */
135     if (hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMALPOWER)
136     {
137       tmp_opamp1_reg_trimming = &OPAMP1->OTR;
138     }
139     else
140     {
141       tmp_opamp1_reg_trimming = &OPAMP1->LPOTR;
142     }
143 
144     if (hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMALPOWER)
145     {
146       tmp_opamp2_reg_trimming = &OPAMP2->OTR;
147     }
148     else
149     {
150       tmp_opamp2_reg_trimming = &OPAMP2->LPOTR;
151     }
152 
153     /* Enable calibration */
154     SET_BIT (hopamp1->Instance->CSR, OPAMP_CSR_CALON);
155     SET_BIT (hopamp2->Instance->CSR, OPAMP_CSR_CALON);
156 
157     /* 1st calibration - N */
158     CLEAR_BIT (hopamp1->Instance->CSR, OPAMP_CSR_CALSEL);
159     CLEAR_BIT (hopamp2->Instance->CSR, OPAMP_CSR_CALSEL);
160 
161     /* Enable the selected opamp */
162     SET_BIT (hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
163     SET_BIT (hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
164 
165     /* Init trimming counter */
166     /* Medium value */
167     trimmingvaluen1 = 16U;
168     trimmingvaluen2 = 16U;
169     delta = 8U;
170 
171     while (delta != 0U)
172     {
173       /* Set candidate trimming */
174       /* OPAMP_POWERMODE_NORMALPOWER */
175       MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen1);
176       MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen2);
177 
178       /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */
179       /* Offset trim time: during calibration, minimum time needed between */
180       /* two steps to have 1 mV accuracy */
181       HAL_Delay(OPAMP_TRIMMING_DELAY);
182 
183       if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT) != 0U)
184       {
185         /* OPAMP_CSR_CALOUT is HIGH try lower trimming */
186         trimmingvaluen1 -= delta;
187       }
188       else
189       {
190         /* OPAMP_CSR_CALOUT is LOW try higher trimming */
191         trimmingvaluen1 += delta;
192       }
193 
194       if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT) != 0U)
195       {
196         /* OPAMP_CSR_CALOUT is HIGH try lower trimming */
197         trimmingvaluen2 -= delta;
198       }
199       else
200       {
201         /* OPAMP_CSR_CALOUT is LOW try higher trimming */
202         trimmingvaluen2 += delta;
203       }
204       /* Divide range by 2 to continue dichotomy sweep */
205       delta >>= 1U;
206     }
207 
208     /* Still need to check if right calibration is current value or one step below */
209     /* Indeed the first value that causes the OUTCAL bit to change from 0 to 1  */
210     /* Set candidate trimming */
211     MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen1);
212     MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen2);
213 
214     /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */
215     /* Offset trim time: during calibration, minimum time needed between */
216     /* two steps to have 1 mV accuracy */
217     HAL_Delay(OPAMP_TRIMMING_DELAY);
218 
219     if ((READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT)) == 0U)
220     {
221       /* Trimming value is actually one value more */
222       trimmingvaluen1++;
223       MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen1);
224     }
225 
226     if ((READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT)) == 0U)
227     {
228       /* Trimming value is actually one value more */
229       trimmingvaluen2++;
230       MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETN, trimmingvaluen2);
231     }
232 
233     /* 2nd calibration - P */
234     SET_BIT (hopamp1->Instance->CSR, OPAMP_CSR_CALSEL);
235     SET_BIT (hopamp2->Instance->CSR, OPAMP_CSR_CALSEL);
236 
237     /* Init trimming counter */
238     /* Medium value */
239     trimmingvaluep1 = 16U;
240     trimmingvaluep2 = 16U;
241     delta = 8U;
242 
243     while (delta != 0U)
244     {
245       /* Set candidate trimming */
246       /* OPAMP_POWERMODE_NORMALPOWER */
247       MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1<<OPAMP_INPUT_NONINVERTING));
248       MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2<<OPAMP_INPUT_NONINVERTING));
249 
250       /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */
251       /* Offset trim time: during calibration, minimum time needed between */
252       /* two steps to have 1 mV accuracy */
253       HAL_Delay(OPAMP_TRIMMING_DELAY);
254 
255       if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT) != 0U)
256       {
257         /* OPAMP_CSR_CALOUT is HIGH try higher trimming */
258         trimmingvaluep1 += delta;
259       }
260       else
261       {
262         /* OPAMP_CSR_CALOUT is HIGH try lower trimming */
263         trimmingvaluep1 -= delta;
264       }
265 
266       if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT) != 0U)
267       {
268         /* OPAMP_CSR_CALOUT is HIGH try higher trimming */
269         trimmingvaluep2 += delta;
270       }
271       else
272       {
273         /* OPAMP_CSR_CALOUT is LOW try lower trimming */
274         trimmingvaluep2 -= delta;
275       }
276       /* Divide range by 2 to continue dichotomy sweep */
277       delta >>= 1U;
278     }
279 
280     /* Still need to check if right calibration is current value or one step below */
281     /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0  */
282     /* Set candidate trimming */
283     MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1<<OPAMP_INPUT_NONINVERTING));
284     MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2<<OPAMP_INPUT_NONINVERTING));
285 
286     /* OFFTRIMmax delay 1 ms as per datasheet (electrical characteristics */
287     /* Offset trim time: during calibration, minimum time needed between */
288     /* two steps to have 1 mV accuracy */
289     HAL_Delay(OPAMP_TRIMMING_DELAY);
290 
291     if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT) != 0U)
292     {
293       /* Trimming value is actually one value more */
294       trimmingvaluep1++;
295       MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1<<OPAMP_INPUT_NONINVERTING));
296     }
297 
298     if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT) != 0U)
299     {
300       /* Trimming value is actually one value more */
301       trimmingvaluep2++;
302       MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2<<OPAMP_INPUT_NONINVERTING));
303     }
304 
305     /* Disable the OPAMPs */
306     CLEAR_BIT (hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
307     CLEAR_BIT (hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
308 
309     /* Disable calibration & set normal mode (operating mode) */
310     CLEAR_BIT (hopamp1->Instance->CSR, OPAMP_CSR_CALON);
311     CLEAR_BIT (hopamp2->Instance->CSR, OPAMP_CSR_CALON);
312 
313     /* Self calibration is successful */
314     /* Store calibration (user trimming) results in init structure. */
315 
316     /* Set user trimming mode */
317     hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
318     hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
319 
320     /* Affect calibration parameters depending on mode normal/low power */
321     if (hopamp1->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
322     {
323       /* Write calibration result N */
324       hopamp1->Init.TrimmingValueN = trimmingvaluen1;
325       /* Write calibration result P */
326       hopamp1->Init.TrimmingValueP = trimmingvaluep1;
327     }
328     else
329     {
330       /* Write calibration result N */
331       hopamp1->Init.TrimmingValueNLowPower = trimmingvaluen1;
332       /* Write calibration result P */
333       hopamp1->Init.TrimmingValuePLowPower = trimmingvaluep1;
334     }
335 
336     if (hopamp2->Init.PowerMode != OPAMP_POWERMODE_LOWPOWER)
337     {
338       /* Write calibration result N */
339       hopamp2->Init.TrimmingValueN = trimmingvaluen2;
340       /* Write calibration result P */
341       hopamp2->Init.TrimmingValueP = trimmingvaluep2;
342     }
343     else
344     {
345       /* Write calibration result N */
346       hopamp2->Init.TrimmingValueNLowPower = trimmingvaluen2;
347       /* Write calibration result P */
348       hopamp2->Init.TrimmingValuePLowPower = trimmingvaluep2;
349     }
350 
351     /* Update OPAMP state */
352     hopamp1->State = HAL_OPAMP_STATE_READY;
353     hopamp2->State = HAL_OPAMP_STATE_READY;
354 
355     /* Restore OPAMP mode after calibration */
356     MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_OPAMODE, opampmode1);
357     MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_OPAMODE, opampmode2);
358   }
359   return status;
360 }
361 
362 /**
363   * @}
364   */
365 
366 /** @defgroup OPAMPEx_Exported_Functions_Group2 Extended Peripheral Control functions
367  *  @brief   Peripheral Control functions
368  *
369 @verbatim
370  ===============================================================================
371              ##### Peripheral Control functions #####
372  ===============================================================================
373     [..]
374       (+) OPAMP unlock.
375 
376 @endverbatim
377   * @{
378   */
379 
380 /**
381   * @brief  Unlock the selected OPAMP configuration.
382   * @note   This function must be called only when OPAMP is in state "locked".
383   * @param  hopamp: OPAMP handle
384   * @retval HAL status
385   */
HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef * hopamp)386 HAL_StatusTypeDef HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef* hopamp)
387 {
388   HAL_StatusTypeDef status = HAL_OK;
389 
390   /* Check the OPAMP handle allocation */
391   /* Check if OPAMP locked */
392   if(hopamp == NULL)
393   {
394     status = HAL_ERROR;
395   }
396   /* Check the OPAMP handle allocation */
397   /* Check if OPAMP locked */
398   else if(hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED)
399   {
400     /* Check the parameter */
401     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
402 
403    /* OPAMP state changed to locked */
404     hopamp->State = HAL_OPAMP_STATE_BUSY;
405   }
406   else
407   {
408     status = HAL_ERROR;
409   }
410 
411   return status;
412 }
413 
414 /**
415   * @}
416   */
417 
418 /**
419   * @}
420   */
421 
422 #endif /* HAL_OPAMP_MODULE_ENABLED */
423 /**
424   * @}
425   */
426 
427 /**
428   * @}
429   */
430 
431