Introduction:
L'objet de ce post est de montrer (avec mes mots) comment réaliser l’interaction du MCU avec un bouton.L'idée est de créer un code qui balaye des LED et inverse le sens dès que l'appui sur un bouton est détecté.
On utilisera pour cela une Macro.
Je profite aussi de ce post pour découvrir : la configuration des registres d'entrées/sorties (I/O); la création d'une fonction simple (juste pour structurer un peu le programme) et enfin, l'utilisation des macro.
Nota : la détection de l'appui sur le bouton ne se fera pas par le mécanisme des interruptions (on verra cela une autre fois pour les interruptions externes et logicielles).
Encore une fois, ce post se déroule dans le cadre d'un MCU ATMega16 avec un IDE AVRStudio et une plateforme matériel EasyAVR 6.
Configuration des registres d'entrées/sortie PORT:
Chez Atmel, sur les ATMega 16, il existe 3 registres en charge de la configuration des entrées/sorties TOR -Tout Ou Rien.
- Un gros : le registre DDRx (avec x = A, B, C ou D) permet de configurer si l'entrée physique est de type entrée (0) ou sortie (1).
DDRA = 0b 01011100;
Dans ce cas là, alors les broches 2, 3 4 et 6 sont de types Sortie (Output). Tandis que les autres broches sont des entrées.
- Le registre PORTy (avec y = A, B, C ou D) permet:
- de forcer un état logique haut = 1 ou bas =0 (si et seulement si la borne DDRx correspondante a été configurée en mode output)
- OU
- de forcer la mise en place d'une résistance de Pull-Up (tirage à +VDD du MCU) si 1 et aucune résistance si 0 (si et seulement si la borne DDRx correspondante a été configurée en mode input).
DDRB = 0b 01011000;
PORTB = 0b 01101100;
Signifie que la broche 0 est de type entrée sans résistance de Pull-Up.
Signifie que la broche 2 est de type entrée avec une résistance de Pull-Up.
Signifie que la broche 6 est de type sortie et que l'état est forcée à 1 (haut logique).
Signifie que la broche 4 est de type sortie et que son état est forcée à 0 (bac logique).
==>Bien entendu, de nombreux cas sont possibles. J'ai pris le partie d'utiliser une notation binaire (0b) pour bien "montrer" l'incidence sur les bit.
- Le registre PINz (avec z = A, B, C ou D) permet de lire / récupérer l'état de la broche si et seulement si la borne DDRx correspondante a été configurée en mode input et cela quelque soit la présence de la résistance de Pull-Up.
Nota : l'activation de la résistance de Pull-Up ou pas, n'a pas grand chose à voir avec l'aspect "programmation" du MCU. Cela tient plutôt de considération électronique (que j'essaierai d'expliquer une autre fois).
Création d'une fonction pour commencer à structurer le code :
Loin de moi l'idée de vouloir rentrer dans les explications sur les fonctions en C pour AVR (je comprends pas tout pour le moment :) ). L'idée est plutôt de montrer l'intérêt des fonctions en C ne serait-ce que pour la structuration du code (là, les fanatiques de C doivent bondir et fabriquer un IED à mettre sous ma voiture....).
Je fais donc créer une fonction très simple qui aura la charge de réaliser le chenillard à LED. La fonction ne renverra aucune valeur--> syntaxe void ; pas plus qu'elle n'utilisera de paramètre en entrée -->paramètre void . Voici ce que l'on obtient donc pour la définition de la fonction.
Je fais donc créer une fonction très simple qui aura la charge de réaliser le chenillard à LED. La fonction ne renverra aucune valeur--> syntaxe void ; pas plus qu'elle n'utilisera de paramètre en entrée -->paramètre void . Voici ce que l'on obtient donc pour la définition de la fonction.
void led_chaser(void)
{
int i=0;
for (i=256;i>0;i=i/2)
{
PORTA = i;
_delay_ms(500);
}
PORTA=0; }
Rien à dire en particulier sur ce code, qui a déjà été abordé lors d'un précédent post ici.
En ce qui concerne l'appel de la fonction, il suffit d'utiliser la syntaxe suivante : led_chaser(); .
C'est entre les parenthèses que l'on aurait passé les arguments de la fonction.
Présentation de la Macro pour lire l'état de l'entrée à scruter :
Pour lire un état PINB7 par exemple, il suffit d'utiliser la syntaxe suivante :
PIN
PIN
Explication : (merci l'algèbre de Boole)
Si l'on part de la table de vérité d'un ET et d'un OU alors cela devient clair.
En effet, si l'on part du mot 0011, qu'elle est l'opération qui laisse les bit inchangé ?
--> Un "ET" avec un 1 bien sûr !
Donc, si l'on désire "lire" l'état sur le bit 7, PB7 de notre entrée, il suffit de réaliser l'opération suivante:
01010110
ET 01000000
-----------
01000000 --> là on ne récupère donc que la valeur du bit 7 ! !
C'est ce que l'on appel la technique du Masque. Google est votre ami et vous trouverez tous pleins d'explication plus efficace que les miennes avec des mots clefs tel que Bitwise, AVR, bit manipulation, etc...
Du point de vue code AVR GCC, cela se traduit de la manière suivante :
Nota : pour une lecture en mode "normalement ouvert" de l'interrupteur, on utilisera la syntaxe :
Voilà sur le principe.
Et la macro alors dans tous cela ?
Et bien les inventeurs de tous cela, ont pensé qu'il serait pertinent de définir des macro pour réaliser tous ces types d'opération de base.
Par exemple, la lecture de la valeur d'une I/O est "macrotisée" en :
Si l'on part de la table de vérité d'un ET et d'un OU alors cela devient clair.
En effet, si l'on part du mot 0011, qu'elle est l'opération qui laisse les bit inchangé ?
--> Un "ET" avec un 1 bien sûr !
Donc, si l'on désire "lire" l'état sur le bit 7, PB7 de notre entrée, il suffit de réaliser l'opération suivante:
01010110
ET 01000000
-----------
01000000 --> là on ne récupère donc que la valeur du bit 7 ! !
C'est ce que l'on appel la technique du Masque. Google est votre ami et vous trouverez tous pleins d'explication plus efficace que les miennes avec des mots clefs tel que Bitwise, AVR, bit manipulation, etc...
Du point de vue code AVR GCC, cela se traduit de la manière suivante :
byte result = PINB & 0x40;En utilisation avec une structure de "IF", cela donne quelque chose du genre :
if(PINB & (1<<PINB7))Ici, on utilise la syntaxe 1<<PINB7 en équivalent de 0x40 = 0b01000000
Nota : pour une lecture en mode "normalement ouvert" de l'interrupteur, on utilisera la syntaxe :
if~(PINB & (1<<PINB7))
Voilà sur le principe.
Et la macro alors dans tous cela ?
Et bien les inventeurs de tous cela, ont pensé qu'il serait pertinent de définir des macro pour réaliser tous ces types d'opération de base.
Par exemple, la lecture de la valeur d'une I/O est "macrotisée" en :
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // Macro to check bitEt à l'utilisation, cela donne :
if (CHECKBIT(PINB,7))
Exemple de code "complet" en C:
/*
* LedAndButton.c
*
* Created: 04/12/2012 21:45:31
* Author: Florent Tainturier
* Test of tutorial for changing the direction of led chaser with input button
*/
#define F_CPU 1000000UL
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // Macro to check bit
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
//Configuration of PORT
DDRB= 0x7f; // 1 as output; 0 as input
DDRA= 0xFF; //all as output
PORTB = 0x00;
while(1)
{
//if (CHECKBIT(PINB,7))
if(~PINB & (1<<PINB7))
{
led_chaser();
} else
{
led_chaserPortB(); }
}
}
void led_chaser(void)
{
int i=0;
for (i=256;i>0;i=i/2)
{
PORTA = i;
_delay_ms(500);
}
PORTA=0; }
void led_chaserPortB(void)
{
int i=0;
for (i=1;i<=256;i=i*2)
{
PORTB = i;
_delay_ms(500);
}
}