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.

ADCx_CR1

ADCx_CR2

ADCx_SQR3

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;

Code complet

#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;
	}
}