/*************************************************************************** * Copyright (C) 2024 - 2025 by Silvano Seva IU2KWO * * * * This program 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. * * * * This program 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 * ***************************************************************************/ #include "stm32h7xx.h" #include #include #include "adc_stm32.h" int adcStm32_init(const struct Adc *adc) { /* * Configure ADC for synchronous clock mode (clocked by AHB clock), divided * by 4. This gives an ADC clock of 50MHz when AHB clock is 200MHz. * * NOTE: ADC1 and ADC2 share the same clock tree! */ switch((uint32_t) adc->priv) { case ADC1_BASE: case ADC2_BASE: RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; __DSB(); ADC12_COMMON->CCR = ADC_CCR_CKMODE_1 | ADC_CCR_CKMODE_0; break; case ADC3_BASE: RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; __DSB(); ADC3_COMMON->CCR = ADC_CCR_CKMODE_1 | ADC_CCR_CKMODE_0; break; default: return -EINVAL; break; } ADC_TypeDef *pAdc = ((ADC_TypeDef *) adc->priv); // Enable ADC voltage regulator, enable boost mode. Wait until LDO regulator // is ready. pAdc->CR = ADC_CR_ADVREGEN | ADC_CR_BOOST_1 | ADC_CR_BOOST_0; while((pAdc->ISR & ADC_ISR_LDORDY) == 0) ; // Start calibration, both offset and linearity. pAdc->CR |= ADC_CR_ADCAL | ADC_CR_ADCALLIN; while((pAdc->CR & ADC_CR_ADCAL) != 0) ; /* * ADC clock is 50MHz. We set the sample time of each channel to 387.5 ADC * cycles, giving a total conversion time of ~7us. */ pAdc->SMPR2 = 0x36DB6DB6; pAdc->SMPR1 = 0x36DB6DB6; // Finally,turn on the ADC pAdc->CR |= ADC_CR_ADEN; while((pAdc->ISR & ADC_ISR_ADRDY) != 0) ; if(adc->mutex != NULL) pthread_mutex_init((pthread_mutex_t *) adc->mutex, NULL); return 0; } void adcStm32_terminate(const struct Adc *adc) { // A conversion may be in progress, wait until it finishes if(adc->mutex != NULL) pthread_mutex_lock((pthread_mutex_t *) adc->mutex); ((ADC_TypeDef *) adc->priv)->CR = 0; switch((uint32_t) adc->priv) { case ADC1_BASE: case ADC2_BASE: if((ADC1->CR == 0) && (ADC2->CR == 0)) RCC->AHB1ENR &= ~RCC_AHB1ENR_ADC12EN; __DSB(); break; case ADC3_BASE: RCC->AHB4ENR &= ~RCC_AHB4ENR_ADC3EN; __DSB(); break; default: break; } if(adc->mutex != NULL) pthread_mutex_destroy((pthread_mutex_t *) adc->mutex); } uint16_t adcStm32_sample(const struct Adc *adc, const uint32_t channel) { if(channel > 16) return 0; ADC_TypeDef *pAdc = ((ADC_TypeDef *) adc->priv); pAdc->SQR1 = channel << ADC_SQR1_SQ1_Pos; pAdc->PCSEL = 1 << channel; pAdc->CR |= ADC_CR_ADSTART; while((pAdc->ISR & ADC_ISR_EOC) == 0) ; return pAdc->DR; }