1. Écriture d’un programme pour le PIC

Comme dans n’importe quel système à microprocesseur, il est nécessaire de préparer un programme qui permettra au PIC d’effectuer son travail. Un programme est constitué d’une liste d’instructions en séquence, chacune d’entre elles identifiant très précisément les fonctions de base que le PIC est capable d’effectuer. Chaque instruction est représentée par un code d’opération (OPCODE en anglais) de 14 bits et est mémorisée dans une des mémoires flash qui contient 1024 emplacements. Chaque OPCODE est représenté par un mnémonique plus facile à retenir par l’utilisateur.

Exemple : l’OPCODE du mnémonique RETURN est : 00 0000 0000 1000

Il est plus facile de représenter un OPCODE dans sa forme hexadécimale car celle-ci est plus adéquate à la manipulation des données.

La combinaison des mnémoniques et des nombres en hexadécimal est le langage assembleur.

Un programme en langage assembleur peut être écrit sur un PC en utilisant un éditeur de texte capable de générer des fichiers de type ASCII (American Standard Code for Information and Interchange). Ce fichier texte est appelé fichier source assembleur. Une fois l’écriture du programme source assembleur terminée, le fichier doit être sauvegardé avec l’extension (.ASM).

La syntaxe utilisée ne doit pas contenir d’accent, d’espace inutile, de cédille, de parenthèse etc.

À la suite d’une ligne d’instructions, le programmeur peut écrire un commentaire à la condition qu’il soit précédé d’un point virgule (;). Notez que tout texte après un point virgule sera ignoré par le programme compilateur. Les commentaires servent à aider un utilisateur à comprendre votre démarche, ils ne sont pas obligatoires mais un bon programmeur les fera toujours.

8.1. Organisation d’un fichier (.ASM) : on prendra comme exemple le laboratoire #1

;=========================#1=================================

; Programme pour le pic 16F84-04. Ce programme fait clignoter une led placée sur RB1

; (T=1 sec.)

; Titre : Led clignotante

; Auteur : Dany Amer

;=========================ENTÊTE==============================

list p=16f84, w=2 ;Identification du microcontrôleur, niveau d’erreur

__config 0x3ff1 ;Pas de protection de lecture de la mémoire, oscillateur à cristal

;Watchdog à Off, Power up timer à "On"

;============================================================

;Étiquette==Mnémonique=Opérande==========Commentaire===================

; Équivalence des registres

; *********************

portb equ 0x06 ;Adresse du registre portb pour utilisation

trisb equ 0x86 ;Adresse du registre trisb pour utilisation

status equ 0x03 ;Adresse du registre status pour utilisation

; Réservation mémoire

; *****************

compteur0 equ 0x0d ;Identifie un registre à l'adresse 0x0d au nom de compteur0

compteur1 equ 0x0e ;Identifie un registre à l'adresse 0x0e au nom de compteur1

compteur2 equ 0x0f ;Identifie un registre à l'adresse 0x0f au nom de compteur2

;============================================================

; Initialisation du programme

; **********************

org 0x00 ;Origine de départ (adresse sélectionnée après un reset)

goto debut ;L'adresse 04 est réservée aux interruptions, il faut

       ;donc sauter par dessus

debut

org 0x05 ;Origine du programme

bsf status,05 ;Sélection de bank1 pour l'accès au trisb

clrf trisb ;Déclaration du portb en sortie

bcf status,05 ;Sélection de bank0 pour accès au portb

clrf portb ;Place toutes les sorties du portb au niveau logique 0

; Programme principal

; *****************

boucle

movlw 0x02 ;Charge dans l'accumulateur la valeur 02

movwf portb ;Place le contenu de w dans le registre portb pour allumer

;la led placée sur la sortie RB1 du portb

call pause ;Appelle la routine étiquette pause

movlw 0x00 ;Charge dans accumulateur la valeur 00

movwf portb ;Place le contenu de w dans le registre portb pour éteindre

;la led placée sur la sortie RB1 du portb

call pause ;Appelle la routine étiquette pause

goto boucle ;Va à l'étiquette nommée boucle

;============================================================

; Temporisation de 0.5 seconde

; ************************

pause

movlw 0xff ;Charge dans l'accumulateur la valeur ff (255)

movwf compteur2 ;Transfert w dans le registre "compteur2"

load1

movlw 0x19 ;Charge l'accumulateur à 19 (25)

movwf compteur1 ;Transfert w dans le registre "compteur1"

load0

movlw 0x1a ;Charge l'accumulateur à 1a (26)

movwf compteur0 ;Transfert w dans le registre "compteur"

dec

decfsz compteur0,1 ;Décrémente le registre "compteur0", saute l'instruction

;suivante si le résultat est zéro

goto dec ;Saute inconditionnellement à "dec"

;Un cycle d’instruction utilise quatre périodes d’horloge

;Décrémente = 1cycle, goto = 2cycles soit au total 3 cycles

;ou 3 s, t = 3 micro-secondes x contenu du registre

;"compteur0" = 3 x 26 = 0.078ms

decfsz compteur1,1 ;Décrémente le registre "compteur1", saute l'instruction

;suivante si le résultat est zéro

goto load0 ;Va à "delai1" t = 3 x 26 x 25 = 1.95 ms

decfsz compteur2,1 ;Décrémente le registre "compteur2", saute l'instruction

;suivante si le résultat est zéro

goto load1 ;Va à "delai2" t = 3 x 26 x 25 x 255 = 0.49725 seconde

return ;Retour à la ligne suivant l'appel de l'étiquette "pause"

end

;============================================================

8.2. L’identification

