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