Mise en oeuvre de l'ADC avec le DMA
Matériel requis
- un ordinateur avec un terminal (putty, termite,...)
- une carte nucleo-L073RZ ou STM32F446RE ou autre
- Un potentiomètre de 10kOhm par exemple relié à PA0 ou la carte IHM (mbed-application-shield)
Création du projet - Côté configuration
Configuration USART
On configure l'USART2 en 115200 baud.
Configuration ADC
Les choses importantes ici sont la résolution (12 bits), on aurait pu prendre moins.
Les paramètre Continous conversion Mode et Discontinuous Conversion Mode peuvent être indifféremment Enable ou Disable.
Le paramètre DMA Continuous Requests peut être aussi indifféremment Enable ou Disable.
Notons que les événements (interrupt) issues de l'ADC ne sont pas autorisées. Ce sera le contrôleur DMA qui se chargera d'informer d'une fin de transfert d'un échantillon vers l'emplacement mémoire où il sera stocké.
Configuration du DMA pour l'ADC
Comme dans cet exemple, on n'échantillonne qu'une seule entrée et on ne veut à l'instant t qu'une seule valeur, on peut être soit en mode Circular soit Normal, on peut cocher Increment Address ou non.
Dans notre cas, on aura une précision sur 12 bits donc on prend des word (16 bits).
Programmation - Côté code donc...
A rebrousse poil, cela donne :
Récupération des échantillons
Dans main.c et dans une section genre / USER CODE BEGIN 4 /
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
ADC_FLAG=TRUE;
}
Dans la fonction main de main.c :
if (ADC_FLAG == TRUE)
{
ADC_FLAG = FALSE;
sprintf(message,"%ld\n\r", /*(uint16_t)*/pdata); //on peut caster mais pas obligé
HAL_UART_Transmit(&huart2, message , sizeof(message), HAL_MAX_DELAY);
HAL_ADC_Start_DMA(&hadc, &pdata, 1);
}
On aura évidemment fait auparavant :
uint8_t ADC_FLAG;
#define TRUE 1
#define FALSE 0
uint32_t pdata = 0; //very important de le mettre en uint32_t
uint8_t message[10]; //ça c'est pour envoyer une chaîne au PC
et in fine dans le main de main.c
uint8_t welcomeMessage[]="Test ADC & DMA sur PA0 8 bits 115200 bauds\r\n"; //un message d'accueil ne fait jamais de mal
HAL_UART_Transmit(&huart2, welcomeMessage , sizeof(welcomeMessage), HAL_MAX_DELAY); //et on le balance
HAL_ADC_Start_DMA(&hadc, &pdata, 1); //on lance les hostivités
while (1)
{
if (ADC_FLAG == TRUE) //sur fin de conversion (merci le callback AL_ADC_ConvCpltCallback)
{
ADC_FLAG = FALSE;
sprintf(message,"%ld\n\r", /*(uint16_t)*/pdata); //on peut caster mais pas obligé
HAL_UART_Transmit(&huart2, message , sizeof(message), HAL_MAX_DELAY);
HAL_ADC_Start_DMA(&hadc, &pdata, 1); // et rebelote
}
}
}
Attention !
Seule cet ordre fonctionne. Si on s'amuse à mettre MX_DMA_Init(); après MX_ADC_Init(); ça déraille complètement, on n'est plus sur 12 bits mais sur 8... Bienvenue en STM32zarbie.
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_DMA_Init();
MX_ADC_Init();
void MX_ADC_Init(void) (extrait)
Si on ne veut numériser qu'un seul échantillon sous 8 bits ou plus, alors on peut se mettre en circular buffer ou non.
Avoir à l'esprit que les conversions seront lancées avec la fonction :
HAL_ADC_Start_DMA(&hadc, &pdata, 1);
Cette fonction signifie que le contrôleur DMA va lancer la conversion dont l'échantillon sera stockée dans pdata.
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.DMAContinuousRequests = ENABLE;