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