Construire une plaquette de développement (Pinguino – addendum)

Previously on Pinguino …logo-pinguino-add

Partie 1 : Présentation

Partie 2 : Hardware

Partie 3 : Software

Partie 4 : Hello World

> Addendum : Aller plus loin

Ce billet fait suite au Hello World sur plaquette de développement Pinguino.

Pour commencer nous allons voir comment modifier le programme existant pour faire clignoter notre LED. Ensuite, nous allons nous rapprocher un peu plus de la cible en modifiant le Hello World initial afin de le faire ressembler à un programme en C. Enfin je ferai une analyse du Hello World initial pour améliorer votre compréhension du fonctionnement de la librairie Pinguino.

Faire clignoter la LED

Allumer une LED c’est bien mais la faire clignoter c’est déjà plus marrant.

Pour faire clignoter une LED il faut alternativement allumer et éteindre la LED avec une temporisation, par exemple 500 ms. Pour réaliser cette tempo, la bibliothèque Pinguino propose la fonction delay qui prend comme paramètre un temps en millisecondes.

Pour alterner l’état d’une sortie, il y a également une fonction : toggle, ce qui évite d’appeler deux fonctions. Décidément Pinguino nous mâche beaucoup trop le travail  !

La PIN 11 reste définie en sortie dans la fonction setup, par contre le delay et le toggle sont placés dans la loop afin de faire clignoter la LED tant que le Pinguino est alimenté :


void setup(void)

{

// initialize the digital pin 13 as an output.

pinMode(11,OUTPUT);

}

void loop(void)

{

toggle(11);        // alternate ON and OFF

delay(500);        // wait for 500ms

}

Notez le type void en paramètre des fonctions setup et loop.

Ce type n’est pas obligatoire dans ce projet et le template par défaut ne le propose même pas.

Sachez qu’il s’agit cependant d’une règle de bonnes pratiques (voir par ex. les [1] règles MISRA C en langage C) et certains compilateurs retourneront un warning si une déclaration/définition de fonction sans paramètres n’a pas le type void.

Pour d’autres exemples de programmes, allez faire un tour sur [2] Example de programmes.

On y trouve entre autres une variante de ce que l’on vient de faire mais en appelant la fonction digitalWrite avec successivement les états HIGH et LOW pour allumer puis éteindre la LED. Pour le code source, c’est par ici : [3] Faire clignoter une LED

Allumer une LED comme un pro

Tout ça c’est très bien mais vous avez peut être envie d’y voir un peu plus clair. Jusqu’ici on s’est contenté d’appeler des fonctions, mais qui a-t-il derrière ? Reprenons notre Hello World initial. Comment ferait-on pour allumer une LED sans la bibliothèque Pinguino ?

Et là on va sortir l’arme du développeur embarqué …

… la datasheet du micro : [4] Datasheet 18F2550.

Dans la datasheet figure le Pin diagram (ou pinout) (Figure 1) :

Figure 1 PIC18F2550 pin diagram

C’est un peu plus complexe que le schéma du Pinguino mais pas tant que ça !

On troque des étiquettes par d’autres.

Ce qui nous intéresse c’est d’allumer la LED PIN 11. Sauf qu’ici il n’y plus les étiquettes Pinguino. Notre LED est reliée physiquement à la pin 12 du micro. On voit que la pin 12 possède plusieurs fonctions différentes séparées par des slashs.

Ce qui nous intéresse est la fonction I/O (pour Input/Output). Sur le PIC 18F2550 LES I/O sont regroupées sur 3 ports bidirectionnels (entrée ou sortie) : PORT A à C. Chaque bit d’un PORT représente une pin. Les I/O sont identifiées par la lettre R, pour Register, suivi d’une lettre pour le port (A, B, C) et d’un chiffre pour le bit correspondant dans le port.

La pin 12 est repérée RC1 pour : Register PORT C, bit 1.

C’est important de comprendre cela lorsqu’on programme des I/O en assembleur ou en C.

Ce qui a de bien avec le projet Pinguino c’est que l’on va pouvoir faire un programme pour allumer notre LED comme on le ferait en C sur un projet de type MPLAB, l’IDE de Microchip et son compilateur C18.

Rappelez-vous nous avons déjà téléchargé MPLAB X dans la partie ¾, pour le chargement du bootloader).

Microchip fournit pour tous ses microcontrôleurs une librairie pour accéder aux différents registres micro (on parle de mapping).

Dans le répertoire d’installation de l’IDE Pinguino, dans le sous-dossier p8\include\non-free\pic16 se trouve un fichier pic18f2550.h.

Si on l’ouvre il contient l’instruction #include « pic18f2455.h », ce qui signifie que les deux micro partagent les mêmes définitions.

En ouvrant le second on trouve un ensemble de définitions dont celles des ports I/O.

Registre de direction TRISx

Comme précédemment on doit position la pin 12 en sortie, soit le bit 1 du PORTC.

Pour cela on utilisera le registre Trisx, avec x la lettre du PORT, soit C. TRIS est l’abréviation de tri-state qui est un état de haute impédance.

Un PORT I/O possède 3 états possibles :

  • Output high ;
  • Output low ;
  • Input (haute impedance ou tri-state).

De là on imagine aisément vu le nom du register de direction que pour mettre une pin d’un port en entrée, on met le bit correspondant à 1 et à 0 pour la mettre en sortie.