Si vous regardez attentivement, vous remarquerez que les trois premières lignes sont précédées du symbole (;). Tout ce qui suit est une zone de commentaires, vous pouvez y inscrire ce que vous voulez. On utilisera cette zone comme zone d’identification dans laquelle on inscrira : le titre du programme, une petite description de ce qu’il fait, la date d’édition, le nom de l’auteur.

Les doubles tirets qui encadrent ce paragraphe ne servent que pour l’esthétique, ils sont précédés du point virgule et ne sont donc pas pris en considération par le programme.

8.3. Les directives

Les directives ne font pas partie du programme, elles ne sont pas traduites en OPCODE, elles servent à indiquer à l’assembleur de quelle manière il doit travailler. Ce sont donc des commandes destinées à l’assembleur lui-même.

Par contre, les instructions seront traduites en OPCODE et chargées dans le PIC. Il faut donc faire la distinction.

§ La directive "list"

La directive "List" en bas de l’entête est destinée au compilateur pour lui indiquer quel type de processeur est utilisé (ici : Pic16F84).

§ Les directives "__config"

La ligne "__config" est aussi destinée au compilateur pour établir les bits de configuration de la case mémoire 0x2007 qui comporte 14 bits.

Les bits 0 et 1 déterminent le type d’oscillateur utilisé ;

Il y a quatre (4) modes d’horloge possibles :

Ø    RC (résistance, capacité), 26.2Khz à 4610Khz ;

Ø    LP (faible consommation) quartz ou oscillateur externe. 32 Khz, 200Khz ;

Ø    XT (quartz ou résonateur céramique ou oscillateur externe. 100 Khz, 455Khz, 2 Mhz, 4Mhz).

Ø    HS (haute vitesse : 8Mhz, 10Mhz) quartz ou résonateur céramique ou oscillateur externe.

Le bit 2 autorise ou non le fonctionnement du watchdog timer ;

Ø    Principe

C’est un système de protection contre un blocage de programme. 

Par exemple, si le programme attend le résultat d’un système extérieur (conversion analogique numérique par exemple) et qu’il n’y a pas de réponse, il peut rester bloqué. Pour en sortir, on utilise un chien de garde. Il s’agit d’un compteur qui, lorsqu’il arrive en fin de comptage, permet de redémarrer le programme. Il est lancé au début du programme. En fonctionnement normal, il est remis à zéro régulièrement dans une branche du programme qui s’exécute régulièrement. Si le programme est bloqué, il ne passe plus dans la branche de remise à zéro et le comptage va jusqu’au bout et déclenche le chien de garde qui relance le programme.

Ø    Mise en service

Elle se décide lors de la programmation physique du PIC. Elle ne peut pas être suspendue pendant l’exécution d’un programme. Elle est définitive jusqu’à une nouvelle programmation de la puce.

La directive de programmation __config permet de valider (option_WDT_ON) ou non (option_WDT_OFF) le chien de garde. La mise en service peut aussi être réalisée directement par le programmateur. L’inconvénient de cette seconde solution est que le code du programme ne contient pas l’information; la mise en service du chien de garde peut être oubliée lors du téléchargement et générer un fonctionnement incorrect du programme en cas de blocage.

Ø    Gestion

Une fois le chien de garde mis en service, il faut remettre le comptage à zéro régulièrement. Cette opération est réalisée par l’instruction CLRWDT. Tant que le programme se déroule normalement, cette instruction est exécutée régulièrement et le chien de garde ne s’active pas. Si un blocage apparaît, la remise à zéro n’a pas lieu et le chien de garde est activé. Le PIC redémarre alors à l’adresse 0000h et le bit TO (Time out bit) (status, 4) est mis à 0. Le test de ce bit au début du programme permet de savoir si le système vient d’être mis sous tension (TO=1) ou si le chien de garde vient de s’activer (TO=0)

Ø    Choix de la durée

Le chien de garde possède sa propre horloge. Sa période de base est de 18 ms. Le pré-diviseur de fréquence utilisé par le compteur est partagé avec le chien de garde. Si le bit PSA (OPTION_REG, 3) est à 1, le pré-diviseur est assigné au chien de garde. Huit (8) valeurs de 1 à 128 sont disponibles, ce qui permet d’aller jusqu’à 128 x 18 ms = 2.3 s avant le déclenchement du chien de garde.

