À vous de jouer !
Vous allez réaliser un petit jeu. Un joueur doit appuyer sur un bouton quand une LED s’allume et avant qu'elle ne s'éteigne. Le moment auquel s’allume la LED est aléatoire. Le joueur aura seulement 300 ms pour réagir, la LED s’éteignant ensuite. Si le joueur gagne, alors la LED se met à clignoter 4 fois par seconde.
Le chronogramme ci-dessous reprend ce fonctionnement. Pendant la phase notée 1, le système s’initialise, puis après une durée aléatoire, la LED s’allume pendant 300 ms (2 sur le schéma). Si le joueur appuie sur le bouton (événement noté 3) quand la LED est allumée alors elle se met à clignoter régulièrement (phase 4), sinon la LED s’éteint avant d’être de nouveau allumée après une durée aléatoire.
Pour cette activité, vous ne réaliserez que la première séquence avec la LED qui s'allume de manière aléatoire. Vous réaliserez la suite dans une autre activité.
Description du système
Le système utilise uniquement :
la LED verte (nommée LD2) de la carte Nucleo : elle est branchée sur la broche 5 du port A (le mode de configuration de la broche de sortie doit être output push-pull),
le bouton USER (bouton bleu) : le bouton est connecté sur la broche 13 du port C (la broche devra être configurée en input floating).
Pour ce qui est des périphériques du microcontrôleur, vous pouvez utiliser tout ce que vous souhaitez.
Afin de faciliter la correction et de vous aider à structurer votre code, récupérez le fichier main_v1.c ci-joint. Vous devez obligatoirement écrire le code des fonctions :
void configure_gpio_pa5(void) ;
void configure_gpio_pc13(void) ;
void set_gpio(GPIO_TypeDef *GPIO, int n) ;
void reset_gpio(GPIO_TypeDef *GPIO, int n) ;
void configure_timer(TIM_TypeDef *TIM, int psc, int arr) ;
void configure_it(void) ;
void start_timer(TIM_TypeDef *TIM) ;
void stop_timer(TIM_TypeDef *TIM) ;
Vous trouverez dans le fichier main_init.c des commentaires indiquant ce que doivent faire ces fonctions.
Vous trouverez aussi une fonction void rand(void)
qui retourne une valeur aléatoire entre 800 et 1800. Utilisez cette fonction pour que la durée entre deux allumages de la LED soit comprise entre 800 ms et 1800 ms (entre deux blocs 2 sur le chronogramme).
Vous êtes libre d’ajouter toutes les fonctions que vous souhaitez, par contre, pour faciliter la correction, mettez l’ensemble de votre code dans le fichier main.c aux endroits attendus (voir commentaires dans le fichier).
Contraintes
Pour réaliser ce projet, vous ne devez pas faire d’attente active (scrutation), mais travailler uniquement sous interruption. Votre main ne doit donc contenir que les fonctions d’initialisation, une fonction pour lancer le premier timer et une boucle while(1)
.
Vous devez aussi tester votre application en simulation pour vérifier que le système fonctionne correctement. Pour cela, vérifiez bien que :
la durée pendant laquelle la LED est allumée est bien de 300 ms,
la durée aléatoire entre deux allumages de la LED est bien comprise entre 800 ms et 1800 ms.
Vérifiez bien que vous avez les éléments suivants.
Vous devez rendre uniquement le fichier main_v1.c que vous avez modifié, ne créez pas de fichier d’en-tête ou d’autres fichiers pour structurer votre code.
Pensez à commenter votre code s'il y a besoin d'en expliquer le fonctionnement.
Vérifiez votre travail
Attention, il peut y avoir plusieurs solutions pour réaliser l’application de jeu. Ce qui compte ici n’est pas la qualité de ces fonctionnalités, mais la maîtrise des éléments de configuration et d’utilisation des périphériques d’un microcontrôleur. Il faut donc avant tout regarder si les fonctions pour manipuler les périphériques sont correctes.
Vérifiez que le fichier main_init.c compile bien, si ce n’est pas le cas trouvez-en les raisons ! Si vraiment rien n'y fait, vérifiez par une simple lecture les éléments à corriger, mais si ça ne compile pas, c'est qu'a priori il y a un problème !
Les éléments à vérifier sont :
La qualité globale du code : est-ce qu’il est bien structuré, est-ce qu’il est un minimum commenté ?
L’utilisation des masques : est-ce que la configuration des registres se fait à l’aide de masques ou non ?
La configuration des GPIO : est-ce qu’ils sont bien configurés ? est-ce que l’horloge est bien validée ?
La configuration des timers : est-ce que leur horloge est validée ? est-ce les valeurs de PSC et ARR sont cohérentes ? est-ce que les fonctions de démarrage et d’arrêt sont bien réalisées ?
La configuration des interruptions et les fonctions d’interruption : est-ce que les interruptions sont bien activées au niveau du périphérique ainsi qu’au niveau du NVIC ? est-ce que les fonctions d’interruption ont bien été réalisées et est-ce que la source d’interruption est validée dans l'interruption ?
Vous pouvez ensuite évaluer le fonctionnement du code en vérifiant en simulation, ou en réel, si la durée pendant laquelle la LED LD2 est allumée est bien de 300 ms.
Pour vous évaluer, vous pouvez commencer par lire le code, mais il faut aussi l’exécuter et examiner la durée des différentes actions et leurs conséquences en mettant des points d’arrêts.
La solution que je vous propose utilise 2 broches d’entrée/sortie, 3 timers et un gestionnaire d’interruption externe. Le timer TIM3 est utilisé pour compter le temps entre deux phases d’allumage de la LED, le timer TIM2 est utilisé pour contrôler la durée d’allumage de la LED et le timer TIM4 fait clignoter la LED en cas de victoire.
Cette solution n’est pas optimale en termes d’utilisation des timers, mais ce n’est pas l’essentiel. Il est possible de faire tout cela avec un seul timer, mais le code devient un peu plus compliqué. La lisibilité et la facilité ont été privilégiées dans ce code.
Le principe de fonctionnement est qu’à chaque interruption d’un timer, on stoppe le timer qui a provoqué l’interruption et on lance le suivant. Il faut aussi dans la fonction d’interruption éteindre et allumer la led. En reprenant le chronogramme de l’énoncé, on peut représenter cela par le schéma suivant :