Cet article présente la configuration de base d’un ADC sur le STM32F446. Dans un premier exemple, l’ADC1 sera configuré sur le canal 0 pour une lecture démarrée dans l’interruption du timer 2. Dans un second exemple, la lecture sera démarrée automatiquement par l’événement CC2 (Capture/Compare canal2) du timer 2. Tous les exemples utiliseront l’interruption de fin de conversion. Le code complet se trouve à la fin de l’article.
Avant de débuter les exemples, nous allons regarder d’un peu plus près les registres à considérer pour configurer l’ADC1.
- RCC
- Pour l’horloge de l’ADC
- CR1 (control register 1)
- Résolution (nous utiliserons 8bits mais l’ADC peut en fournir 12)
- Activation de l’interruption de fin de conversion
- CR1 (control register 2)
- Déclenchement manuel de la lecture
- Activation de l’ADC
- SQR3 ( regular sequence register 3)
- Numéro du canal à lire
- SR (status register)
- Pour désengager le bit EOC après une conversion complétée



Exemple 1 : Lecture ADC1 ch0 avec déclenchement manuel
Le canal 0 du ADC 1 est relié à PA0 comme le montre cette partie de la table 10 de la feuille de spécification.

Configuration de PA0
// Active horloge sur GPIOA RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Configure PA1 en sortie GPIOA->MODER |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER0_0;
Initialisation de ADC1
// Activation de l’horloge sur ADC1 RCC->APB2ENR|= RCC_APB2ENR_ADC1EN; // Résolution à 8 bits + interruption EndOfConversion ADC1->CR1 |= ADC_CR1_RES_1 | ADC_CR1_EOCIE; // Active l’ADC1 ADC1->CR2 |= ADC_CR2_ADON; // Canal à lire = canal 0 ADC1->SQR3 |= 0; // Active IRQ du ADC NVIC_EnableIRQ(ADC_IRQn);
Gestion de l’interruption TIM2
extern "C" // si en c++ void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; // Démarre la lecture ADC1 ADC1->CR2 |= ADC_CR2_SWSTART; } }
Gestion de l’interruption ADC1
extern "C" // si en c++ void ADC_IRQHandler(void) { if (ADC1->SR & ADC_SR_EOC) { ADC1->SR &= ~ADC_SR_EOC; adcValue = ADC1->DR; GPIOA->ODR ^= (1<<5); } }
Exemple 2 : Lecture ADC1 ch0 avec déclenchement automatisé
Plutôt que de démarrer la lecture de l’ADC manuellement dans le timer 2, nous allons utiliser l’événement CC2 du timer 2 pour le faire automatiquement.
Initialisation de l'ADC
// Activation de l’horloge sur ADC1 RCC->APB2ENR|= RCC_APB2ENR_ADC1EN; // Résolution à 8 bits + interruption EndOfConversion ADC1->CR1 |= ADC_CR1_RES_1 | ADC_CR1_EOCIE; // Active l’ADC1 avec déclenchement sur CC2 du timer 2 ADC1->CR2 |= ADC_CR2_ADON |ADC_CR2_EXTEN_0; ADC1->CR2 |= ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1; // Canal à lire = canal 1 ADC1->SQR3 |= 1; // Active IRQ du ADC NVIC_EnableIRQ(ADC_IRQn);
Code d’initialisation de CC2 TIM2
// Active horloge sur TIM2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = (SystemCoreClock>>1) / 10000 - 1;
TIM2->ARR = 999;
// PWM2
TIM2->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 ;
TIM2->CCER = TIM_CCER_CC2E; // Active sortie CH2
TIM2->CCR2 = TIM2->ARR >>1; // Duty cycle à 50%
// Active registre ARR et démarre le compteur
TIM2->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;
#include "stm32f4xx.h" bool refreshADC = false; uint8_t adcValue = 0; int main(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; GPIOA->MODER |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER0_0; // Active horloge sur TIM2 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; TIM2->PSC = (SystemCoreClock>>1) / 1000000 - 1; TIM2->ARR = 24; TIM2->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 ;// PWM2 TIM2->CCER = TIM_CCER_CC2E; // Active sortie CH2 TIM2->CCR2 = TIM2->ARR >>1; // Duty cycle à 50% // Active l'interruption (IRQ) du TIM2 NVIC_EnableIRQ(TIM2_IRQn); TIM2->DIER = TIM_DIER_UIE; // Active registre ARR et démarre le compteur TIM2->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN; // Initialisation de l'ADC1 RCC->APB2ENR|= RCC_APB2ENR_ADC1EN; ADC1->CR1 |= ADC_CR1_RES_1 | ADC_CR1_EOCIE; ADC1->CR2 |= ADC_CR2_ADON ; ADC1->CR2 |= ADC_CR2_ADON |ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1; ADC1->SQR3 |= 0; NVIC_EnableIRQ(ADC_IRQn); while(1) { if(refreshADC) { refreshADC = false; // Faire qqchose avec la donnée } } } extern "C" void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_UIF) // if UIF flag is set { TIM2->SR &= ~TIM_SR_UIF; // clear UIF flag refreshADC = true; //ADC1->CR2 |= ADC_CR2_SWSTART; // Pour l'exemple 1 } } extern "C" void ADC_IRQHandler(void) { if (ADC1->SR & ADC_SR_EOC) // if UIF flag is set { ADC1->SR &= ~ADC_SR_EOC; // clear UIF flag adcValue = ADC1->DR; } }