Le bit 3 autorise ou non le fonctionnement du power up timer (délai de 72 ms à l'allumage qui prolonge le Power On Reset) ;

Les bits 4 à 13 permettent d'interdire la lecture de la mémoire programme.

Code Protect

Power Up Timer

Watchdog

Oscillateur

bit 4

bit 3

bit 2

bit 1, bit 0

0 => Oui

0 => Oui

0 => Non

00=>LP, 01=>XT

1 => Non

1 => Non

1 => Oui

10=>HS, 11=>RC

Les 5 bits sont à 1 par défaut soit : pas de Code Protect, pas de Power UP Timer, Watchdog actif, oscillateur RC

Dans le cas du Travail #1, on utilise un oscillateur à cristal, le "watchdog" est à "Off", Le "power up timer" est à "On" et la protection en lecture est inactive. Ce qui donne pour la directive __config 3FF1

Bit13

Bit12

Bit11

Bit10

Bit9

Bit 8

Bit 7

Bit 6

Bit 5

Bit 4

Bit 3

Bit 2

Bit 1

Bit 0

1

1

1

1

1

1

1

1

1

1

0

0

0

1

§ La directive « #define » (définition)

Une définition « #define » permet de remplacer un texte complexe par un nom particulier qu’on veut lui donner.

Dans le programme, on peut par exemple donner le nom "stat" au bit 5 du registre status (status,05 = stat). Il faut alors le définir dans le programme en utilisant la directive #define.

#define stat status,05

Pour mettre à 1 le bit 5 du registre status, on écrira alors :

bsf stat au lieu de bsf status,05

§ La directive « #include »

Afin d’éviter l’écriture d’une multitude de lignes d’assignation au début de chaque programme, on peut les regrouper dans un fichier et les appeler par une seule commande :

#include "P16F84.INC" ou #include <P16F84.INC>

§ La directive « org »

La directive "org" précise à quelle adresse sera placée l’instruction qui suit. Après un reset ou une mise sous tension, le PIC démarre toujours à l’adresse 0x00, c’est donc à cette adresse que doit débuter votre programme.

L’adresse 0x04 est utilisée par les interruptions. Nous devons donc sauter par-dessus vers le début de notre programme principal.

Exemple :

org 0x00

goto debut

debut org 0x05

La première ligne est une directive qui indique que la ligne suivante sera placée à l’adresse 0x00.

La deuxième ligne est une instruction qui indique au PIC que le programme doit sauter à l’adresse 0x05 représentée par l’étiquette "debut" (voir étiquette plus loin).

§ La directive « end »

Cette directive indique l’endroit où doit cesser l’assemblage de votre programme. Elle est obligatoire sinon une erreur vous signalera que la fin de fichier a été atteinte sans rencontrer la directive "end" (End Of File). Les instructions après la directive "end" sont tout simplement ignorées.

8.4. Les assignations ou directive « equ »

Les lignes commençant à "portb" et finissant à "compteur2" sont des assignations. Elles signalent à l’assembleur les valeurs (adresses) de toutes les constantes que nous allons utiliser. Il est en effet plus facile de retenir port B que de manipuler la valeur 0x06.

Une assignation est une simple substitution, elle associe un nombre à une étiquette. Au moment de l’assemblage, chaque assignation rencontrée sera remplacée par sa valeur.

Si vous remplacez la valeur d’une assignation, le changement sera effectif pour tout le programme. Vous ne risquez donc pas d’oublier des valeurs en chemin.

8.5. Les macros

Une macro remplace un morceau de code que nous utilisons souvent dans le programme. Elle fonctionne comme un simple traitement de texte.

Exemple :

Repete macro

movlw 0x00

movwf portb

call pause

endm

La macro se compose d’un nom écrit en première colonne suivi de la directive "macro", de une ou plusieurs instructions et de fin de macro "endm" (end of macro).

Chaque fois que le mot "repete" sera rencontré, il sera remplacé au moment de l’assemblage par les lignes d’instructions de la macro soit :

movlw 0x00

movwf portb

Call pause

8.6. Le programme principal

Dans la mémoire programme, les instructions sont codées en suites de 0 et de 1, nous, nous préférons écrire des choses comme goto ou clrw, l'assembleur se charge de la traduction. Chaque ligne peut contenir jusqu’à 4 types d’informations appelées champs.

Les quatre champs (ou colonnes) sont : l’étiquette, le mnémonique (OPCODE), l’opérande et le commentaire.

La syntaxe doit être la suivante pour l’assembleur MPLAB, que nous utiliserons dans les laboratoires. Nous avons dans l’ordre :

- Étiquette (facultative)

- Espace(s) ou tabulation(s)

- Mnémonique (en majuscules ou minuscules)

- Tabulation ou Espace(s)

- Opérande ou la valeur

- Virgule éventuelle de séparation

- Bit de destination W ou F ou éventuellement numéro du bit de 0 à 7 si nécessaire

- Espace(s) ou tabulation(s)

- Point-virgule (facultatif si pas de commentaire)

- Commentaire. (facultatif)

Notez que le mnémonique ne peut pas se trouver en première colonne, et que tout ce qui suit le point-virgule est ignoré de l’assembleur (donc c’est la zone commentaire).

La première colonne est réservée pour les étiquettes (repères)

Vous disposez également de la possibilité d’insérer un ou plusieurs espace(s) ou tabulation(s) de chaque côté de la virgule.

  • Étiquette :

L’étiquette permet de donner un nom à un nombre (adresse, variable) ou à une ligne du programme. Elle commence par un caractère alphanumérique (ou un souligné), dans ce cas, le souligné ne doit pas être suivi par un chiffre. Les caractères utilisables sont lettres, chiffres, souligné et point d’interrogation. Leur longueur maximale est de 32 caractères. Par défaut il y a une différence entre une majuscule et une minuscule.

L’étiquette en début de ligne de programme commence en colonne 1; elle est suivie par un espace, une tabulation, par deux points ( : ) ou par un retour de chariot si elle est seule sur la ligne. Derrière un call ou un goto, elle est en colonne #3.

L’étiquette, représentant un nombre, est placée conformément à la directive ou à l'instruction qui l'utilise.

  • Mnémonique :

C’est l'instruction elle-même qui doit commencer en colonne 2. S’il y a une étiquette en colonne 1, il doit y avoir un ou plusieurs espace, deux points ( : ) ou une tabulation avant le mnémonique.

  • Opérande(s) :

L’opérande vient après le mnémonique, il complète l’instruction. Il doit être séparé de l'instruction par un ou plusieurs espace, par deux points ( : ) ou par une tabulation. S’il y a plusieurs opérandes, ils sont séparés par des virgules.

  • Commentaire :

Tout texte commençant par un point virgule est un commentaire. Il n’est pas pris en compte par la compilation jusqu’à la fin de la ligne sauf si le point virgule fait partie d’une chaîne de caractères.

Remarque : Une ligne contient 255 caractères au maximum.

8.7.  Mode sommeil 

§         Principe

Lorsque le PIC n’a rien à faire (par exemple lors de l’attente d’une mesure extérieure), ce mode est utilisé pour limiter sa consommation : le PIC est mis en sommeil (le programme s’arrête) jusqu’à son réveil (le programme repart). Ce mode est principalement utilisé pour les systèmes embarqués fonctionnant sur pile. 

§         Gestion 

Mise en sommeil : La mise en sommeil est réalisée grâce à l’instruction SLEEP. La séquence suivante est exécutée : 

-         Le chien de garde est remis à 0 (équivalent à CLRWDT)

-         Le bit TO (Time Out bit) (status, 4) est mis à 1

-         Le bit PD (Power Down bit) (status, 3) est mis à 0

-         L’oscillateur est arrêté ; le PIC n’exécute plus d’instruction 

Réveil : ce mode n’est intéressant que si l’on peut en sortir pour relancer le programme. Trois événements permettent de sortir le PIC du sommeil. 

-       Application d’un niveau 0 sur MCLR (broche 4). Le pic effectue alors un reset et relance le programme à partir de l’adresse 0000h. Les bits NOT_TO (status, 4) et NOT_PD (status, 3) permettent à l’utilisateur de savoir quel événement a lancé le programme (mise sous tension, reset, chien de garde)

-       Activation du chien de garde. Le programme reprend à l’instruction suivant le SLEEP

-       Apparition d’une interruption (RB0/INT, RB4 à RB7 ou EEPROM). Il faut pour cela que les bits de validation spécifique des interruptions concernées soient positionnés. Si le bit de validation générale des interruptions (GIE) est à 0 (pas de validation des interruptions), le programme reprend après l’instruction SLEEP comme pour le chien de garde. Si le bit de validation générale des interruptions (GIE) est à 1 (validation des interruptions), l’instruction suivant le SLEEP est exécutée et la fonction d’interruption liée à l’événement qui a réveillé le PIC est exécutée. 

9.    Reset : 

Il y a plusieurs possibilités de Reset :

§      Reset à l'allumage (POR)

§      Reset externe par mise à la masse de l'entrée MCLR

§      Reset après Watchdog

Le Power-up Timer (PWT) produit un délai de 72 ms à l'allumage.

L'oscillateur Start-up Timer (OST) maintient le 16F84 en position reset jusqu'à ce que l'oscillateur à quartz se soit stabilisé. 

10.  Registre OPTION_REG (0x81)

C’est un registre de bits où chaque bit a un rôle particulier. Il se trouve à l’adresse 0x81, dans la banque 1. Le contenu de ce registre est :

BIT 7: NOT_RBPU (Pull-up Enable Bit)

Quand ce bit est mis à 0 (actif au niveau bas), une résistance de rappel au +5 volts est placée sur chaque broche du port B. Cette option valide les résistances sur toutes les broches du port B en même temps. Cette fonction n’existe pas pour le port A.

Quand ce bit est à 1, les résistances sont désactivées.

BIT 6: INTEDG (Interrupt Edge Select Bit)

Ce bit donne le sens de déclenchement de l’interruption sur la broche RB0/INT

INTEDG = 1, on a interruption si le niveau sur RB0/INT passe de 0 à 1

INTEDG = 0, l’interruption s’effectuera lors de la transition de 1 vers 0 sur la broche RB0/INT. 

BIT 5: T0CS (TMR0 Clock Source Select Bit)

Ce bit détermine le fonctionnement du timer 0.

T0CS = 1, compte les impulsions reçues sur la broche RA4

T0CS = 0, incrémenté en fonction de l’horloge interne

BIT 4 : T0SE (TMR0 Source Edge Select Bit)

Ce bit donne le sens de la transition qui détermine le comptage du TMR0 

T0SE = 1, l’incrémentation du TMR0 se fait lors de la transition de + 5 volts à 0 sur la broche RA4/T0CKI.

T0SE = 0, l’incrémentation du TMR0 se fait lors de la transition de 0 à + 5 volts sur la broche RA4/T0CKI

Si le BIT 5 est à 0, Le BIT 4 n’est pas utilisé et sera maintenu à 0

BIT 3 : PSA (PreScaler Assignment Bit)

PSA = 1, effectue une prédivision au niveau du timer du watchdog

PSA = 0, effectue une prédivision du timer 0 (TMR0) 

BIT 2-0 : PS2-PS1-PS0 (Prescaler Rate Select Bits)

Ces trois bits déterminent la valeur de prédivision pour le registre déterminé au BIT3 

Bit Value

TMR0 Rate

WDT Rate

000

1:2

1:1

001

1:4

1:2

010

1:8

1:4

011

1:16

1:8

100

1:32

1:16

101

1:64

1:32

110

1:128

1:64

111

1:256

1:128

Si on ne désire pas utiliser de prédivision, il faut mettre le BIT 3 à 1 (Prédivision sur watchdog) et les BITS 2-0 à 0 ce qui correspond à une prédivision (1:1) soit pas de prédivision. 

11.    Les interruptions 

Il y a quatre (4) sources d'interruption : 

a)       Externe par la broche RB0/INT

