Blue Pill - ADC

Table des matières

TL;DR

Mise en oeuvre d'un Analog-to-Digital Converter.

Le matériel

Un potentiomètre (10K dans mon cas) en plus du montage feu rouge quelque peu remanié pour libérer les broches ADC. Les LEDs sont connectées aux broches B12 à B14 et le bouton poussoir n'est pas utilisé. Le projet consiste à allumer une des 3 LEDs suivant la position du potentiomètre.

ADC

La procédure à suivre pour lancer une conversion est la suivante:
  1. initialisation de l'ADC
  2. initialisation d'un canal
  3. lancement de la conversion

Un peu de code

#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: Chaque canal possède une durée d'échantillonage exprimée en nombre de cycle. Parce qu'il est rare de n'avoir qu'une seule conversion à faire, les conversions sont ordonnées au sein d'une séquence. Avec ici une seule conversion, la taille par défaut de la séquence (1) est correcte. Une séquence comporte au maximum 16 entrées et se décompose en 3 registres: SQR 1..3, la première entrée est dans le registre 3, la 16 ème dans le registre 1. Par défaut c'est le canal 0 qui est utilisé c'est pourquoi presque tous les tutoriels utilisent PA0.
...
#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

A suivre

Conversions continues et interruptions