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>© 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