1 /**
2   ******************************************************************************
3   * @file    stm32h7xx_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) 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 "stm32h7xx_hal.h"
27 
28 /** @addtogroup STM32H7xx_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 OPAMP Extended Exported Functions
47   * @{
48   */
49 
50 /** @defgroup OPAMPEx_Exported_Functions_Group1 Extended Input and Output 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 /**
65   * @brief  Run the self calibration of 2 OPAMPs in parallel.
66   * @note   Trimming values (PMOS & NMOS) are updated and user trimming is
67   *         enabled is calibration is successful.
68   * @note   Calibration is performed in the mode specified in OPAMP init
69   *         structure (mode normal or low power). To perform calibration for
70   *         both modes, repeat this function twice after OPAMP init structure
71   *         accordingly updated.
72   * @param  hopamp1 handle
73   * @param  hopamp2 handle
74   * @retval HAL status
75   */
76 
HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef * hopamp1,OPAMP_HandleTypeDef * hopamp2)77 HAL_StatusTypeDef HAL_OPAMPEx_SelfCalibrateAll(OPAMP_HandleTypeDef *hopamp1, OPAMP_HandleTypeDef *hopamp2)
78 {
79   HAL_StatusTypeDef status = HAL_OK;
80 
81   uint32_t trimmingvaluen1;
82   uint32_t trimmingvaluep1;
83   uint32_t trimmingvaluen2;
84   uint32_t trimmingvaluep2;
85 
86 /* Selection of register of trimming depending on power mode: OTR or HSOTR */
87   __IO uint32_t* tmp_opamp1_reg_trimming;
88   __IO uint32_t* tmp_opamp2_reg_trimming;
89 
90   uint32_t delta;
91   uint32_t opampmode1;
92   uint32_t opampmode2;
93 
94   if((hopamp1 == NULL) || (hopamp2 == NULL))
95   {
96     status = HAL_ERROR;
97   }
98   /* Check if OPAMP in calibration mode and calibration not yet enable */
99   else if(hopamp1->State !=  HAL_OPAMP_STATE_READY)
100   {
101     status = HAL_ERROR;
102   }
103   else if(hopamp2->State != HAL_OPAMP_STATE_READY)
104   {
105     status = HAL_ERROR;
106   }
107   else
108   {
109       /* Check the parameter */
110       assert_param(IS_OPAMP_ALL_INSTANCE(hopamp1->Instance));
111       assert_param(IS_OPAMP_ALL_INSTANCE(hopamp2->Instance));
112 
113       assert_param(IS_OPAMP_POWERMODE(hopamp1->Init.PowerMode));
114       assert_param(IS_OPAMP_POWERMODE(hopamp2->Init.PowerMode));
115 
116       /* Set Calibration mode */
117       /* Non-inverting input connected to calibration reference voltage. */
118       SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_FORCEVP);
119       SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_FORCEVP);
120 
121       /* Save OPAMP mode  */
122       opampmode1 = READ_BIT(hopamp1->Instance->CSR,OPAMP_CSR_VMSEL);
123       opampmode2 = READ_BIT(hopamp2->Instance->CSR,OPAMP_CSR_VMSEL);
124 
125       /* Use of standalone mode */
126       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_VMSEL, OPAMP_STANDALONE_MODE);
127       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_VMSEL, OPAMP_STANDALONE_MODE);
128 
129       /*  user trimming values are used for offset calibration */
130       SET_BIT(hopamp1->Instance->CSR, OPAMP_CSR_USERTRIM);
131       SET_BIT(hopamp2->Instance->CSR, OPAMP_CSR_USERTRIM);
132 
133       /* Select trimming settings depending on power mode */
134       if (hopamp1->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
135       {
136         tmp_opamp1_reg_trimming = &OPAMP1->OTR;
137       }
138       else
139       {
140         tmp_opamp1_reg_trimming = &OPAMP1->HSOTR;
141       }
142 
143       if (hopamp2->Init.PowerMode == OPAMP_POWERMODE_NORMAL)
144       {
145         tmp_opamp2_reg_trimming = &OPAMP2->OTR;
146       }
147       else
148       {
149         tmp_opamp2_reg_trimming = &OPAMP2->HSOTR;
150       }
151 
152       /* Enable calibration */
153       SET_BIT (hopamp1->Instance->CSR, OPAMP_CSR_CALON);
154       SET_BIT (hopamp2->Instance->CSR, OPAMP_CSR_CALON);
155 
156       /* 1st calibration - N */
157       /* Select 90U% VREF */
158       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
159       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
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_NORMAL */
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 2 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 Low try higher trimming */
186           trimmingvaluen1 += delta;
187         }
188         else
189         {
190           /* OPAMP_CSR_CALOUT is High try lower trimming */
191           trimmingvaluen1 -= delta;
192         }
193 
194         if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT)!= 0U)
195         {
196           /* OPAMP_CSR_CALOUT is Low try higher trimming */
197           trimmingvaluen2 += delta;
198         }
199         else
200         {
201           /* OPAMP_CSR_CALOUT is High try lower 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 2 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       /* Select 10U% VREF */
235       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
236       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
237 
238       /* Init trimming counter */
239       /* Medium value */
240       trimmingvaluep1 = 16U;
241       trimmingvaluep2 = 16U;
242       delta = 8U;
243 
244       while (delta != 0U)
245       {
246         /* Set candidate trimming */
247         /* OPAMP_POWERMODE_NORMAL */
248         MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1<<OPAMP_INPUT_NONINVERTING));
249         MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2<<OPAMP_INPUT_NONINVERTING));
250 
251         /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
252         /* Offset trim time: during calibration, minimum time needed between */
253         /* two steps to have 1 mV accuracy */
254         HAL_Delay(OPAMP_TRIMMING_DELAY);
255 
256         if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT)!= 0U)
257         {
258           /* OPAMP_CSR_CALOUT is Low try higher trimming */
259           trimmingvaluep1 += delta;
260         }
261         else
262         {
263           /* OPAMP_CSR_CALOUT is HIGH try lower trimming */
264           trimmingvaluep1 -= delta;
265         }
266 
267         if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT)!= 0U)
268         {
269           /* OPAMP_CSR_CALOUT is Low try higher trimming */
270           trimmingvaluep2 += delta;
271         }
272         else
273         {
274           /* OPAMP_CSR_CALOUT is High try lower trimming */
275           trimmingvaluep2 -= delta;
276         }
277         /* Divide range by 2 to continue dichotomy sweep */
278         delta >>= 1U;
279       }
280 
281       /* Still need to check if right calibration is current value or one step below */
282       /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0  */
283       /* Set candidate trimming */
284       MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1<<OPAMP_INPUT_NONINVERTING));
285       MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2<<OPAMP_INPUT_NONINVERTING));
286 
287       /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
288       /* Offset trim time: during calibration, minimum time needed between */
289       /* two steps to have 1 mV accuracy */
290       HAL_Delay(OPAMP_TRIMMING_DELAY);
291 
292       if (READ_BIT(hopamp1->Instance->CSR, OPAMP_CSR_CALOUT)!= 0U)
293       {
294         /* Trimming value is actually one value more */
295         trimmingvaluep1++;
296         MODIFY_REG(*tmp_opamp1_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep1<<OPAMP_INPUT_NONINVERTING));
297       }
298 
299       if (READ_BIT(hopamp2->Instance->CSR, OPAMP_CSR_CALOUT)!= 0U)
300       {
301         /* Trimming value is actually one value more */
302         trimmingvaluep2++;
303         MODIFY_REG(*tmp_opamp2_reg_trimming, OPAMP_OTR_TRIMOFFSETP, (trimmingvaluep2<<OPAMP_INPUT_NONINVERTING));
304       }
305 
306       /* Disable calibration & set normal mode (operating mode) */
307       CLEAR_BIT (hopamp1->Instance->CSR, OPAMP_CSR_CALON);
308       CLEAR_BIT (hopamp2->Instance->CSR, OPAMP_CSR_CALON);
309 
310      /* Disable the OPAMPs */
311       CLEAR_BIT (hopamp1->Instance->CSR, OPAMP_CSR_OPAMPxEN);
312       CLEAR_BIT (hopamp2->Instance->CSR, OPAMP_CSR_OPAMPxEN);
313 
314       /* Self calibration is successful */
315       /* Store calibration (user trimming) results in init structure. */
316 
317       /* Set user trimming mode */
318       hopamp1->Init.UserTrimming = OPAMP_TRIMMING_USER;
319       hopamp2->Init.UserTrimming = OPAMP_TRIMMING_USER;
320 
321       /* Affect calibration parameters depending on mode normal/high speed */
322       if (hopamp1->Init.PowerMode != OPAMP_POWERMODE_HIGHSPEED)
323       {
324         /* Write calibration result N */
325         hopamp1->Init.TrimmingValueN = trimmingvaluen1;
326         /* Write calibration result P */
327         hopamp1->Init.TrimmingValueP = trimmingvaluep1;
328       }
329       else
330       {
331         /* Write calibration result N */
332         hopamp1->Init.TrimmingValueNHighSpeed = trimmingvaluen1;
333         /* Write calibration result P */
334         hopamp1->Init.TrimmingValuePHighSpeed = trimmingvaluep1;
335       }
336 
337       if (hopamp2->Init.PowerMode != OPAMP_POWERMODE_HIGHSPEED)
338       {
339         /* Write calibration result N */
340         hopamp2->Init.TrimmingValueN = trimmingvaluen2;
341         /* Write calibration result P */
342         hopamp2->Init.TrimmingValueP = trimmingvaluep2;
343       }
344       else
345       {
346         /* Write calibration result N */
347         hopamp2->Init.TrimmingValueNHighSpeed = trimmingvaluen2;
348         /* Write calibration result P */
349         hopamp2->Init.TrimmingValuePHighSpeed = trimmingvaluep2;
350 
351        }
352       /* Update OPAMP state */
353       hopamp1->State = HAL_OPAMP_STATE_READY;
354       hopamp2->State = HAL_OPAMP_STATE_READY;
355 
356       /* Restore OPAMP mode after calibration */
357       MODIFY_REG(hopamp1->Instance->CSR, OPAMP_CSR_VMSEL, opampmode1);
358       MODIFY_REG(hopamp2->Instance->CSR, OPAMP_CSR_VMSEL, opampmode2);
359   }
360 
361   return status;
362 }
363 
364 /**
365   * @}
366   */
367 
368 /** @defgroup OPAMPEx_Exported_Functions_Group2 Peripheral Control functions
369  *  @brief   Peripheral Control functions
370  *
371 @verbatim
372  ===============================================================================
373              ##### Peripheral Control functions #####
374  ===============================================================================
375     [..]
376       (+) OPAMP unlock.
377 
378 @endverbatim
379   * @{
380   */
381 
382 /**
383   * @brief  Unlock the selected OPAMP configuration.
384   * @note   This function must be called only when OPAMP is in state "locked".
385   * @param  hopamp: OPAMP handle
386   * @retval HAL status
387   */
HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef * hopamp)388 HAL_StatusTypeDef HAL_OPAMPEx_Unlock(OPAMP_HandleTypeDef* hopamp)
389 {
390   HAL_StatusTypeDef status = HAL_OK;
391 
392   /* Check the OPAMP handle allocation */
393   /* Check if OPAMP locked */
394   if(hopamp == NULL)
395   {
396     status = HAL_ERROR;
397   }
398   /* Check the OPAMP handle allocation */
399   /* Check if OPAMP locked */
400   else if(hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED)
401   {
402     /* Check the parameter */
403     assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
404 
405    /* OPAMP state changed to locked */
406     hopamp->State = HAL_OPAMP_STATE_BUSY;
407   }
408   else
409   {
410     status = HAL_ERROR;
411   }
412 
413   return status;
414 }
415 
416 /**
417   * @}
418   */
419 
420 /**
421   * @}
422   */
423 
424 /**
425   * @}
426   */
427 
428 /**
429   * @}
430   */
431 
432 #endif /* HAL_OPAMP_MODULE_ENABLED */
433