Enfin, pour accéder à un bit particulier on utilisera la notation pointée suivante :


TRISCbits.TRISC1 = 0  // pin11 ou C1 en sortie.

A noter que l’on placer un port entier en entrée ou en sortie en utilisant le label PORTC.

Registre de donnée du port PORTx

Et pour la donnée on utilisera la notation pointée :


PORTCbits.RC1 = 1 ; // pin11 ou C1 à 1.

De même que pour le registre de direction, on peut écrire sur un port entier en utilisant le label PORTC.

Je ne parlerai pas ici de la différence entre l’écriture sur le PORT ou sur le LATCH pour ne pas complexifier inutilement ce billet.

Voici pour terminer le programme complet, tel qu’on pourrait l’écrire en C :


void setup()

{

     TRISCbits.TRISC1 = 0;

     PORTCbits.RC1 = 1;

}

void loop()

{

}

Les explications étaient plus complexes que le programme

Comprendre la librairie Pinguino par l’exemple

J’ai volontairement commencé par décrypter le fonctionnement des I/O avant de décrypter la librairie Pinguino. Car celle-ci n’est rien d’autre qu’une surcouche de ce que l’on a fait juste au dessus.

Notre Hello World initial peut se résumer aux 2 appels suivants :

pinMode(11,OUTPUT);
digitalWrite(11,HIGH);

Que font réellement ces fonctions ?

Dans le répertoire d’installation de l’IDE Pinguino, dans le dossier \p8\include\pinguino\core se trouve le fichier digitalw.c qui contient la définition de ces 2 fonctions et le fichier const.h qui contient les constantes que l’on a déjà rencontré :

#define INPUT   1
#define OUTPUT  0
#define HIGH    1
#define LOW     0

Tiens tiens tiens

On retrouve ce dont on a déjà parlé plus haut, on configure une sortie en mettant un 0 et on allume la LED en mettant 1 sur la sortie correspondante.

Continuons avec la première fonction : pinMode

#define pB    0
#define pC    1
#define pA    2
#define pD    3
#define pE    4
#define pF    5
#define pG    6

#define _0    1<<0    // 0x01
#define _1    1<<1    // 0x02
#define _2    1<<2    // 0x04
#define _3    1<<3    // 0x08
#define _4    1<<4    // 0x10
#define _5    1<<5    // 0x20
#define _6    1<<6    // 0x40
#define _7    1<<7    // 0x80

#ifdef PIC18F2550

const u8 mask[18]={    _0,_1,_2,_3,_4,_5,_6,_7,
_6,_7,_0,_1,_2,
_0,_1,_2,_3,_5};

const u8 port[18]={    pB, pB, pB, pB, pB, pB, pB, pB,
pC, pC, pC, pC, pC,
pA, pA, pA, pA, pA};

#endif

void pinmode(int input, int state)

{
switch (port[input])
{
case pB: if (state) TRISB=TRISB | mask[input];

else TRISB=TRISB & (255-mask[input]);

break;

case pC: if (state) TRISC=TRISC | mask[input];

else TRISC=TRISC & (255-mask[input]);

break;

case pA: if (state) TRISA=TRISA | mask[input];

else TRISA=TRISA & (255-mask[input]);

break;

#if defined(PIC18F4550) || defined(PICUNO_EQUO)

case pD: if (state) TRISD=TRISD | mask[input];

else TRISD=TRISD & (255-mask[input]);

break;

case pE: if (state) TRISE=TRISE | mask[input];

else TRISE=TRISE & (255-mask[input]);

break;

#endif
}
}

Hey revenez ! Ce n’est pas si compliqué…

On commence par le switch, qui se fait sur le contenu du 12ème élément à partir de 0 du vecteur port, soit pC, qui vaut 1.

state vaut OUTPUT soit 0 donc on exécute l’instruction contenue dans le else :


TRISC=TRISC & (255-mask[input]);

 

Bon le registre TRISx, on en a déjà parlé. Le ET binaire à l’aide de l’opérateur & permet de faire un masque entre le contenu du registre TRISC et le résultat de l’opération (255-mask[input]).

Le registre TRISx est un registre 8 bits. 255 est la plus grande représentation d’un entiers 8 bits (0xFF en héxadécimale). mask[input] avec input=11 contient _1 soit 1<<1. L’opérateur réalise un décalage à gauche ce qui est une représentation traditionnelle dans le monde de l’embarqué pour identifier un bit. Par exemple :

Bit 0 : 1 << 0

Bit 1 : 1 << 1 (en binaire 10b soit 0x2)

Bit N : 1 << N

Donc en mettant tout bout à bout on obtient : TRISC = TRISC & (255 – 2).

Et en représentation binaire cela donne : TRISC = TRISC & 11111101b.

Ce qui revient à mettre le bit 1 du registre TRISC à 0, c’est-à-dire en sortie.

Vous pouvez faire de même avec les autres fonctions (digitalWrite, delay, toggle, …). C’est toujours important de comprendre ce que l’on fait avant de s’autoriser à utiliser des raccourcis !

Ou alors version Morpheus :

Il y a une différence entre connaître le chemin et arpenter le chemin.

there’s a difference between knowing the path and walking the path.

C’est tout pour cette fois-ci ! A la prochaine.

Références :

[1] règles MISRA C ;

[2] Example de programmes ;

[3] Faire clignoter une LED ;

[4] Datasheet 18F2550.

Publicités