b)       Par dépassement du registre timer (TMR0)

c)       Par changement d’état des broches 4 à 7 du portb

d)       À la fin de l’écriture des données dans l’EEPROM. 

Imaginez une conversation normale : 

§           Chaque interlocuteur prend la parole quand vient son tour de parler.

§           Survient un événement extérieur dont le traitement est urgent. Par exemple un bloc de béton tombe du deuxième étage de l’immeuble au pied duquel vous discutez.

§           Votre interlocuteur ne va pas attendre la fin de votre conversation pour vous signaler le danger. Il va donc vous interrompre durant le cours normal de votre discussion afin d’écarter le danger et traiter cet événement extérieur.

§           Les interlocuteurs reprendront la conversation sitôt le danger écarté. 

Pour le PIC, le principe est le même : 

§           Le programme se déroule normalement

§           Survient un événement spécifique. Le programme principal est interrompu, l’événement est traité avant de retourner à l’endroit de l’interruption.

L’interruption est une rupture de séquence asynchrone. Pour la déclencher, il faut que deux conditions principales soient remplies : 

§           Elle doit figurer dans la liste des événements du processeur

§           L’utilisateur doit l’avoir autorisée.

11.1.           Principe

Lorsqu’une interruption est provoquée :

§      Le programme termine l’instruction en cours

