/* ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, 2011,2012,2013 Giovanni Di Sirio. This file is part of ChibiOS/RT. ChibiOS/RT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. ChibiOS/RT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * @file STM32F4xx/adc_lld.c * @brief STM32F4xx/STM32F2xx ADC subsystem low level driver source. * * @addtogroup ADC * @{ */ #include "ch.h" #include "hal.h" #if HAL_USE_ADC || defined(__DOXYGEN__) int debugzero = 0; /*===========================================================================*/ /* Driver local definitions. */ /*===========================================================================*/ #define SDADC_FORBIDDEN_CR1_FLAGS (SDADC_CR1_INIT | SDADC_CR1_RDMAEN | \ SDADC_CR1_RSYNC | SDADC_CR1_JSYNC | \ SDADC_CR1_ROVRIE | SDADC_CR1_REOCIE | \ SDADC_CR1_JEOCIE | SDADC_CR1_EOCALIE) #define SDADC_ENFORCED_CR1_FLAGS (SDADC_CR1_JDMAEN | SDADC_CR1_JOVRIE) #define SDADC_FORBIDDEN_CR2_FLAGS (SDADC_CR2_RSWSTART | \ SDADC_CR2_RCONT | \ SDADC_CR2_RCH | \ SDADC_CR2_JSWSTART | \ SDADC_CR2_JCONT | \ SDADC_CR2_STARTCALIB | \ SDADC_CR2_CALIBCNT) /*===========================================================================*/ /* Driver exported variables. */ /*===========================================================================*/ /** @brief ADC1 driver identifier.*/ #if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) ADCDriver ADCD1; #endif /** @brief ADC2 driver identifier.*/ #if STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) ADCDriver ADCD2; #endif /** @brief ADC3 driver identifier.*/ #if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) ADCDriver ADCD3; #endif /** @brief SDADC1 driver identifier.*/ #if STM32_ADC_USE_SDADC1 || defined(__DOXYGEN__) ADCDriver SDADCD1; #endif /** @brief SDADC2 driver identifier.*/ #if STM32_ADC_USE_SDADC2 || defined(__DOXYGEN__) ADCDriver SDADCD2; #endif /** @brief SDADC3 driver identifier.*/ #if STM32_ADC_USE_SDADC3 || defined(__DOXYGEN__) ADCDriver SDADCD3; #endif /*===========================================================================*/ /* Driver local variables and types. */ /*===========================================================================*/ /*===========================================================================*/ /* Driver local functions. */ /*===========================================================================*/ /** * @brief ADC DMA ISR service routine. * * @param[in] adcp pointer to the @p ADCDriver object * @param[in] flags pre-shifted content of the ISR register * * @notapi */ static void adc_lld_serve_dma_interrupt(ADCDriver *adcp, uint32_t flags) { /* DMA errors handling.*/ if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { /* DMA, this could help only if the DMA tries to access an unmapped address space or violates alignment rules.*/ _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); } else { /* It is possible that the conversion group has already be reset by the ADC error handler, in this case this interrupt is spurious.*/ if (adcp->grpp != NULL) { if ((flags & STM32_DMA_ISR_HTIF) != 0) { /* Half transfer processing.*/ _adc_isr_half_code(adcp); } if ((flags & STM32_DMA_ISR_TCIF) != 0) { /* Transfer complete processing.*/ _adc_isr_full_code(adcp); } } } } #if STM32_ADC_USE_ADC /** * @brief ADC ISR service routine. * * @param[in] adcp pointer to the @p ADCDriver object * @param[in] sr content of the ISR register */ static void adc_lld_serve_interrupt(ADCDriver *adcp, uint32_t sr) { /* It could be a spurious interrupt caused by overflows after DMA disabling, just ignore it in this case.*/ if (adcp->grpp != NULL) { if (sr & ADC_SR_AWD) { /* Analog watchdog error.*/ _adc_isr_error_code(adcp, ADC_ERR_AWD1); } } } #endif /* STM32_ADC_USE_ADC */ #if STM32_ADC_USE_SDADC /** * @brief ADC ISR service routine. * * @param[in] adcp pointer to the @p ADCDriver object * @param[in] isr content of the ISR register */ static void sdadc_lld_serve_interrupt(ADCDriver *adcp, uint32_t isr) { /* It could be a spurious interrupt caused by overflows after DMA disabling, just ignore it in this case.*/ if (adcp->grpp != NULL) { /* Note, an overflow may occur after the conversion ended before the driver is able to stop the ADC, this is why the DMA channel is checked too.*/ if ((isr & SDADC_ISR_JOVRF) && (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) { /* ADC overflow condition, this could happen only if the DMA is unable to read data fast enough.*/ _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW); } } } #endif /* STM32_ADC_USE_SDADC */ /*===========================================================================*/ /* Driver interrupt handlers. */ /*===========================================================================*/ #if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) /** * @brief ADC1 interrupt handler. * * @isr */ CH_IRQ_HANDLER(Vector88) { uint32_t sr; CH_IRQ_PROLOGUE(); sr = ADC1->SR; ADC1->SR = 0; adc_lld_serve_interrupt(&ADCD1, sr); CH_IRQ_EPILOGUE(); } #endif /* STM32_ADC_USE_ADC1 */ #if STM32_ADC_USE_SDADC1 || defined(__DOXYGEN__) /** * @brief SDADC1 interrupt handler. * * @isr */ CH_IRQ_HANDLER(Vector134) { uint32_t isr; CH_IRQ_PROLOGUE(); isr = SDADC1->ISR; SDADC1->CLRISR = isr; sdadc_lld_serve_interrupt(&SDADCD1, isr); CH_IRQ_EPILOGUE(); } #endif /* STM32_ADC_USE_SDADC1 */ #if STM32_ADC_USE_SDADC2 || defined(__DOXYGEN__) /** * @brief SDADC2 interrupt handler. * * @isr */ CH_IRQ_HANDLER(Vector138) { uint32_t isr; CH_IRQ_PROLOGUE(); isr = SDADC2->ISR; SDADC2->CLRISR = isr; sdadc_lld_serve_interrupt(&SDADCD2, isr); CH_IRQ_EPILOGUE(); } #endif /* STM32_ADC_USE_SDADC2 */ #if STM32_ADC_USE_SDADC3 || defined(__DOXYGEN__) /** * @brief SDADC3 interrupt handler. * * @isr */ CH_IRQ_HANDLER(Vector13C) { uint32_t isr; CH_IRQ_PROLOGUE(); isr = SDADC3->ISR; SDADC3->CLRISR = isr; sdadc_lld_serve_interrupt(&SDADCD3, isr); CH_IRQ_EPILOGUE(); } #endif /* STM32_ADC_USE_SDADC3 */ /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ /** * @brief Low level ADC driver initialization. * * @notapi */ void adc_lld_init(void) { #if STM32_ADC_USE_ADC1 /* Driver initialization.*/ adcObjectInit(&ADCD1); ADCD1.adc = ADC1; #if STM32_ADC_USE_SDADC ADCD1.sdadc = NULL; #endif ADCD1.dmastp = STM32_DMA1_STREAM1; ADCD1.dmamode = STM32_DMA_CR_CHSEL(ADC1_DMA_CHANNEL) | STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; nvicEnableVector(ADC1_IRQn, CORTEX_PRIORITY_MASK(STM32_ADC_IRQ_PRIORITY)); #endif #if STM32_ADC_USE_SDADC1 /* Driver initialization.*/ adcObjectInit(&SDADCD1); #if STM32_ADC_USE_ADC SDADCD1.adc = NULL; #endif SDADCD1.sdadc = SDADC1; SDADCD1.dmastp = STM32_DMA2_STREAM3; SDADCD1.dmamode = STM32_DMA_CR_CHSEL(SDADC1_DMA_CHANNEL) | STM32_DMA_CR_PL(STM32_ADC_SDADC1_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; nvicEnableVector(SDADC1_IRQn, CORTEX_PRIORITY_MASK(STM32_ADC_SDADC1_IRQ_PRIORITY)); #endif #if STM32_ADC_USE_SDADC2 /* Driver initialization.*/ adcObjectInit(&SDADCD2); #if STM32_ADC_USE_ADC SDADCD2.adc = NULL; #endif SDADCD2.sdadc = SDADC2; SDADCD2.dmastp = STM32_DMA2_STREAM4; SDADCD2.dmamode = STM32_DMA_CR_CHSEL(SDADC2_DMA_CHANNEL) | STM32_DMA_CR_PL(STM32_ADC_SDADC2_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; nvicEnableVector(SDADC2_IRQn, CORTEX_PRIORITY_MASK(STM32_ADC_SDADC2_IRQ_PRIORITY)); #endif #if STM32_ADC_USE_SDADC3 /* Driver initialization.*/ adcObjectInit(&SDADCD3); #if STM32_ADC_USE_ADC SDADCD3.adc = NULL; #endif SDADCD3.sdadc = SDADC3; SDADCD3.dmastp = STM32_DMA2_STREAM5; SDADCD3.dmamode = STM32_DMA_CR_CHSEL(SDADC3_DMA_CHANNEL) | STM32_DMA_CR_PL(STM32_ADC_SDADC3_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; nvicEnableVector(SDADC3_IRQn, CORTEX_PRIORITY_MASK(STM32_ADC_SDADC3_IRQ_PRIORITY)); #endif } /** * @brief Configures and activates the ADC peripheral. * * @param[in] adcp pointer to the @p ADCDriver object * * @notapi */ void adc_lld_start(ADCDriver *adcp) { /* If in stopped state then enables the ADC and DMA clocks.*/ if (adcp->state == ADC_STOP) { #if STM32_ADC_USE_ADC1 if (&ADCD1 == adcp) { bool_t b; b = dmaStreamAllocate(adcp->dmastp, STM32_ADC_ADC1_DMA_IRQ_PRIORITY, (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, (void *)adcp); chDbgAssert(!b, "adc_lld_start(), #1", "stream already allocated"); dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR); rccEnableADC1(FALSE); /* ADC initial setup, starting the analog part here in order to reduce the latency when starting a conversion.*/ adcp->adc->CR1 = 0; adcp->adc->CR2 = 0; adcp->adc->CR2 = ADC_CR2_ADON; } #endif /* STM32_ADC_USE_ADC1 */ #if STM32_ADC_USE_SDADC1 if (&SDADCD1 == adcp) { bool_t b = dmaStreamAllocate(adcp->dmastp, STM32_ADC_SDADC1_DMA_IRQ_PRIORITY, (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, (void *)adcp); chDbgAssert(!b, "adc_lld_start(), #2", "stream already allocated"); dmaStreamSetPeripheral(adcp->dmastp, &SDADC1->JDATAR); rccEnableSDADC1(FALSE); PWR->CR |= PWR_CR_SDADC1EN; adcp->sdadc->CR2 = 0; adcp->sdadc->CR1 = (adcp->config->cr1 | SDADC_ENFORCED_CR1_FLAGS) & ~SDADC_FORBIDDEN_CR1_FLAGS; adcp->sdadc->CR2 = SDADC_CR2_ADON; } #endif /* STM32_ADC_USE_SDADC1 */ #if STM32_ADC_USE_SDADC2 if (&SDADCD2 == adcp) { bool_t b = dmaStreamAllocate(adcp->dmastp, STM32_ADC_SDADC2_DMA_IRQ_PRIORITY, (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, (void *)adcp); chDbgAssert(!b, "adc_lld_start(), #3", "stream already allocated"); dmaStreamSetPeripheral(adcp->dmastp, &SDADC2->JDATAR); rccEnableSDADC1(FALSE); PWR->CR |= PWR_CR_SDADC2EN; adcp->sdadc->CR2 = 0; adcp->sdadc->CR1 = (adcp->config->cr1 | SDADC_ENFORCED_CR1_FLAGS) & ~SDADC_FORBIDDEN_CR1_FLAGS; adcp->sdadc->CR2 = SDADC_CR2_ADON; } #endif /* STM32_ADC_USE_SDADC2 */ #if STM32_ADC_USE_SDADC3 if (&SDADCD3 == adcp) { bool_t b = dmaStreamAllocate(adcp->dmastp, STM32_ADC_SDADC3_DMA_IRQ_PRIORITY, (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, (void *)adcp); chDbgAssert(!b, "adc_lld_start(), #4", "stream already allocated"); dmaStreamSetPeripheral(adcp->dmastp, &SDADC3->JDATAR); rccEnableSDADC1(FALSE); PWR->CR |= PWR_CR_SDADC3EN; adcp->sdadc->CR2 = 0; adcp->sdadc->CR1 = (adcp->config->cr1 | SDADC_ENFORCED_CR1_FLAGS) & ~SDADC_FORBIDDEN_CR1_FLAGS; adcp->sdadc->CR2 = SDADC_CR2_ADON; } #endif /* STM32_ADC_USE_SDADC3 */ } } /** * @brief Deactivates the ADC peripheral. * * @param[in] adcp pointer to the @p ADCDriver object * * @notapi */ void adc_lld_stop(ADCDriver *adcp) { /* If in ready state then disables the ADC clock.*/ if (adcp->state == ADC_READY) { dmaStreamRelease(adcp->dmastp); #if STM32_ADC_USE_ADC1 if (&ADCD1 == adcp) { adcp->adc->CR1 = 0; adcp->adc->CR2 = 0; rccDisableADC1(FALSE); } #endif #if STM32_ADC_USE_SDADC1 if (&SDADCD1 == adcp) { adcp->sdadc->CR1 = 0; adcp->sdadc->CR2 = 0; rccDisableSDADC1(FALSE); PWR->CR &= ~PWR_CR_SDADC1EN; } #endif #if STM32_ADC_USE_SDADC2 if (&SDADCD2 == adcp) { adcp->sdadc->CR1 = 0; adcp->sdadc->CR2 = 0; rccDisableSDADC2(FALSE); PWR->CR &= ~PWR_CR_SDADC2EN; } #endif #if STM32_ADC_USE_SDADC3 if (&SDADCD3 == adcp) { adcp->sdadc->CR1 = 0; adcp->sdadc->CR2 = 0; rccDisableSDADC3(FALSE); PWR->CR &= ~PWR_CR_SDADC3EN; } #endif } } /** * @brief Starts an ADC conversion. * * @param[in] adcp pointer to the @p ADCDriver object * * @notapi */ void adc_lld_start_conversion(ADCDriver *adcp) { uint32_t mode; const ADCConversionGroup* grpp = adcp->grpp; /* DMA setup.*/ mode = adcp->dmamode; if (grpp->circular) { mode |= STM32_DMA_CR_CIRC; } if (adcp->depth > 1) { /* If the buffer depth is greater than one then the half transfer interrupt interrupt is enabled in order to allows streaming processing.*/ mode |= STM32_DMA_CR_HTIE; } dmaStreamSetMemory0(adcp->dmastp, adcp->samples); dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * (uint32_t)adcp->depth); dmaStreamSetMode(adcp->dmastp, mode); dmaStreamEnable(adcp->dmastp); #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC if (adcp->adc != NULL) #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_ADC { uint32_t cr2 = ADC_CR2_ADON; /* ADC setup.*/ adcp->adc->SR = 0; adcp->adc->LTR = grpp->u.adc.ltr; adcp->adc->HTR = grpp->u.adc.htr; adcp->adc->SMPR1 = grpp->u.adc.smpr[0]; adcp->adc->SMPR2 = grpp->u.adc.smpr[1]; adcp->adc->SQR1 = grpp->u.adc.sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels); adcp->adc->SQR2 = grpp->u.adc.sqr[1]; adcp->adc->SQR3 = grpp->u.adc.sqr[2]; /* ADC conversion start, the start is performed using the method specified in the CR2 configuration, usually ADC_CR2_SWSTART.*/ adcp->adc->CR1 = grpp->u.adc.cr1 | ADC_CR1_AWDIE | ADC_CR1_SCAN; if ((grpp->u.adc.cr2 & ADC_CR2_SWSTART) != 0) cr2 |= ADC_CR2_CONT; adcp->adc->CR2 = grpp->u.adc.cr2 | cr2 | ADC_CR2_DMA | ADC_CR2_ADON; } #endif /* STM32_ADC_USE_ADC */ #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC else if (adcp->sdadc != NULL) #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_SDADC { uint32_t cr2 = SDADC_CR2_ADON; /* Entering initialization mode.*/ adcp->sdadc->CR1 |= SDADC_CR1_INIT; while ((adcp->sdadc->ISR & SDADC_ISR_INITRDY) == 0) ; /* SDADC setup.*/ adcp->sdadc->CONF0R = grpp->u.sdadc.confxr[0]; adcp->sdadc->CONF1R = grpp->u.sdadc.confxr[1]; adcp->sdadc->CONF2R = grpp->u.sdadc.confxr[2]; adcp->sdadc->CONFCHR1 = grpp->u.sdadc.confchr[0]; adcp->sdadc->CONFCHR2 = grpp->u.sdadc.confchr[1]; /* Leaving initialization mode.*/ adcp->sdadc->CR1 &= ~SDADC_CR1_INIT; /* SDADC conversion start, the start is performed using the method specified in the CR2 configuration, usually SDADC_CR2_JSWSTART.*/ if ((grpp->u.sdadc.cr2 & SDADC_CR2_JSWSTART) != 0) cr2 |= SDADC_CR2_JCONT; adcp->sdadc->CR2 = (grpp->u.sdadc.cr2 & ~SDADC_FORBIDDEN_CR2_FLAGS) | cr2; } #endif /* STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC else { chDbgAssert(FALSE, "adc_lld_start_conversion(), #1", "invalid state"); } #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ } /** * @brief Stops an ongoing conversion. * * @param[in] adcp pointer to the @p ADCDriver object * * @notapi */ void adc_lld_stop_conversion(ADCDriver *adcp) { /* Disabling the associated DMA stream.*/ dmaStreamDisable(adcp->dmastp); #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC if (adcp->adc != NULL) #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_ADC { adcp->adc->CR1 = 0; adcp->adc->CR2 = 0; adcp->adc->CR2 = ADC_CR2_ADON; } #endif /* STM32_ADC_USE_ADC */ #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC else if (adcp->sdadc != NULL) #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_SDADC { adcp->sdadc->CR1 = 0; adcp->sdadc->CR2 = 0; adcp->sdadc->CR2 = ADC_CR2_ADON; } #endif /* STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC else { chDbgAssert(FALSE, "adc_lld_stop_conversion(), #1", "invalid state"); } #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ } /** * @brief Calibrates an ADC unit. * @note The calibration must be performed after calling @p adcStart(). * @note For SDADC units it is assumed that the field SDADC_CR2_CALIBCNT * has been * * @param[in] adcp pointer to the @p ADCDriver object * * @api */ void adcSTM32Calibrate(ADCDriver *adcp) { chDbgAssert((adcp->state == ADC_READY) || (adcp->state == ADC_COMPLETE) || (adcp->state == ADC_ERROR), "adcSTM32Calibrate(), #1", "not ready"); #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC if (adcp->adc != NULL) #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_ADC { /* Resetting calibration just to be safe.*/ ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL; while ((ADC1->CR2 & ADC_CR2_RSTCAL) != 0) ; /* Calibration.*/ ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CAL; while ((ADC1->CR2 & ADC_CR2_CAL) != 0) ; } #endif /* STM32_ADC_USE_ADC */ #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC else if (adcp->sdadc != NULL) #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_SDADC { /* Selecting a full calibration in three steps.*/ adcp->sdadc->CR2 = (adcp->sdadc->CR2 & ~SDADC_CR2_CALIBCNT) | SDADC_CR2_CALIBCNT_1; /* Calibration.*/ adcp->sdadc->CR2 |= SDADC_CR2_STARTCALIB; while ((adcp->sdadc->ISR & SDADC_ISR_EOCALF) == 0) ; /* Clearing the EOCALF flag.*/ adcp->sdadc->CLRISR |= SDADC_ISR_CLREOCALF; } #endif /* STM32_ADC_USE_SDADC */ #if STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC else { chDbgAssert(FALSE, "adcSTM32Calibrate(), #2", "invalid state"); } #endif /* STM32_ADC_USE_ADC && STM32_ADC_USE_SDADC */ } #if STM32_ADC_USE_ADC || defined(__DOXYGEN__) /** * @brief Enables the TSVREFE bit. * @details The TSVREFE bit is required in order to sample the internal * temperature sensor and internal reference voltage. * @note This is an STM32-only functionality. * * @api */ void adcSTM32EnableTSVREFE(void) { ADC1->CR2 |= ADC_CR2_TSVREFE; } /** * @brief Disables the TSVREFE bit. * @details The TSVREFE bit is required in order to sample the internal * temperature sensor and internal reference voltage. * @note This is an STM32-only functionality. * * @api */ void adcSTM32DisableTSVREFE(void) { ADC1->CR2 &= ~ADC_CR2_TSVREFE; } /** * @brief Enables the VBATE bit. * @details The VBATE bit is required in order to sample the VBAT channel. * @note This is an STM32-only functionality. * * @api */ void adcSTM32EnableVBATE(void) { SYSCFG->CFGR1 |= SYSCFG_CFGR1_VBAT; } /** * @brief Disables the VBATE bit. * @details The VBATE bit is required in order to sample the VBAT channel. * @note This is an STM32-only functionality. * * @api */ void adcSTM32DisableVBATE(void) { SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_VBAT; } #endif /* STM32_ADC_USE_ADC */ #if 0 || defined(__DOXYGEN__) /** * @brief Configures the calibration sequence. * @note TODO - UPDATE * @param ADCDriver* one of &SDADCD1, &SDADCD2, &SDADCD3 * @param SDADC_CalibrationSequence: Number of calibration sequence to be performed. * This parameter can be one of the following values: * @arg SDADC_CalibrationSequence_1: One calibration sequence will be performed * to calculate OFFSET0[11:0] (offset that corresponds to conf0) * @arg SDADC_CalibrationSequence_2: Two calibration sequences will be performed * to calculate OFFSET0[11:0] and OFFSET1[11:0] * (offsets that correspond to conf0 and conf1) * @arg SDADC_CalibrationSequence_3: Three calibration sequences will be performed * to calculate OFFSET0[11:0], OFFSET1[11:0], * and OFFSET2[11:0] (offsets that correspond to conf0, conf1 and conf2) * @retval None */ void sdadcSTM32Calibrate(ADCDriver* adcp, SDADC_NUM_CALIB_SEQ numCalibSequences, ADCConversionGroup* grpp) { uint32_t SDADCTimeout = 0; uint32_t tmpcr2 = 0; if (!(adcp == &SDADCD1 || adcp == &SDADCD2 || adcp == &SDADCD3)) return; sdadcSTM32SetInitializationMode(adcp, true); /* SDADC setup.*/ adcp->sdadc->CR2 = grpp->ll.sdadc.cr2; adcp->sdadc->CONF0R = grpp->ll.sdadc.conf0r; adcp->sdadc->CONF1R = grpp->ll.sdadc.conf1r; adcp->sdadc->CONF2R = grpp->ll.sdadc.conf2r; adcp->sdadc->CONFCHR1 = grpp->ll.sdadc.confchr1; adcp->sdadc->CONFCHR2 = grpp->ll.sdadc.confchr2; sdadcSTM32SetInitializationMode(adcp, false); /* configure calibration to be performed on conf0 */ /* Get SDADC_CR2 register value */ tmpcr2 = adcp->sdadc->CR2; /* Clear the SDADC_CR2_CALIBCNT bits */ tmpcr2 &= (uint32_t) (~SDADC_CR2_CALIBCNT); /* Set the calibration sequence */ tmpcr2 |= numCalibSequences; /* Write in SDADC_CR2 and start calibration */ adcp->sdadc->CR2 = tmpcr2 | SDADC_CR2_STARTCALIB; /* Set calibration timeout: 5.12 ms at 6 MHz in a single calibration sequence */ SDADCTimeout = SDADC_CAL_TIMEOUT; /* wait for SDADC Calibration process to end */ while (((adcp->sdadc->ISR & SDADC_ISR_EOCALF) == 0) && (--SDADCTimeout != 0)); if(SDADCTimeout == 0) { /* Calib timeout */ port_halt(); return; } /* cleanup by clearing EOCALF flag */ adcp->sdadc->CLRISR |= SDADC_ISR_CLREOCALF; } #endif /* STM32_ADC_USE_SDADC */ #endif /* HAL_USE_ADC */ /** @} */