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