§      Il arrête la procédure qu’il était en train d’exécuter

§      Il passe à l’adresse 0x04

§      À cette adresse doit se trouver l’instruction qui envoie sur la routine de traitement de l’interruption

§      À la fin de cette routine de traitement, il reprend la procédure initiale là où elle était.

11.2.           Registre INTCON (0x0B et 0x8B) est le registre qui gère les interruptions. Son écriture permet de les autoriser ou non et sa lecture de déterminer le type d’interruption qui s’est produite.

Bit 7 : GIE (Global Interrupt Enable)

Autorise ou non les interruptions, quelles qu’elles soient

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset”

GIE = 0 interdit toutes les interruptions

GIE = 1 autorise toutes les interruptions non masquées

Il passe seul à 0 pendant le traitement d’une interruption, il est remis à 1 par l’instruction RETFIE (Return From Interrupt)

BIT 6: EEIE (EEprom Interrupt Enable)

Fonctionne en lecture/écriture

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset”  

EEIE = 0 interdit l’interruption en fin de programmation EEPROM

EEIE = 1 autorise l’interruption en fin de programmation EEPROM

BIT 5 : T0IE (Timer 0 Interrupt Enable).

Qu’est-ce que le TIMER0 ?

Le TIMER0 est en fait un compteur avec lequel :

§           On peut compter les impulsions reçues sur la broche RA4/T0CKI. On dira dans ce cas que nous sommes en mode “COMPTEUR”.

§           On peut compter les cycles d’horloge du PIC lui-même. Dans ce cas, comme la fréquence de l’horloge est fixe, nous comptons le temps. On est alors en mode “TIMER”.

La sélection de l’un ou l’autre de ces modes de fonctionnement se fait par le bit 5 (T0CS = Timer0 Clock Source Select bit) du registre OPTION_REG.

Le registre TMR0 qui se trouve à l’adresse 0x01 en Bank0 contient tout simplement la valeur actuelle du timer0.

On peut écrire ou lire dans le registre TMR0. Si ce registre est configuré en lecture, sa lecture nous donnera le nombre d’événements survenus sur sa broche RA4/T0CKI. Il faut auparavant préciser le sens de la transition qui permet d’effectuer le comptage. C’est le bit 4 (T0SE = Timer0 Source Edge Select bit) du registre OPTION_REG qui permet cette précision

Le registre TMR0 ne possède que 8 bits, il ne peut donc compter que jusqu’à 255 (FF). Tout débordement du timer0 (passage de 0xFF à 0x00) positionne le “flag” T0IF du registre INTCON. On peut donc consulter ce “flag” pour savoir s’il y a débordement du timer.

Si on autorise une interruption par timer0 en positionnant le bit 5 (T0IE = Timer0 Interrupt Enable) à 1, chaque fois qu’une interruption est générée, le “flag” T0IF passe à 1. 

Avec un quartz de 4 Mhz, sachant qu’un cycle d’instruction dure 4 périodes d’horloge, nous avons une interruption toutes les 256 ms en mode timer0.

Si on veut augmenter ce temps, on utilisera un prédiviseur et des sous routines de temporisation. Un prédiviseur étant un circuit diviseur d’événements situé avant l’entrée de comptage du timer0. Les divisions de fréquence s’échelonnent de 2 à 256 (voir les bits 0 à 2 du registre OPTION_REG).

Ce bit 5 fonctionne en lecture/écriture

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset”

T0IE = 0 interdit l’interruption due au dépassement du timer

T0IE = 1 autorise l’interruption due au dépassement du timer

BIT 4 : INTE (Interrupt Enable)

Fonctionne en lecture/écriture

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset”

INTE = 0 interdit l’interruption par l’entrée RB0/INT (broche 6)

INTE = 1 autorise l’interruption par l’entrée RB0/INT (broche 6)

BIT 3 : RBIE (Register B Interrupt Enable)

Fonctionne en lecture/écriture

Autorise ou non les interruptions par changement d’état sur le port B,    entrées 4 à 7

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset” 

RBIE = 0 interdit les interruptions par changement d’état sur le port B

RBIE = 1 autorise les interruptions par changement d’état sur le port B

BIT 2 : T0IF (Timer 0 Overflow Interrupt Flag)

Fonctionne en lecture/écriture

Fonctionne si le bit 5 autorise les interruptions dues au dépassement timer et signale alors un éventuel dépassement

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset”

T0IF = 0 le compteur timer TMR0 n’a pas effectué de dépassement

T0IF = 1 le compteur timer TMR0 a effectué un dépassement 

BIT 1 : INTF (Interrupt Flag)

Fonctionne en lecture/écriture

Fonctionne si le bit 4 autorise les interruptions par l’entrée RB0/INT (broche 6) et signale alors une interruption valide sur cette broche 6

Passe à 0 après un “Power On Reset”

Passe à 0 après tous les autres “Reset”

INTF = 0 pas d’interruption sur RB0/INT

INTF = 1 une interruption valide s’est produite sur RB0/INT

