Programmation de microcontrôleurs STM32: Timer
Cette page vise à présenter une première utilisation des interruptions sur un microcontrôleur STM32. À la suite de cette page, un développeur logiciel doit être capable:
- De trouver les informations nécessaires dans une datasheet pour l’utilisation des timers,
- De programmer les différents registres pour différentes utilisations des timers (LED, bouton-poussoir etc.),
- D’adapter le fonctionnement d’un timer à l’utilisation d’un capteur externe.
Détection d’un obstacle
Dans cette partie, l’objectif va être de concevoir un système de détection d’obstacle. Le fonctionnement recherché sera le suivant: faire clignoter une LED de plus en plus rapidement selon la distance d’un obstacle. Pour cela, nous allons décomposer le problème en trois sous-arties:
- Le clignotement de la LED en utilisant un timer,
- La lecture d’un capteur pour récupérer la distance,
- La mise en place de l’application finale.
Clignotement d’une LED
Lors des expérimentations précédentes, nous avons utilisé différentes stratégies pour faire clignoter une LED:
- Inversion lors de l’appui sur un bouton poussoir (scrutation),
- Inversion lors de l’appui sur un bouton poussoir (interruption),
- Boucle d’exécution avec l’ajout d’un délai avant l’inversion.
Si toutes ces solutions sont fonctionnelles, aucune ne permet d’avoir une prise en compte précise du temps.
Il n’est donc pas possible de réaliser un clignotement régulier et d’une durée parfaitement maîtrisée.
Pour cela, nous allons donc utiliser les timers mis à disposition au sein de la carte STM32.
Plus particulièrement, nous utiliserons ici le timer TIM2.
Périodique
Pour commencer, nous allons voir comment utiliser un timer pour déclencher périodiquement une routine responsable de la gestion de la LED.
1 le bit correspondant du registre RCC_APB1ENR.Documentation
Le registre RCC_APB1ENR est détaillé page 146 du manuel de référence (Chapitre 6: Reset and clock control (RCC)).
CR1 du TIM2 permet de configurer son fonctionnement.
Notamment, les champs CEN, DIR, CMS et ARPE permettent respectivement d’activer le compteur, de sélectionner la direction du compteur (croissant ou décroissant), l’alignement (aligné sur les fronts ou centré) et de bufferiser le préchargement.
Modifiez le registre pour que le compteur soit éteint, croissant, aligné sur les fronts et que le préchargement soit bufferisé.
On l’activera seulement une fois la configuration terminée.Documentation
Le registre TIM2_CR1 est détaillé page 549 du manuel de référence (Chapitre 17: General-purpose timers (TIM2 to TIM5)).
L’utilisation d’un buffer pour le pré-chargement permet de s’assurer que le compteur ne change pas de valeur maximale n’importe quand.
Ainsi, c’est seulement lorsqu’un débordement (overflow) aura lieu que la nouvelle valeur sera prise en compte.
UIE du registre DIER.Documentation
Le registre TIM2_DIER est détaillé page 554 du manuel de référence (Chapitre 17: General-purpose timers (TIM2 to TIM5)).
ARR et PSC pour avoir le fonctionnement attendu.
On considèrera que chacun de ces registres ne fait que 16 bits.Documentation
Les registres TIM2_PSC et TIM2_ARR sont détaillés page 564 du manuel de référence (Chapitre 17: General-purpose timers (TIM2 to TIM5)).
Aide supplémentaire (si nécessaire)
90,000,000 s’écrit sur 27 bits.
Chacun des regitres étant sur 16 bits, il va falloir diviser la fréquence de l’horloge avec PSC pour obtenir une valeur inférieur à $2^{16}$ à stocker dans ARR.
TIM2_PSC et TIM2_ARR avant d’activer le buffer avec le bit ARPE.TIM2 est associé à l’interruption numéro 28.
De la même manière que pour les expérimentations sur les interruptions, activez la gestion de cette interruption.Écrivez à présent la routine qui inversera l’état de la LED à chaque fois qu’elle sera appelée.
Pensez également à réinitialiser l’interruption une fois qu’elle a été traitée.
Cela peut être fait en remettant à 0 le bit UIF du registre TIM2_SR.
Comme précédemment, le système est pré-configuré pour que la routine d’interruption liée au timer TIM2 soit appelée TIM2_IRQHandler.
main à présent, ajoutez le code pour initialiser l’état de la LED à 0.CNT à la valeur 0
Vérifiez ensuite le bon fonctionnement de votre système.Aide supplémentaire (si nécessaire)
En cas d’erreur, utilisez le mode de debogage de l’IDE pour visualiser l’état des registres du TIM2.
Assurez-vous dans l’ordre:
- que le bit
CENdu registreCR1est bien activé, - que le registre
CNTs’incrémente correctement, - que les valeurs des registres
ARRetPSCont bien été calculées, - que les autres registres de configuration ont les valeurs attendus.
ARR/PSC sur 16 bits ?Automatique
Dans l’exercice ci-dessus, le clignotement s’effectue avec l’intervention d’une interruption. Dans un cas idéal pour notre application finale, on souhaiterait pouvoir contrôler le clignotement de la LED en modifiant seulement une valeur de configuration: l’ajustement serait alors automatique.
Or, les timers STM32 (et notamment le TIM2) intègrent tous les éléments pour effectuer ce clignotement de manière autonome, sans intervention du logiciel après configuration.
À présent, plutôt que d’utiliser une interruption, on va donc voir comment reconfigurer TIM2 pour obtenir ce fonctionnement.
Pour cela, nous allons utiliser le mécanisme de PWM (Pulse Width Modulation) pour contrôler directement la sortie à l’aide du canal 1 du timer.
TIM2 effectuée précédemment.
Désactivez l’interruption.GPIOA (registre RCC_AHB1ENR).
Modifiez ensuite le mode de la broche 5 du GPIOA pour qu’elle utilise le mode de fonction alternative (registre GPIOA_MODER).
Modifiez ensuite le registre des fonctions alternatives (GPIOA_AFR) pour connecter la broche 5 au TIM2.Documentation
L’ensemble des connections pour les fonctions alternatives sont disponibles page 57 de la datasheet (Chapitre 3: Pinout and pin description).
TIM2_CCMR1.
Dans notre cas, on souhaite configurer le canal en tant que sortie, utilisez le mode 1 de PWM et activer le préchargement (preload).TIM2_DIER, désactivez l’interruption pour le canal 1.TIM2_CCER.
Modifiez le registre pour activer la sortie sur l’état haut.CNT, PSC, ARR et CCR1.CNT, PSC, ARR et CCR1 pour obtenir le clignotement souhaité.Pour notre application finale, nous aurons besoin de trois fonctions:
TIM2_Initqui configure et initialise l’état du timerTIM2au départ,TIM2_Startqui lance le fonctionnement du compteur,TIM2_Frequencyqui prend en paramètre une distance en cm et fait clignoter la LED plus ou moins rapidement selon la valeur reçue. Pour information, la distance calculée mesurée peut aller de 2.5cm à 425cm. En-dessous de cet intervalle, on laissera la LED constamment allumé (ou clignotement très rapide). Au-dessus de cet intervalle, on laisse la LED constamment éteinte (ou clignotement très lent). Une stratégie envisageable est simplement de modifier la valeur du registreCCR1selon la distance et ainsi faire varier le rapport cyclique.
Préparez ces trois fonctions pour la suite du projet. Testez ensuite leur bon fonctionnement.
float).
On effectuera donc des arrondis: par exemple 3 au lieu de 2.5cm.Lecture d’un capteur HC-SR04

