#define ADC_ADC_PRESCALER (RCC->CFGR & RCC_CFGR_ADCPRE_Msk)
#define ADC_TWO_CLOCK_CYCLES 2
void
adc_init(ADC_TypeDef *adc, uint32_t cr2) {
uint8_t cycles;
adc->CR2 |= cr2;
adc->CR2 |= ADC_CR2_ADON; // wake up
// P.223: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) for at least two ADC clock cycles.
cycles = ADC_TWO_CLOCK_CYCLES * ((1 + ADC_ADC_PRESCALER) * 2); // SysClk != AdcClk
while (cycles--) { // more than 2 ADC clock cycles
__NOP();
}
adc->CR2 |= ADC_CR2_CAL;
while (adc->CR2 & ADC_CR2_CAL);
}
void
adc_init_channel(ADC_TypeDef *adc, uint8_t channel, enum ADC_NB_CYCLES nb_cycle) {
...
}
uint16_t
adc_read(ADC_TypeDef *adc) {
/*
Note: If any other bit in this register apart from ADON is changed at the same time, then
conversion is not triggered. This is to prevent triggering an erroneous conversion.
adc->CR2 |= ADC_CR2_SWSTART | ADC_CR2_ADON; # => Fail !!!
*/
adc->CR2 |= ADC_CR2_ADON;
adc->CR2 |= ADC_CR2_SWSTART;
while (!(adc->SR & ADC_SR_EOC)); // wait for End Of Conversion
return (uint16_t)adc->DR;
}
Le canal correspond à la PIN du GPIO sur laquelle est connectée le capteur:
...
#define POTENTIOMETER_ADC ADC1
#define POTENTIOMETER_CHANNEL 1
...
adc_init(POTENTIOMETER_ADC, POTENTIOMETER_CR2);
adc_init_channel(POTENTIOMETER_ADC, POTENTIOMETER_CHANNEL, ADC_239_5);
POTENTIOMETER_ADC->SQR3 |= POTENTIOMETER_CHANNEL << 0;
L'initialisation du canal consiste à passer la broche en entrée analogique et à définir le temps d'échantillonage en nombre de cycle. Je n'ai pas beaucoup de raison d'utiliser le maximum de cycles, la précision n'ayant aucune importance ici.
Le code complet: https://gitlab.com/dsx/blue-pill/-/tree/master/stm32f103c8t6/04_adc