BIT 0 : RBIF (Register B Interrupt Flag)

Fonctionne en lecture/écriture

Fonctionne si le bit 3 autorise les interruptions par changement d’état sur le port B et signale alors une interruption valide sur le port B

État aléatoire après un “Power On Reset”

Inchangé après tous les autres “Reset”

RBIF = 0 aucune broche RB4, RB5, RB6 et RB7 n’ a changé d’état

RBIF = 1 l’une des broches RB4, RB5, RB6 et RB7 a changé d’état. 

Pendant la routine d’interruption, les autres interruptions ne sont pas traitées.

Les interruptions quelles qu’elles soient ne sont autorisées que si le bit 7 (GIE) du registre INTCON (0x0b) est à 1.

§         Après un reset, ce bit est à 0

§         Pendant l’interruption, il passe à 0

§         Après un retfie, il revient à 1 

11.3.     Sauvegarde des registres

La seule chose dont le programme se souvient, après être parti en interruption est l’instruction à laquelle il doit revenir. Il faut donc sauvegarder, dans la routine d’interruption, successivement les registres “W” et “STATUS” sans modifier leur contenu. 

11.4.     Interruption externe (RB0/INT) 

L’interruption se produit sur le front montant ou descendant d’une impulsion appliquée sur la broche 6 (RB0/INT). Il s’agit du front montant si le bit 6 (INTEDG) du registre OPTION_REG est à 1 et d’un front descendant s’il est à 0.

L’interruption externe est autorisée si le bit 4 (INTE) du registre INTCON est à 1

§         Après un reset, ce bit est 0

§         Il faut le mettre à 0 avant de le porter à 1

Lorsqu’une interruption valide a eu lieu, le bit 1 (INTF) du registre INTCON passe à 1, pensez donc à le remettre à 0.

Exemple d’interruption : on veut allumer une lumière placée sur RA2 à partir d’un bouton poussoir (BP) placé sur RB0/INT. (voir figure suivante) 

§         Une pression sur BP allume la Led

§         Une autre pression éteint la Led

§         Et ainsi de suite

La séquence est la suivante :

Dans notre cas cela correspond à :

On éteint la Led

Le programme principal boucle sur Led éteinte

Une interruption survient (appui sur BP)

Traitement de l’interruption (charge de 04 dans W puis application de la fonction OU EXCLUSIF entre W et Port A è Led allumée

Le programme principal boucle sur Led allumée jusqu’à la prochaine interruption.

11.5. L’anti-rebond

Imaginez un bouton poussoir géant.

Lorsque la barre tombe sur les contacts, elle rebondit plusieurs fois. Il y a donc une série de fermetures et d’ouvertures du bouton poussoir. C’est ce que le Pic, qui est un composant rapide (1 ms par instruction) qui ne travaille pas à la même échelle de temps que nous, voit lorsque nous appuyons sur le bouton poussoir.

Pour remédier à ce problème, il faut tout simplement attendre un temps supérieur au temps de rebondissement avant d’autoriser une nouvelle interruption sur RB0.

11.6. Explications

Le programme principal effectue normalement ses initialisations puis teste si une interruption est survenue en vérifiant le "flag" TEMPO. Si le "flag" n’est pas à 1, il boucle sans fin.

Si le bouton poussoir est appuyé, une interruption est générée, RA2 est inversé, la routine d’interruption positionne le "flag" TEMPO à 1 et interdit toute nouvelle interruption de RB0 (toute autre action sur BP sera sans effet pour ne pas prendre en compte les rebonds).

La routine d’interruption prend fin. Retour au programme principal qui continue alors à tester le "flag" TEMPO qui vient d’être positionné par la routine d’interruption. Appelle de la routine de temporisation dont la durée est supérieure à celle des rebonds.

Après écoulement du temps nécessaire à la fin des rebonds, les "flags" TEMPO et INTE (RBO/INT) sont remis à 0 et les interruptions sont à nouveau autorisées afin de permettre de prendre en compte une nouvelle pression sur le BP.

11.7. Finalisation du programme

;=======================================================

; Programme pour le pic 16F84. Ce programme allume une lumière placée sur RA2 à l'aide

; d'un bouton poussoir placé sur RB0/INT

; Titre : Télérupteur version 1

; Auteur : Dany Amer

; Remarque : Anti-rebond de 390 ms (temporisation) avec un quartz de 4MHz

; Fichier requis : p16f84.inc

;=======================================================

;=====================ENTÊTE==============================

list p=16f84, w=2 ; Définition de processeur, niveau d’erreur

#include <p16f84.inc> ; Définition des constantes

__config _cp_off & _wdt_off & _pwrte_on & _xt_osc

;======================================================

; ASSIGNATIONS

; *************

optionval equ 0x00 ; Valeur du registre "option"

; Résistances pull-ups "ON"

; Interruption RB0/INT sur front descendant

intermask equ 0x90 ; Masque d'interruption. Autorisation générale

; d'interruption et permission de l'interruption RB0/INT

;=======================================================

; DÉFINITIONS

; **********

#define led porta, 2 ; LED de sortie

#define tempof flags, 0 ; Drapeau d'interruption pour temporisation contre les

    ; rebonds du bouton poussoir

;=======================================================

; MACRO

; *****

bank0 macro

bcf status, rp0 ; Passer en banque0

endm

bank1 macro

bsf status, rp0 ; Passer en banque1

endm

;=======================================================

; DÉCLARATION DE VARIABLES

; ***********************

cblock 0x0c ; Début de la zone variables

w_temp :1 ; Emplacement de sauvegarde du registre W

status_temp : 1 ; Emplacement de sauvegarde du registre STATUS

compteur0 : 1 ; Emplacement du compteur

compteur1 : 1 ; Emplacement du compteur1

compteur2 : 1 ; Emplacement du compteur2

flags : 1 ; Emplacement du bit b0 réservé pour le "flag" tempo

endc ; Fin de la zone de stockage

;=======================================================

; DÉMARRAGE SUR RESET

; *******************

org 0x00 ; Adresse de départ après reset

goto init ; Aller à l'étiquette "init"

;=======================================================

; ROUTINE INTERRUPTION

; ********************

; sauvegarde des registres

; ----------------------------

org 0x04 ; Adresse d'interruption

movwf w_temp ; Sauvegarder le registre W

swapf status, w ; Swap status avec résultat dans w

movwf status_temp ; Sauvegarder status "swappé"

call intrb0 ; Traiter l'interruption RB0/INT

; Restauration des registres

; -----------------------------

swapf status_temp, w ; Swap ancien status, résultat dans w

movwf status ; Restaurer status

swapf w_temp, f ; Inversion L et H de l'ancien W sans modifier Z

swapf w_temp, w ; Réinversion de L et H dans W

      ; W restauré sans modifier status

retfie ; Retour à la ligne suivant l'appel d'interruption

;=======================================================

; INTERRUPTION RB0/INT

; *******************

intrb0

movlw 0x04 ; Bit à inverser

bank0 ; Passer en banque0

xorwf porta , f ; Inverser LED

bcf intcon, inte ; Interdire une autre interruption sur RB0/INT

bsf tempof ; Positionner le "flag" tempo

return ; Fin d'interruption RB0/INT

;=======================================================

;=======================================================

; INITIALISATION

; *************

init

bcf tempof ; Effacer "flag" tempo

bank1 ; Passer en banque1

movlw 0x01

movwf trisb ; RB0 en entrée

movlw 0xfb

movwf trisa ; RA2 en sortie

movlw optionval ; Charger masque (pull-ups "ON" et RBO/INT sur

    ;niveau bas)

movwf option_reg ; Initialiser le registre "option"

bank0 ; Passer en banque0

movlw intermask ; Masque d'interruption (autorisation des interruptions

               ; GIE et INTE)

