mardi 4 décembre 2012

EasyAVR AVRStudio et quelques exemples sur les I/O led et button

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).
Par Exemple :

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).
Par exemple si l'on a le code suivant : 
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.




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
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 :
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 bit
Et à 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);
}
}

dimanche 2 décembre 2012

Prise en main de EasyAVR 6


               1-Introduction et Installation:


Et bien voilà, c'est la grand jour et le papa Noël est passé avec un peu d'avance (normal il venait de Budapest).
Par soucis d'économie, je suis directement partie sur le kit EasyAVR avec un code de réduction qui allait bien.
Bref, le plus dure à été de déballer la carte de son corset anti-statique ! En effet, l'emballage est vraiment très protecteur....à tel point qu'il devait presque délicat de sortir la carte de son emballage. Ci-dessous une illustration du contenu du kit (LCD et TFT non visible).

Le kit est bien fournis, avec une version papier du manuel utilisateur. Après, on regrettera le fait que tout le manuel utilisateur de MikroC ne soit pas fournis en version papier.
Les DVD d'installation sont également présent.

Pour l'installation, il suffit d'insérer le DVD fourni et de réaliser l'installation de MikroC Pro (ou pas) puis des différent Driver.
Si comme pour moi, le DVD semble avoir une faiblesse....alors contourner cela depuis le site de MikroE par téléchargement des logiciels et driver.
Veillez à bien respecter l'ordre d'installation : logiciel, puis driver et ENFIN raccorder la carte au PC avec le câble USB !
Sinon, vous aurez gagné le droit à un fonctionnement erratique...
Une fois l'installation terminée, vous aurez le droit à un redémarrage du PC en règle !


               2-Premier Code--Hello World

On va démarrer avec un premier code simple. Il s'agira de réaliser un petit chenillard sur les LED (2 rangées)   raccordées sur les ports A et B du ATmega16.


Bien que l'IDE MikroC Pro soit sympa, celui de Atmel AVRStudio reste une référence. C'est pourquoi le code sera "montré" pour les 2 IDE et les différences expliquées.

Configuration :

La configuration choisie repose sur une utilisation directe des ports du MCU (MicroControllerUnit, terme utilisé par la suite pour définir le microcontroller).
Donc, on utilise directement les ports en Output. Cette configuration s'effectue dans le registre : DDRx, où x = A, B , C etc...
Dans notre exemple on obtient donc la syntaxe suivante (en hexa 0x):
DDRA = 0xFF;
Désormais toute les "Pin" du port A sont de type "Output". Elles vont donc pouvoir driver un courant de 20mA , max 200mA (données issues de la Datasheet de l'ATmega16). Concrètement, on obtient le schéma électrique suivant :
Illustration du schéma électronique de la sortie LED ATMega16 EasyAVR (schéma réalisé sous KiCad).

Forcer une sortie à un état logique:

Pour forcer une sortie à un état logique (1 ou 0), il existe plusieurs syntaxe en C. Nous évoquerons ici que les syntaxes testées (et comprises :) ). On verra par la suite les autres possibilités.
PORTA = 0;
Permet de forcer toutes les sorties à 0 du port A
PORTB = 0xff;
Permet de forcer toutes les sorties à 1 du port B (notation en hexa).

Pour forcer un bit particulier du registre PORTx, on peut aussi employer la syntaxe suivante :
PORTA.B4= 1;
Attention : cette syntaxe est valable sous l'environnement MikroC.
Il existe un équivalent générique sous AVRStudio (non présenté ici).
Dans le cas présent, cela signifie que le bit 4 du portA est mis ) l'état logique 1.

Contrainte du chenillard:

C'est l'architecture électronique qui impose le programme à rédiger. Dans le cas présent, chaque sortie étant directement reliées à une LED, alors il va falloir "balayer" le port A.
Une des méthodes possibles est d'utiliser la boucle FOR.
A chaque itération de la boucle, on viendra inscrire une valeur différente dans le registre PORTA.

==> i=i*2  st l'incrément à mettre en place pour avoir un changement de bit et compter de "2 en 2" et obtenir l'effet chenillard souhaité.

Exemple de code sous MikroC:


int i=0;
const unsigned long delay = 100;
void main() {
     DDRA = 0xFF;
     PORTA = 0;
     while(1){
              for (i=1;i<256;i=i*2) {
                   PORTA= i;
                   Delay_ms(delay);
              }
              PORTA = 0;
              Delay_ms(delay);
              PORTA = 0xff;
              Delay_ms(500);
              PORTA = 0;
              Delay_ms(500);
              PORTA = 0xff;
              Delay_ms(500);
              PORTA = 0;
     }
   
}



Exemple de code sous AVRStudio:



/*
 * Blinkg_HelloWorld_AVRStudio.c
 *
 * Created: 02/12/2012 16:46:15
 *  Author: Maison
 */

#include <avr/io.h>
#include <util/delay.h>

int i=0;
const unsigned long delay = 100;
int main(void)
{ //Setting of the port
DDRA = 0xFF; //Toutes les sorties en mode output
PORTA = 0;   // sortie à l'état bas
DDRB = 0xFF; //Toutes les sorties en mode output
PORTB = 0;   // sortie à l'état bas
    while(1)
    {
for (i=1;i<256;i=
i*2) {
PORTA= i;
_delay_ms(delay);
}
PORTA = 0;
_delay_ms(delay);
PORTA = 0xff;
_delay_ms(delay);
PORTA = 0;
_delay_ms(delay);
PORTA = 0xff;
_delay_ms(delay);
PORTA = 0;
for (i=1;i<256;i=i*2) {
PORTB= i;
_delay_ms(delay);
}
PORTB = 0;
_delay_ms(delay);
PORTB = 0xff;
_delay_ms(delay);
PORTB = 0;
_delay_ms(delay);
PORTB = 0xff;
_delay_ms(delay);
PORTB = 0;
} }

Et voici une courte vidéo pour le résultat finale :

3-Mais comment charger le code AVRAtmel depuis EasyAVR

J'essaierai de rajouter des captures d'écran une prochaine fois.
Mais il suffit de charger le .hex généré depuis AVRStudio depuis l'utilitaire AVRFlash Prog de MikroElektronika.

Nota : il est aussi possible d'utiliser le port JTAG ou AVRISP de l'EasyAVR pour utiliser un programmateur externe. Mais j'essaierai d'illustrer cela une prochaine fois.