Une première interface de dialogue homme-machine : le boutonEt oui, tout le monde le sait, surtout sur un forum dédié à l'arcade, que le bouton-poussoir est la plus belle invention de l'homme pour se faire comprendre d'une machine.
La seconde plus belle étant, à mon avis, le microswitch qui a grandement amélioré la première!
Et donc, nous allons utiliser un bouton pour faire comprendre à notre µC qu'à certains moments, on veut que nos leds clignotent et pas à d'autres. (et je cite Balladur: "Je vous demande de vous arrêter!")
Tout d'abord, un rappel des opérateurs bitwise.Parce que c'est toujours utile!
AND : & | | | OR : | | | | XOR : ^ | | | NOT : ~ |
BitA | BitB | BitA&BitB | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 |
| | | BitA | BitB | BitA|BitB | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
| | | BitA | BitB | BitA^BitB | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
| | | |
Et n'oubliez pas que ces opérations s'effectuent bit à bit!
Les éléments du MSP430G2231 que nous utiliserons lors de ce tuto.
IntroPour cet exercice, on va partir du
tuto-002 avec de légères modifications pour rendre le code plus "solide".
#include <msp430g2231.h>
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // désactivation du watchdog
P1OUT &= ~(BIT0 | BIT6); // bits de sorties correspondants à P1.0 et P1.6 à 0
P1DIR |= (BIT0 | BIT6); // définit P1.0 et P1.6 en tant que sorties
for (;;)
{
P1OUT ^= (BIT0 | BIT6); // inversion de l'état des bits de sorties 0 et 6
__delay_cycles(250000); // de manière a réduire la vitesse de clignotement
}
}
Vous remarquerez que l'opérateur de concaténation/addition "+" a disparu.
La raison est simple, cet opérateur additionne bêtement les valeurs : 0000‿0001₂ + 0000‿0001₂ = 0000‿0010₂.
Hors, vu que nous manipulons majoritairement des valeurs binaires, les opérateurs bitwise permettent d'éviter des bourdes : 0000‿0001₂ | 0000‿0001₂ = 0000‿0001₂.
Donc si par erreur on tape
P1OUT ^= (BIT0 | BIT0);
seul le BIT0 de P1OUT sera modifié.
Alors qu'avec
P1OUT ^= (BIT0 + BIT0);
on se serait retrouvé avec le BIT1 de P1OUT modifié… bonjour le merdier!
(Merci a Keube pour m'avoir signalé ça dans le tuto-002
)
Premiers pas dans la gestion du bouton poussoirPour que notre µC sache que notre bouton, raccordé à P1.3, est une entrée, il suffit de définir le BIT3 de P1DIR à 0. Ou de ne pas toucher au BIT3 de P1DIR, par défaut tous les bits de registres sont réinitialisés à 0 à chaque démarrage du µC (mon choix personnel).
Comment récupérer l'état du bouton? Deux solutions se présentent à nous.
La première, est d'aller voir "manuellement" quel est l'état du BIT3 de P1IN dans une boucle. Cette méthode est tout à fait fonctionnelle, mais un peu laborieuse et on va gaspiller pas mal de ressources.
Les MSP430 nous permettent une seconde solution bien plus "raffinée" et performante pour gérer ce genre d'événements. Et ce sont les interruptions.
Une interruption, comme son nom l'indique, interrompt le processus en cours d'exécution, "note" l'endroit où il en était dans son processus, et exécute le code lié à l'interruption, puis reprend là où il en était. Je ne rentrerai pas dans les détails de ce processus, cette petite explication rapide est suffisante pour comprendre le principe fondamental. Dans notre cas, l'interruption se déclenchera lorsque l'on poussera sur le bouton.
Conclusion, nous allons nous oublier P1IN en faveur de P1IE (Port 1 Interrupt) et passer le BIT3 de ce dernier à 1 pour définir P1.3 comme déclencheur d'interruption.
P1IE |= BIT3;
Mais il faut aussi activer le gestionnaire d'interrupts dans le µC, pour ça, il est nécessaire de rajouter la ligne suivante après l'assignation du BIT3 au gestionnaire d'interruptions du port 1
__enable_interrupt();
Le corps du codeBon, on a définit la condition de déclenchement de l'événement, maintenant, il faut définir modifier la boucle de clignotement des leds.
Le plus simple est d'utiliser une variable et une condition. On initialise cette variable à 0, on va utiliser une variable globale de type nombre entier non signé.
(Variable globale = variable définie en dehors des fonctions. Nombre entier non signé ça signifie que si on lui assigne un chiffre négatif, il ne tiendra pas compte du "-")
unsigned int blinking = 0;
Dans la boucle, si cette variable est plus grande que 0, alors le changement d'état des deux bits contrôlant les leds se produit, sinon, on continue la boucle.
for (;;)
{
if(blinking > 0){
P1OUT ^= (BIT0 | BIT6); // inversion de l'état des bits de sorties 0 et 6
__delay_cycles(250000); // de manière a réduire la vitesse de clignotement
}
}
Ce qui s'exécute durant l'interruptionIl nous reste encore à produire le code qui va s'exécuter lors d'une interruption.
#pragma vector=PORT1_VECTOR
De façon générale dans les microcontroleurs on trouve une table de vecteurs d'interruptions à une adresse donnée de la mémoire. Sur le msp430 (celui du tuto?) elle se trouve entre 0x0FFFFh et 0xFFC0h.
A chaque adresse correspond une fonction à appeler quand l'interruption tombe. Par exemple, toujours sur le même micro, l'interruption correspondant au port 1 se trouve en 0x0FFE4h.
Ton #pragma vector=PORT1_VECTOR va donc placer une "redirection" vers l'interruption __interrupt void Port_1(void) à cette adresse.
Note qu'elle est commune à tout le port 1, et tu dois donc y gérer les interruptions des 8 pins si tu en actives plusieurs.
__interrupt void Port_1(void)
{
Le "__interrupt" préviens que ce n'est pas une simple fonction, mais le code de gestion de l'interruption. Cette fonction particulière, ne renverra rien - le "void" - et ne prends rien comme argument - le (void) -.
blinking ^= 0x01;
Alors, le préfixe 0x indique au compilateur que la valeur est une valeur haxadécimale. Par défaut, les valeurs hexadécimales sont codées sur 16 bits, juste ce qu'il nous faut pour "remplir" un unsigned int. 0x01 vaut donc 0000‿0000‿0000‿0001₂.
Pour le reste, vous devriez être capable de comprendre l'effet de cette ligne de code.
P1IFG &= ~BIT3;
P1IFG (Port 1 Interrupt FlaG) est - semble-t-il - le registre dans lequel sont stockés le fait qu'un interrupt s'est déclenché sur un des bits. Pour que l'interrupt puisse à nouveau se produire, il faut nettoyer le registre, d'après ce que j'ai compris.
//OPTIONNEL P1IES ^= BIT3;
P1IES - Port 1 Interrupt Edge Select - ce registre va permettre de sélectionner sur quel flanc du signal l'interruption va se produire. Si un bit est à 0 (par défaut), c'est le flanc montant, à 1 c'est le flanc descendant.
Ce que cette ligne de code fait, c'est que les leds ne clignoteront que lorsque le bouton sera enfoncé.
//OPTIONNEL P1OUT &= ~(BIT0 | BIT6);
}
Et on s'assure que les leds soient éteintes.
RésultatCe qui nous donne, au démarrage du µC, les leds sont éteintes, on appuie sur le bouton P1.3 , les leds se mettent à clignoter. Et si on appuie à nouveau, les leds s'éteignent.
#include <msp430g2231.h>
unsigned int blinking = 0; //variable globale contrôlant le clignotement
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // désactivation du watchdog
P1OUT &= ~(BIT0 | BIT6); // bits de sorties correspondants à P1.0 et P1.6 à 0
P1DIR |= (BIT0 | BIT6); // définit P1.0 et P1.6 en tant que sorties
P1IE |= BIT3;
__enable_interrupt();
for (;;)
{
if(blinking > 0){ // si blinking est plus grand que 0, alors on clignote, sinon, on ne fait rien
P1OUT ^= (BIT0 | BIT6); // inversion de l'état des bits de sorties 0 et 6
__delay_cycles(250000); // de manière a réduire la vitesse de clignotement
}
}
}
#pragma vector=PORT1_VECTOR // Joker
__interrupt void Port_1(void) // initialisation de la fonction interrupt
{
blinking ^= 0x01 // blinking étant une variable globale, elle est aussi accessible depuis l'interrupt et ici, on inverse l'état de son BIT0
P1IFG &= ~BIT3; // Remise à 0 du flag d'interrupt
// P1IES ^= BIT3; // Changement de flanc pour la génération d'interruption
P1OUT &= ~(BIT0 | BIT6); // on s'assure que les leds sont bien éteintes après un appuis sur le bouton
}
Challenge!Je vais corser un peu par rapport au précédent.
Pour ce challenge, vous allez partir de votre solution au challenge précédent.
Ensuite, il faut qu'au démarrage les 2 leds soient éteintes.
Au premier appuis sur le bouton, les leds doivent clignoter alternativement.
Au second appuis, les deux doivent être éteintes.
Et bien sur, ça doit fonctionner plus de 2 fois sans reset
Si vous voulez pousser le vice à fond, il y a une requête supplémentaire, il faut qu'au troisième appuis, le clignotement reprenne là où il avait été interrompu!
(Challenge battu, même la partie vicieuse
)
Encore merci à Keube, il m'est d'une grande aide, aussi bien pour la rédaction que pour améliorer ma façon de coder
Méthodes pour me contacterAu cas où vous rencontriez une embûche, si vous voulez discuter ou approfondir les explications ou les explorations à propos de ces tutos, voici quelques méthodes pour me contacter :
- par ce sujet
- par MP
- par mail
- par jabber
- par msn
J'utilise la même adresse mail pour msn et jabber: kodein AT reflexd.com (j'essaye d'éviter les moissonneurs automatiques d'adresse
).