movwf intcon ; Charger le masque dans le registre "INTCON"

bcf led ; Éteindre la Led

goto start ; Aller au programme principal

;=======================================================

; PROGRAMME PRINCIPAL

; *******************

start

btfss tempof ; Tester si le "flag" tempo est mis. Si oui sauter la

; prochaine instruction sinon exécuter la suivante

goto start ; Boucler

call tempo ; Exécuter la temporisation

bcf tempof ; Effacer le "flag" tempo

bcf intcon, intf ; Effacer le "flag" de RB0/INT

bsf intcon, inte ; Remettre RB0/INT en service

goto start

;=======================================================

; TEMPORISATION DE 390 ms

; **********************

tempo

movlw 0x02 ; Charge la valeur 2 dans l'accumulateur

movwf compteur2 ; Transfert w dans le registre "compteur2"

load1

movlw 0xff ; Charge la valeur ff dans l'accumulateur

movwf compteur1 ; Transfert w dans le registre "compteur1"

load0

movlw 0xff ; Charge la valeur ff dans l'accumulateur

movwf compteur0 ; Transfert w dans le registre "compteur"

dec

decfsz compteur0, 1 ; Décrémente le registre "compteur0", saute

; l'instruction suivante si le résultat est zéro

goto dec ; Décrémente = 1 cycle, goto = 2 cycles soit au total

   ; 3 cycles d’instruction , t = 3 micro-secondes x

   ; contenu du registre "compteur" = 3us x 255 = 765ms

decfsz compteur1, 1 ; Décrémente le registre "compteur1"

goto load0 ; t = 3 x 255 x 255 = 195ms

decfsz compteur2, 1 ; Décrémente le registre "compteur2"

goto load1 ; t = 3us x 255 x 255 x 2 = 390 millisecondes

return ; Retour à la ligne suivant l'appel de l'étiquette

; "tempo"

end ; Directive fin de programme

;=======================================================

11.8. Utilisation de deux interruptions

Dans l’exemple suivant, on va utiliser deux interruptions différentes. Dans notre programme de télérupteur, on va remplacer la temporisation par une interruption sur le TIMER0.

La mise en œuvre du timer0 est visible dans l’application suivante :

;==========================================================

; Programme pour le pic 16F84. Ce programme allume une lumière placée sur RA2 à l'aide

; d'un bouton poussoir placé sur RB0/INT

; Titre : Télérupteur version 2

; Auteur : Dany Amer

; Remarque : Anti-rebond de 328 ms à l'aide du timer (quartz de 4MHz)

; Fichier requis : p16f84.inc

;========================ENTÊTE=============================

list p=16f84, w=2 ; Définition de processeur, niveau d’erreur

#include <p16f84.inc> ; Définition des constantes

__config _cp_off & _wdt_off & _pwrte_on & _xt_osc

;==========================================================

; ASSIGNATIONS

; ************

optionval equ 0x07 ; Valeur registre option

; Résistance pull-up "ON" (Non RBPU = bit 7 = 0)

; Interruption RB0/INT sur front descendant (INTDG=bit 6 = 0)

