PARTIE 2
Noxyben
2008
www.noxyben.fr
-1-
Introduction ................................................................................................................................ 3
Le schéma de base...................................................................................................................... 4
Principe de fonctionnement des ports I/O .................................................................................. 7
Utilisation des ports d’entrée/sortie (I/O)................................................................................... 8
Utilisation d’un PORT en sortie : un chenillard 8 leds .......................................................... 8
Le matériel : ....................................................................................................................... 8
Le programme : ................................................................................................................ 10
Analyse du programme : .................................................................................................. 12
Compilation du programme et injection dans le PIC : ..................................................... 16
Tests ................................................................................................................................. 17
Conclusion :...................................................................................................................... 17
Utilisation d’un PORT en entrée : un chenillard 8 leds contrôlé par 2 boutons poussoirs. . 18
Le matériel : ..................................................................................................................... 18
Le programme : ................................................................................................................ 19
Analyse du programme : .................................................................................................. 22
Compilation du programme et injection dans le PIC : ..................................................... 23
Tests ................................................................................................................................. 24
Conclusion :...................................................................................................................... 24
Le PORTB et les interruptions ............................................................................................. 25
Principe de fonctionnement :............................................................................................ 25
Le matériel : ..................................................................................................................... 26
Le programme : ................................................................................................................ 27
Analyse du programme : .................................................................................................. 29
Tests ................................................................................................................................. 34
Conclusion :...................................................................................................................... 34
-2-
Introduction
Après avoir vu dans la première partie comment est structuré le PIC, on va enchaîner
directement sur des montages concrets : rien de mieux pour apprendre et mémoriser
que de faire soi-même un montage « en vrai ». Je vous invite chaleureusement à
faire un tour chez votre marchand de composants électroniques et à faire chauffer
votre fer à souder. Vous allez avoir besoin d’un PIC16F877, un programmateur, une
platine d’essai du genre « pastilles pré-percées » au pas de 2,54 mm, et quelques
composants qui traînent certainement dans vos fonds de tiroirs : Led, régulateur
7805, condensateurs, support 40 broches…
Pour la partie logicielle il vous faudra télécharger la version de démonstration de
SourceBoost, qui comprend le compilateur BoostC que nous allons employer, et
éventuellement un logiciel de transfert comme Icprog pour piloter votre
programmateur de PIC. Si vous avez acheté un programmateur en kit comme le
Velleman K8076, le logiciel « PICPROG » est compris. Pensez à télécharger les
mises à jour.
-3-
Le schéma de base
IC2
D1
9V =
7805
R1
C3 C4
Led1
1 40
2 39 ICSP
3 38
4 37
5 36
6 35
7 34
PIC 16F877
8 33
C6
-4-
9 32
10 31
C5 11 30
12 29
13 28
14 27
15 26
C2 16 25
C1
17 24
Q1
18 23
19 22
20 21
IC1
Le schéma ci-dessus est la base sur laquelle on va pouvoir construire des
applications utiles. Il ne fait rien d’autre que de permettre au PIC de fonctionner, c’est
en quelque sorte le « minimum vital ». Bien entendu, d’autres variantes sont
possibles, mais plutôt que d’en dresser la liste exhaustive, je vous propose de
réaliser cette version et de remettre à plus tard l’étude des différents circuits
d’oscillateur, de reset…
-5-
Le connecteur ICSP : Bien que n’étant pas indispensable, on va équiper
notre platine de ce connecteur « In Circuit Serial Programming », qui nous
permettra de programmer le PIC sans le retirer de son support. A vrai dire, le
brochage n’est pas vraiment normalisé. Je vous propose donc la version
suivante, que vous pourrez adapter à votre guise. Le seul impératif est que les
bonnes lignes du programmateur aboutissent sur les bonnes pattes du PIC
1 6
2 7
3 8
4 9
5 10
-6-
Principe de fonctionnement des ports I/O
Les ports d’entrée / sortie numériques peuvent être considérés comme les
périphériques les plus simples du microcontrôleur. Pour le PIC, on contrôle leur
fonctionnement à l’aide de registres spéciaux (deux registres par port). Par exemple,
pour le port A, on a le registre PORTA et le registre TRISA.
Les registres « TRISx » contrôlent le mode de fonctionnement des entrées / sorties :
selon la valeur de chaque bit, 0 ou 1, la pin correspondante du port fonctionnera soit
en sortie, soit en entrée. Les registres « PORTx » contiennent les données, lues, ou
à écrire sur le port. Certains ports possèdent en plus des fonctionnalités spécifiques :
le PORTB est équipé de résistances de tirage (pull-up) internes qui peuvent être
activées ou non. La pin RB0 du PORTB sert d’entrée d’interruption externe, et les
pins RB4 à RB7 peuvent générer une interruption lorsqu’un changement d’état est
détecté à leurs bornes. Toutes ces particularités sont bien entendu décrites dans les
documentations Microchip spécifiques à chaque composant.
-7-
Utilisation des ports d’entrée/sortie (I/O)
Le matériel :
1 40
2 39
3 38
4 37 VCC
5 36
6 35
R9 R8 R7 R6 R5 R4 R3 R2
7 34
PIC 16F877
8 33
9 32
10 31
11 30
Rd8
12 29
13 28
Rd8
14 27
15 26
Rd6
16 25
17 24
Rd5
18 23
19 22
Rd4
20 21
Rd3
Rd2
Rd1
-8-
La valeur normalisée la plus proche étant 330, on prendra donc pour les résistances
R2 à R9 des 330 Ohms ¼ de Watt.
Pour les résistances Rd1 à Rd8, afin d’assurer une bonne saturation des transistors,
on prendra des résistances de 4,7 kOhms ¼ de Watt.
-9-
Le programme :
On rentre enfin dans le vif du sujet. Afin de piloter notre montage avec un joli effet
« K2000 », je vous propose le programme ci-dessous :
#include <system.h>
//Initialisation port A
porta = 0x00;
//Initialisation port B
portb = 0x00;
//Initialisation port C
portc = 0x00;
//Initialisation port D
portd = 0x00;
//Initialisation port E
porte = 0x00;
//Configuration port A
trisa = 0x00;
//Configuration port B
trisb = 0x00;
//Configuration port C
trisc = 0x00;
//Configuration port D
trisd = 0x00;
//Configuration port E
trise = 0x00;
- 10 -
//Validation des résistances de pull-ups du port B
clear_bit( option_reg, NOT_RBPU );
- 11 -
delay_ms(100);
portd = 0b00001100;
delay_ms(10);
portd = 0b00000100;
delay_ms(100);
portd = 0b00000110;
delay_ms(10);
portd = 0b00000010;
delay_ms(100);
portd = 0b00000011;
delay_ms(10);
}
}
Analyse du programme :
#include <system.h>
Une autre directive de précompilation, qui permet de définir les bits de configuration
du PIC. Pour mémoire, ces bits de configuration permettent de configurer un certain
nombre de paramètres de fonctionnement du PIC. Bien heureusement, SourceBoost
reprend à peu de chose près les mêmes désignations que Microchip :
• PWRTE_OFF : désactive le Power-up Timer Enable bit
• BODEN_OFF : désactive le Brown-Out reset Enable bit
• WDT_OFF : désactive le Watch Dog Timer
• LVP_OFF : désactive le Low Voltage Programming
• CPD_OFF : désactive la protection de la mémoire de données EEPROM ?
• DEBUG_OFF : désactive l’In-Circuit Debugger
• HS_OSC : Configure l’oscillateur en mode HS (High Speed)
• CP_OFF : désactive la protection du code
Pour l’instant on désactive tout (OFF), à part bien sûr l’oscillateur qu’on configure en
mode « HS », mode recommandé par Microchip pour l’emploi d’un quartz 20 MHz.
Si vous utilisez un quartz 4 MHz, le mode le plus approprié sera « XT ». La différence
réside entre HS et XT réside dans le gain de l’amplificateur inverseur du circuit
d’oscillateur du PIC. Plus la fréquence de travail est élevée, plus le gain doit être
- 12 -
élevé. Mais dans la pratique, en mode « HS » et avec un quartz à 4 MHz, ça marche
aussi. Pour savoir quel mode choisir, regardez dans la documentation Microchip sur
la gamme des PIC « mid-range », ou mieux sur la documentation du 16F877 dans la
section « oscillator ».
//Initialisation port A
porta = 0x00;
//Initialisation port B
portb = 0x00;
//Initialisation port C
portc = 0x00;
//Initialisation port D
portd = 0x00;
//Initialisation port E
porte = 0x00;
Les variables porta, portb, portc, portd et porte sont affectées aux registres
correspondant à l’état des Entrées/Sortie des ports. (respectivement le PORTA,
PORTB, PORTC, PORTD, PORTE). Ces variables sont déclarées dans le fichier
d’entête spécifique au PIC 16F877 (fichier avec l’extension « .h » ), lui-même inclus
dans le fichier «system.h». Ainsi, vous pouvez accéder à un registre du PIC
simplement en l’appelant par son nom, sans se soucier de son adresse.
Écrire une donnée dans un de ces registres affectera dès-lors les pins
correspondantes du PORT, si celles-ci sont configurées en sortie. Ici, on initialise tout
à 0.
- 13 -
//Configuration port A
trisa = 0x00;
//Configuration port B
trisb = 0x00;
//Configuration port C
trisc = 0x00;
//Configuration port D
trisd = 0x00;
//Configuration port E
trise = 0x00;
Suivant le même principe que pour les registres porta, portb, portc… les variables
trisa, trisb, trisc, trisd, trise sont affectées aux registres de contrôle de direction des
ports (respectivement le PORTA, PORTB, PORTC, PORTD, PORTE). On affecte au
cinq registres «tris» la valeur hexadécimale (d’où le «x» pour heXa) 00. Un coup
d’œil aux fiches détaillées des registres correspondant nous montre que cette valeur
00 passe toutes les lignes d’Entrés/Sortie des ports en mode « sortie ».
Une remarque : Microchip recommande de d’initialiser en premier lieu les registres
« portx », et de configurer la direction avec les registres « trisx » ensuite. Pour éviter
d’avoir un état incertain sur les pins au moment du démarrage.
Certaines pins des ports sont susceptibles d’être utilisée par le convertisseur
Analogique / Numérique. Par défaut, (voir fiche détaillée du registre adcon1), les pins
en question sont configurées en mode analogique. La valeur hexadécimale 0x06 les
passe en mode numérique.
- 14 -
//Validation des résistances de pull-ups du port B
clear_bit( option_reg, NOT_RBPU );
Comme l’indique le commentaire, il s’agit là d’une boucle sans fin. «While» est une
instruction de contrôle de boucle du langage C. La traduction française donnerait :
« tant que 1… ». Or, «1» n’a pas de raison de changer d’état, donc une fois lancée,
on ne sort pas de cette boucle. Tout le code entre les accolades ouvrante et
fermante se répètera dès lors indéfiniment.
portd = 0b00000001;
delay_ms(100);
- 15 -
portd = 0b00000011;
delay_ms(10);
Après l’étude théorique, il ne reste plus qu’à compiler notre programme et l’injecter
dans le PIC. Pour cela, on va suivre les étapes suivantes :
COMPILATION :
- 16 -
• Si à ce stade tout est ok, déconnectez le programmateur, placez le PIC sur
son circuit ou remettez les ponts à la place du connecteur ICSP de votre
platine prototype.
Tests
Vous pouvez alimenter votre circuit et… He is alive ! Un magnifique effet de balayage
anime vos 8 led. Elle est pas belle la vie ?
Conclusion :
Ca y est, vous avez créé une application à base de microcontrôleur ! Il est vrai que
pour l’instant, l’interaction de notre montage avec le monde réel est très limitée. Il
peut néanmoins servir à créer de jolis effets lumineux. On pourrait également
envisager de s’en servir comme télécommande rustique (à une seule fonction ?) en
montant une led infrarouge et en programmant la séquence d’allumages/extinctions
adéquate.
- 17 -
Utilisation d’un PORT en entrée : un chenillard 8 leds contrôlé par 2
boutons poussoirs.
Étape suivante : on va utiliser des pins d’un autre port en entrée, pour contrôler le
fonctionnement de notre application.
Le matériel :
1 40
2 39
3 38
4 37
5 36
6 35
7 34
RB1
RB0/INT
PIC 16F877
8 33
9 32
10 31
11 30
12 29
13 28 switchon switchoff
14 27
15 26
16 25
17 24
18 23
19 22
20 21
- 18 -
Le programme :
#include <system.h>
//Initialisation port A
porta = 0x00;
//Initialisation port B
portb = 0x00;
//Initialisation port C
portc = 0x00;
//Initialisation port D
portd = 0x00;
//Initialisation port E
porte = 0x00;
//Configuration port A
trisa = 0x00;
//Configuration port B
trisb = 0x03; // On configure les deux pins de poids faible en entrée
//Configuration port C
trisc = 0x00;
//Configuration port D
trisd = 0x00;
//Configuration port E
trise = 0x00;
- 19 -
//Validation des résistances de pull-ups du port B
clear_bit( option_reg, NOT_RBPU );
if (switchon == 0)
{
go = 1;
}
if (switchoff == 0)
{
go = 0;
portd = 0b00000000;
}
if ( go )
{
portd = 0b00000001;
delay_ms(100);
portd = 0b00000011;
delay_ms(10);
portd = 0b00000010;
delay_ms(100);
portd = 0b00000110;
delay_ms(10);
portd = 0b00000100;
delay_ms(100);
portd = 0b00001100;
delay_ms(10);
portd = 0b00001000;
delay_ms(100);
portd = 0b00011000;
delay_ms(10);
portd = 0b00010000;
delay_ms(100);
portd = 0b00110000;
delay_ms(10);
portd = 0b00100000;
delay_ms(100);
portd = 0b01100000;
- 20 -
delay_ms(10);
portd = 0b01000000;
delay_ms(100);
portd = 0b11000000;
delay_ms(10);
portd = 0b10000000;
delay_ms(100);
portd = 0b11000000;
delay_ms(10);
portd = 0b01000000;
delay_ms(100);
portd = 0b01100000;
delay_ms(10);
portd = 0b00100000;
delay_ms(100);
portd = 0b00110000;
delay_ms(10);
portd = 0b00010000;
delay_ms(100);
portd = 0b00011000;
delay_ms(10);
portd = 0b00001000;
delay_ms(100);
portd = 0b00001100;
delay_ms(10);
portd = 0b00000100;
delay_ms(100);
portd = 0b00000110;
delay_ms(10);
portd = 0b00000010;
delay_ms(100);
portd = 0b00000011;
delay_ms(10);
}
}
}
- 21 -
Analyse du programme :
//Configuration port B
trisb = 0x03; // On configure les deux pins de poids faible en entrée
Les deux boutons poussoirs (« switchon » et « switchoff » sur le schéma) sont câblés
sur les pins du port B : RB0 et RB1. Dans l’exemple précédent, ces pins étaient
configurées en sortie. Il nous faut maintenant les configurer en entrée. Pour cela, on
met à 1 les bits correspondants du registre trisb, qui contrôle la direction du PORTB.
Ce qui nous donne, en binaire : 0b00000011, et donc, en hexadécimal : 0x03.
On crée ici deux variables, « switchon » et « switchoff », de type « bit », donc codées
chacune sur 1 bit. Ces variables ne devront pas être placées n’importe où en
mémoire. Ce qu’on veut, c’est les associer aux bits correspondants aux pins RB0 et
RB1 du PORTB. On spécifie donc au compilateur l’adresse de ces variables. Par
exemple, pour switchon, « @0x06.0 », signifie « à l’adresse 0x06, le bit 0 ». 0x06
étant, bien entendu, l’adresse du PORTB. Le « .0 » spécifie le rang du bit, ici le bit de
poids 0, autrement dit RB0. Et pour switchoff, on procède de la même manière mais
en associant le bit « .1 », autrement dit RB1.
L’attribut « volatile » devrait surprendre les familiers du langage C. C’est un ajout de
BoostC au langage C ANSI standard, utilisé pour spécifier au compilateur que la
variable peut être modifiée en dehors du flot normal du programme. Imaginez, vous
venez de lire l’état d’une variable, vous ne l’avez pas modifiée, et voilà qu’elle
change d’état ! Incompréhensible. D’où l’attribut volatile. On avait jusqu’à présent les
variables « const » constantes, les variables standard modifiables par le programme,
et on a maintenant les variables « volatile » dont l’état peut changer en dehors du
programme.
- 22 -
bool go =0; // variable de contrôle
On crée une autre variable, « go », de type « bool » qui ne peut prendre que deux
états : vrai ou faux (1 ou 0). On ne spécifie pas d’adresse, c’est donc le compilateur
qui lui en attribuera une dans l’un des registre généraux de l’espace RAM. On
initialise cette variable à 0. Celle-ci servira de variable de contrôle, nous permettant
de valider ou non l’allumage des 8 leds.
if (switchon == 0)
{
go = 1;
}
if (switchoff == 0)
{
go = 0;
portd = 0b00000000;
}
C’est au tour de switchoff d’être testé. S’il est égal à 0 (le bouton poussoir switchoff
est actionné et ramène le potentiel Vss sur RB1), on exécute la portion de code entre
accolades : on attribue la valeur 0 à la variable go, puis on met toutes les pins du
PORTD à 0 -> toutes les leds sont éteintes.
if ( go )
{
portd = 0b00000001;
delay_ms(100);
...
...
...
}
On teste enfin la variable « go ». Si elle est « vraie » (état =1), on exécute le code
entre accolades qui allume successivement les leds via le PORTD. Si elle est
« fausse » (état = 0) on recommence la boucle en testant switchon, puis switchoff,
puis à nouveau go, etc.
Rien de particulier ici. Procédez comme pour l’exemple précédent, et tout devrait
bien se passer.
- 23 -
Tests
Conclusion :
- 24 -
Le PORTB et les interruptions
Principe de fonctionnement :
- 25 -
Le matériel :
1 40
2 39
3 38
4 37
5 36
6 35
7 34
PIC 16F877
8 33
9 32
10 31
11 30
12 29
13 28 switchint
14 27
15 26
16 25
17 24
18 23
19 22
20 21
- 26 -
Le programme :
#include <system.h>
//***********************************************************************************************
volatile bit switchint@0x06.4; // 4 pour RB4
int i; // variable utilisée comme compteur dans une boucle "for"
bool speed=1; // variable "mémoire"
//***********************************************************************************************
void interrupt( void ) //routine de gestion des interruptions
{
if (intcon & 0b00000001)
{
delay_ms(10); // attente de 10 ms, pour éviter les rebonds du contact
- 27 -
}
}
//***********************************************************************************************
void main( void )
{
//Initialisation port A
porta = 0x00;
//Initialisation port B
portb = 0x00;
//Initialisation port C
portc = 0x00;
//Initialisation port D
portd = 0x00;
//Initialisation port E
porte = 0x00;
//Configuration port A
trisa = 0x00;
//Configuration port B
trisb = 0x10; // On configure la pin RB4 en entrée (0x10 0b00010000)
//Configuration port C
trisc = 0x00;
//Configuration port D
trisd = 0x00;
//Configuration port E
trise = 0x00;
- 28 -
//Boucle sans fin
while( 1 )
{
leds();
}
}
Analyse du programme :
Notre programme nous permet de faire défiler les 8 leds contrôlées par le PORTD :
Contrairement aux exemples précédents où on effectuait un « va et vient » avec un
effet de fondu, les leds sont allumées successivement l’une après l’autre. Puis on
recommence.
Deux vitesses de défilement sont possibles. On passe de l’une à l’autre en
actionnant le bouton poussoir « switchint ».
#include <system.h>
- 29 -
void interrupt( void ) //routine de gestion des interruptions
{
if (intcon & 0b00000001)
{
delay_ms(10); // attente de 10 ms, pour éviter les rebonds du contact
Voici donc la fameuse routine d’interruption ! Il s’agit d’une fonction spéciale chargée
du traitement des interruptions. Pas besoin de l’appeler dans le corps du programme
(contenu dans la fonction main() ), elle sera exécutée automatiquement lorsqu’une
source d’interruption valide se manifestera. Son nom est figé, donc si vous la
remplacez par une fonction « void interruption( void ) », ça ne marchera pas !
SourceBoost ne reconnaitrait pas qu’il s’agit là de la routine de traitement des
interruptions. Quelle que soit la source d’interruption, la fonction interrupt() sera
appelée. Donc, si vous avez plusieurs sources d’interruptions possibles, il faudra
tester quelle est l’origine, afin d’exécuter le traitement adéquat.
C’est ce qu’on fait ici avec l’instruction « if (intcon & 0b00000001) » : on fait un ET
logique entre le registre INTCON et la valeur binaire 0b00000001. Si le bit de poids 0
du registre INCON est égal à 1, le résultat vaudra 1 et la portion de code suivant le
test « if » sera exécutée. Le bit de poids 0 d’INTCON n’est autre que RBIF, bit
« drapeau » de signalisation de l’interruption sur changement du port PORTB.
On aurait pu ici se passer de ce test, étant donné qu’on a qu’une seule source
d’interruption dans notre exemple.
Vient ensuite une temporisation de 10 millisecondes (delay_ms(10) ) qui nous permet
de nous affranchir des rebonds du contact de switchint. Sans cela, ces rebonds
généreraient une multitude d’interruptions successives.
Puis, on test la valeur de switchint, le traitement effectif n’ayant lieu que s’il est à 0,
autrement dit si le bouton poussoir est appuyé. Je dois vous faire remarquer ici que
contre le PIC, vous n’avez aucune chance : il est beaucoup plus rapide que vous !
Une interruption sera générée lorsque vous appuierez sur switchint, mais aussi
lorsque vous le relâcherez ! Mais dans ce dernier cas, switchint sera égal à 1, et le
traitement sera ignoré : la condition du « if » sera fausse.
Vient ensuite le code utile du traitement de notre interruption : on inverse l’état de la
variable « speed » grâce à l’opérateur « ! » (lire « NOT ») qui complémente
l’opérande située à sa droite. Ainsi, « speed » devient égal à l’inverse de « speed ».
Pour mémoire, j’ai mis en commentaire l’instruction « portb = portb ». Pourquoi ? Il
faut savoir ici que l’interruption est générée lorsque l’état physique d’une pin du
PORTB est différent de l’état mémorisé dans son tampon de réception. On a alors un
« mismatch », autrement dit un décalage. Pour supprimer ce décalage, il faut
effectuer une opération de lecture du PORTB pour « réaligner » l’état théorique et
l’état physique du port. Dans notre cas, une lecture de la variable switchint provoque
la lecture du port, c’est donc déjà fait. Mais dans l’hypothèse ou votre code de
- 30 -
traitement de l’interruption n’accéderait pas au PORTB, il faudrait effectuer cette
lecture. Sans quoi, à peine sorti de la routine d’interruption, on y retournerait, le
« mismatch » étant toujours présent !
On efface enfin le bit de signalisation d’interruption, RBIF (poRt B Interrupt Flag bit)
grâce à l’instruction « clear_bit(intcon, RBIF) ».
}
}
- 31 -
void main( void )
{
//Initialisation port A
porta = 0x00;
//Initialisation port B
portb = 0x00;
//Initialisation port C
portc = 0x00;
//Initialisation port D
portd = 0x00;
//Initialisation port E
porte = 0x00;
//Configuration port A
trisa = 0x00;
//Configuration port B
trisb = 0x10; // On configure la pin RB4 en entrée (0x10 0b00010000)
//Configuration port C
trisc = 0x00;
//Configuration port D
trisd = 0x00;
//Configuration port E
trise = 0x00;
On entre ensuite dans le corps du programme (la fonction « main »), la seule
différence par rapport à l’exemple précédent étant la valeur affectée au registre
« trisb » : seule la pin RB4 est configurée en entrée.
- 32 -
//désactivation (temporaire) du mécanisme de gestion des interruptions
clear_bit( intcon, GIE );
Puis vient enfin, comme dans les exemples précédents, une boucle sans fin qui se
contente d’appeler la fonction « leds() » définie précédemment.
- 33 -
Tests
Conclusion :
Vous connaissez maintenant le mécanisme de base de gestion des interruptions.
Les bits de validation (RBIE) et de signalisation (RBIF) de l’interruption sur
changement du PORTB étant présents dans le registre INTCON, nous n’avons pas
eu pour l’instant à nous préoccuper des registres PIE1, PIR1, PIE2, PIR2.
Mine de rien, notre dernier exemple nous a fait passer d’une programmation
« séquentielle », où toutes les actions étaient effectuées dans l’ordre, l’une à la suite
de l’autre, à une programmation « événementielle » capable de prendre en compte
un événement à tout moment du déroulement du programme.
Autrement dit : on devient bons !
- 34 -