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