; Incrémenté en fonction de l’horloge interne (T0CS = bit5 = 0

; Utilisation de la prédivision du timer (PSA = bit 3 = 0)

; Prédivision du timer à 256 (PS0, PS1, PS2=bits 0,1 et 2 à

; l’état 1)

intermask0 equ 0x90 ; Masque d'interruption avant l'interruption RB0

; Interruption générale autorisée GIE = bit 7 = 1

; Interruption du timer interdite T0IE = bit 5 = 0

; Interruption sur RB0/INT autorisée INTE = bit 4 = 1

intermask1 equ 0x20 ; Masque d'interruption après l'interruption RB0

; Interruption INTE hors service (RB0/INT annulée)

; Interruption TMR0 en service (T0IE=1)

; "Flag" INTF remis à 0

intermask2 equ 0x10 ; Masque d'interruption durant l'interruption Timer0

; Interruption TMR0 interdite

; Interruption RB0/INT en service

; Flag RB0/INT effacé

; Le "flag" GIE sera remis par retfie

passages equ 0x05 ; Nombre de passages dans le registre TMR0

; Temporisation contre les rebonds du bouton poussoir

; 256 s x 256 x 5 =328 ms

;==========================================================

; DÉFINITIONS

; **********

#define led porta,2 ; LED placée sur RA2

;==========================================================

; MACRO

; *****

bank0 macro

bcf status,rp0 ; Passer en banque0

endm

bank1 macro

bsf status,rp0 ; Passer en banque1

endm

input macro

bcf status,rp0

movlw 0x01

movwf trisb ; RB0 en entrée

endm

output macro

movlw 0xfb

movwf trisa ; RA2 en sortie

bsf status,rp0

endm

;==========================================================

; DÉCLARATION DE VARIABLES

; ***********************

cblock 0x0c ; Début de la zone "variables"

w_temp :1 ; Sauvegarde du registre W

status_temp : 1 ; Sauvegarde du registre STATUS

compteur : 1 ; Compteur de passages dans le registre "TMR0"

endc ; Fin de la zone

;==========================================================

; DÉMARRAGE SUR RESET

; *******************

org 0x00 ; Adresse de départ après reset

goto init ; Aller à l'étiquette "init"

;==========================================================

; ROUTINE INTERRUPTION

; ********************

; Sauvegarde des registres

; -----------------------------

org 0x04 ; Adresse de départ de l'interruption

movwf w_temp ; Sauvegarder le registre W

swapf status,w ; Permuter status (low et high) et mettre le résultat dans w

movwf status_temp ; Sauvegarder le registre status permuté

; Test des différentes interruptions

; ----------------------------------------

btfsc intcon,t0ie ; Tester si l'interruption timer est autorisée. Si 1, aller

    ; à l'instruction suivante, sinon la sauter.

btfss intcon,t0if ; Tester si l'interruption timer est en cours. Si oui sauter

   ; la prochaine instruction, si non l'exécuter.

goto intbp         ; Aller à l'étiquette intbp

call inttimer       ; Traiter l'interruption par débordement du timer

bcf intcon,t0if    ; Effacer le "flag" d'interruption du timer

goto recupreg   ; Aller à récupération des registres sauvegardés

intbp

call intrb0         ; Traiter l'interruption par RB0/INT

bcf intcon,intf   ; Effacer le "flag" de l'interruption RB0/INT

; Restauration des registres

; ------------------------------

recupreg

swapf status_temp,w ; Permuter status_temp et mettre le résultat dans w

movwf status             ; Restaurer status

swapf w_temp,f          ; Permuter L et H de w_temp, mettre le résultat dans f sans  

   ; modifier Z

swapf w_temp,w         ; Réinversion de L et H, résultat dans w, w est ainsi restauré

   ;sans  modifier status

retfie                        ; Retour d'interruption

;==========================================================

; INTERRUPTION TIMER 0

; *******************

inttimer

decfsz compteur,f    ; Décrémenter le compteur de passages dans TMR0

return                     ; Pas 0, retour au point d'appel de inttimer

movlw passages      ; Charger w avec le nombre de passages

movwf compteur      ; Recharger le compteur de passages

movlw intermask2  ; Préparation valeur INTCON

movwf intcon          ; Interruption TMR0 interdite

         ; Interruption RB0/INT en service

         ; Flag RB0/INT effacé

         ; Le "flag" GIE sera remis par retfie

return                     ; Fin d'interruption timer

;==========================================================

; INTERRUPTION RB0/INT

; *******************

intrb0

movlw 0x04         ; Préparer pour inversion bit

bank0                ; Passer en banque0

xorwf porta,f       ; Inverser le niveau logique sur la LED

clrf tmr0             ; Remise à zéro du timer0

movlw intermask1 ; Préparation pour le registre INTCON

movwf intcon     ; Interruption INTE hors service (RB0/INT annulée)

    ; Interruption TMR0 en service (T0IE=1)

    ; "Flag" INTF remis à 0

return               ; Fin d'interruption RB0/INT

;==========================================================

; INITIALISATIONS

; **************

init

input ; RB0 en entrée

output ; RA2 en sortie

clrf porta ; Sorties portA à 0

clrf portb ; Sorties portB à 0

bank1 ; Passer en banque1

movlw optionval ; Charger masque

; Résistances pull-ups "ON"

; RB0/INT active sur niveau bas

; Signal d'horloge interne

; TMR0 sélectionné

; Prédiviseur à 256

movwf option_reg ; Initialiser registre option

movlw passages ; Charger le nombre de passages

movwf compteur ; Charger le compteur de passages

bcf led ; Éteindre la LED

bank0 ; Passer en banque 0

movlw intermask0 ; Masque interruption

; Interruption générale autorisée

; Interruption timer interdite

; Interruption RB0/INT autorisée

movwf intcon ; Charger le registre INTCON

goto start ; Aller au programme principal

;==========================================================

; PROGRAMME PRINCIPAL

; ********************

start

goto start ; Boucler

end ; Fin de programme

;==========================================================

Le fonctionnement de ce programme est explicité par l’ordinogramme suivant :