Dans notre application de détection d’obstacle, nous avons besoin d’un mécanisme pour mesurer la distance entre notre système et un éventuel objet. Pour cela, nous allons utiliser le capteur de mesure de distance par ultrasons HC-SR04. Ce composant possède deux signaux:
- Une entrée
Trigutilisée pour commander la mesure. Chaque demande de mesure est effectuée en mettant le signalTrigà l’état haut pour au moins 10µs. - Une sortie
Echoqui est la réponse sous forme d’impulsion. Cette impulsion est à l’état haut entre 0.15ms et 25ms. Lorsque cette valeur dépasse 38ms, alors cela signifie qu’aucun obstacle n’a été détecté. On s’assurera qu’une nouvelle mesure n’est commandée que toutes les 60ms. La distance est ensuite déduite selon le calcul suivant: $distance = (T / 58.8235)$ avec la distance en cm et T la durée de l’impulsion en µs.
L’objectif des prochaines étapes va être de piloter ce capteur en utilisant le timer TIM3.
Notamment, nous allons utiliser les canaux 1, 3 et 4 du TIM3.
Dans l’ordre, l’objectif sera alors de lancer les opérations suivantes:
- Lancer d’un nouveau cycle de mesure. Placer à
1le signalTrigen utilisant le canal 1. - Au bout de 10µs, remettre le signal
Trigà0. - Sauvegarder le moment du front montant du signal
Echoavec le canal 3. - Sauvegarder le moment du front descendant du signal
Echoavec le canal 4. - Adapter la fréquence en fonction de la mesure à la fin du cycle de mesure.
- Relancer un nouveau cycle de mesure.
Configuration générale du TIM3
TIM2, activez l’horloge pour le timer TIM3 dans le registre RCC_APB1ENR.
De même, modifiez le registre pour que le compteur soit éteint, croissant, aligné sur les fronts et que le préchargement soit bufferisé.TIM3_DIER.PSC et ARR en conséquence.Canal 1: génération du signal Trig
Pour qu’une mesure soit effectuée, une demande doit avoir été faite sur le signal Trig.
Ainsi, détecter dès que possible un obstacle implique d’effectuer de manière régulière des mesures.
Pour cela, nous allons à nouveau utiliser le principe de PWM (Pulse Width Modulation) des timers.
La configuration attendue sera finalement en grande partie similaire à celle du clignotement automatique de la LED: seuls la période et le rapport cyclique seront différents.
Trig du capteur comme sortie de notre timer, il est nécessaire de la rediriger par le biais des fonctions alternatives des GPIO.
À partir des spécifications de la carte Nucleo et du microcontrôleur, déduisez quelles broches peuvent être utilisées pour relier Trig à la sortie du canal 1 du timer TIM3.TIM3.TIM3_CCMR1.
Dans notre cas, on souhaite configurer le canal en tant que sortie, utilisez le mode 1 de PWM et activer le préchargement (preload).TIM3_DIER, désactivez l’interruption pour le canal 1.TIM3_CCER.
Modifiez le registre pour activer la sortie sur l’état haut.Trig.
En considérant les valeurs précédemment choisies d’ARR et PSC, à quelle valeur le signal Trig doit être mis à 0 / à 1.
Modifiez le registre TIM3_CCR1 en fonction de la valeur voulue.Canaux 3/4: réception du signal Echo
Echo du capteur comme entrée de notre timer, il est nécessaire de la rediriger par le biais des fonctions alternatives des GPIO.
À partir des spécifications de la carte Nucleo et du microcontrôleur, déduisez quelles broches peuvent être utilisées pour relier Echo à l’entrée des canaux 3 ou 4 du timer TIM3.TIM3.Trig.
D’après l’architecture du timer, pourquoi est-il alors nécessaire d’utiliser les canaux 3 et 4 pour faire les détections distinctes des fronts montants et descendants plutôt que les canaux 2 et 3 ?
Expliquez avec un schéma simplifié.Documentation
L’architecture du timer TIM3 est détaillée page 512 du manuel de référence (Chapitre 17: General-purpose timers (TIM2 to TIM5)).
TIM3_CCMR2.
Dans notre cas, on souhaite configurer le canal en tant qu’entrée, sans filtre, sans prescaler et avec le signal IC3 connecté à TI3 (voir l’architecture).
De même, désactivez l’interruption correspondante dans TIM3_DIER.TIM3_CCER.
Modifiez le registre pour activer la capture sur front montant du canal 3.TIM3_CCMR2.
Dans notre cas, on souhaite configurer le canal en tant qu’entrée, sans filtre, sans prescaler et avec le signal IC4 connecté à TI3 (voir l’architecture).
En revanche, la détection d’un front descendant implique que la mesure est terminée.
Il est donc nécessaire d’activer l’interruption correspondante dans TIM3_DIER.TIM3_CCER.
Modifiez le registre pour activer la capture sur front descendant du canal 4.TIM3 s’appelle TIM3_IRQHandler.
Modifiez cette routine pour qu’elle calcule le nombre de cycles écoulés entre les deux fronts et stocke le résultat dans une variable globale.
Pensez également à réinitialiser l’état du bit d’interruption dans le registre TIM3_SR.Montage et test
Nous avons à présent configué le timer TIM3 afin qu’il s’interface avec le capteur HC-SR04.
L’objectif va être de tester le bon fonctionnement du système et de préparer les fonctions pour l’application finale.
Pour notre application finale, nous aurons besoin de trois fonctions:
TIM3_Initqui configure et initialise l’état du timerTIM3au départ,TIM3_Startqui lance le fonctionnement du timer,TIM3_IRQHandlerqui est appelée par une interruption pour calculer le temps entre les deux fronts.
Préparez ces trois fonctions pour la suite du projet.
Testez ensuite leur bon fonctionnement.
TIM3_Init et TIM3_Start doivent être exécutées avant la boucle while.
À l’aide du mode de debogage, vérifiez la valeur renvoyée par TIM3_IRQHandler après plusieurs appels.
Application finale
Nous disposons à présent de tous les éléments pour notre application de détecteur d’obstacle.
Il nous reste à appeler les différentes fonctions dans notre main afin de les faire fonctionner ensemble.
main pour qu’elle appelle d’abord les fonctions d’initialisations puis lance les compteurs.
Dans la boucle TIM3_IRQHandler, ajoutez le code nécessaire au calcul de la distance en cm à partir de la valeur calculée et appelez la fonction TIM2_Frequency.HCSR04_Init() responsable d’effectuer le paramétrage entier d’un timer passé en paramètre.
Cette fonction devra réutiliser vos fonctions définies précédemment.Plusieurs optimisations sont imaginables au niveau du code conçu jusqu’à maintenant:
- Utiliser un seul canal pour la détection des deux fronts,
- N’appeler
TIM2_Frequencydepuis la bouclewhileque lorsque c’est nécessaire, - Avoir une boucle
whilecomplètement vide, - …
Proposez et/ou testez une ou plusieurs de ces possibles optimisations.
Communication avec une télécommande infrarouge

Tout comme le capteur HC-SR04, de nombreux composants utilisent des durées variables d’état haut / bas pour transmettre des informations. Par exemple, c’est le cas du modèle standard de télécommande infrarouge illustré ci-dessous. Plus précisément, celui-ci s’appuye sur le protocole NEC, qui transmet les bits d’informations sous la forme d’impulsions.
