Comment programmer en assembleur - La famille PIC de Microchip

 

Qui n'a pas eu un jour envie de programmer des microcontrolleurs ? Moi-même ne savais pas programmer il n'y pas si longtemps avant que je crée cette page ! Mais il ne m'a fallu que de quelques jours (je deconne pas) pour apprendre le minimum. Ce qui est dans cette page devrait vous aider un maximum pour commencer et vous lancer dans la programmation des PIC, microcontrolleurs les moins chers (à partir de 10FF).

Tout d'abord, la programmation d'un composant nécessite une bonne compréhension du fonctionnement de celui-ci. Je ne devellope que 2 PIC pour le moment, le 12c508 et le 16f84. Le premier est OTP (en géréral : prix = 10FF), et le second EEPROM, donc effaçable electriquement et reprogrammable (prix moyen 30 à 50FF).

Pour ça, je pourrais écrire des pages, mais rien ne vaut mieux que les datasheets fournis par le constructeur lui-même. Bien que reticent au debut, j'ai vite vu que ceux-ci étaient tres complets et utiles, et, une fois les bases acquises, très faciles à comprendre (du moment que vous comprenez quelques mots en anglais technique). Pour les télécharger (2Mo pour le 16f84 et 1.6Mo pour le 12c508), allez sur Microchip, puis choisissez dans le menu de gauche "Litterature", puis PIC Micro Devices, la référence du PIC, puis vous n'avez qu'à télécharger. Pour vous lancer, vous pourrez aussi trouver dans la rubrique "Applications Notes" (retour à l'accueil) pleins d'applications (vous l'aurez deviné....) pour chacun des PICs existant (de la simple idée de base au projet élaboré avec schema, typon et code source inclus).

La structure d'un programme en assembleur est très simple, cependant il est très préférable d'avoir une idée claire de votre projet et de le structurer par un organigramme sur papier (montrant les conditions, les tests à effectuer, les différentes possibilités possibles, etc...). Vous devrez prévoir chacun des cas possibles, et pallier à d'éventuels problèmes.

Pour faire votre programme vous devrez travailler sur MPLAB, téléchargeable librement sur le site de Microchip. Après avoir entré votre programme (listing), allez dans Project > Build Node. Vous selectionnerez alors les options possibles. Surtout choisissez ErrorFile car si votre programme comprend une erreur (syntaxe, valeur, etc...), le compileur vous le dira et n'assemblera pas votre projet.

Vous remarquerez que l'on travaille sur 3 colonnes :

Rien ne détermine la fin d'une ligne (pas de ";" comme en pascal). Mais pour mettre des commentaires (assez utile !), utilisez un point virgule puis votre commentaire.

Le décut d'un programme pourra commencer par #include "p16f84.inc" (en colonne 2) pour un PIC16F84 (ou "p12c508.inc", etc...): ceci vous evite de declarer les adresses des registres que vous utiliserez.

Ensuite vous mettrez en 2nde colonne ORG et en 3ème 0. On appelle ceci le ResetVector : c'est là que le programme demarera. Vous pourrez mettre en instruction GOTO routine. Si vous utilisez les interruptions, vous utiliserez en plus ORG 4 (avec un goto aussi si ça vous arrange) : c'est là que le programme sautera à chaque interruption.

1- Les Variables

Toutes les variables sont des variables 8 bits (pour les PIC 8 bits, en tout cas), allant de 0 à 255 (00h à FFh, xxh étant la représentation hexadécimale). En effet vous devrez utiliser de l'hexadecimal. Mais MPLAB permet d'utilies le binaire et le décimal.

Il y a 2 manières générales de déclarer des variables :

CBLOCK    adresse
                    liste des variables séparées par des virgules
ENDC

Donnez une adresse non utilisée par le µC : par exemple 0x0C, etc.... De toute façon elle ne reviendra pas dans le programme que vous écrirez.

Comment définir une valeur ?

MPLAB se chargera alors de convertir tout ceci en hexadecimal. Vous pouvez télécharger le fichier ASM suivant montrant des déclarations de variables classiques.

Vous pourrez alors effectuer diverses opérations sur vos variables (voir plus bas)

2 - Les Registres

Je ne vais pas vous detailler tous les registres qui existent, car ils sont différents pour chaque PIC. Vous devrez alors etudier le datasheet fourni par microchip, qui est très complet et précis. Mais voyons dejà les registres classiques :

Il existe deux "banks" dans lesquels sont contenus les registres. On passe de l'un à l'autre par :

bsf    STATUS,RP0 => passage bank 1
bcf    STATUS,RP0 => retour bank 0
Vous retrouverez la place de chaque registre dans le datasheet.

etc....

Chaque registre est constitué de bits (8 en général), que vous mettrez à 1 ou 0 séparement ou en même temps.

3 - Les Ports E/S

Le PIC16F84 a deux ports : le port RA0-4 et le port RB0-7 (soit un total de 13 E/S configurables séparement).

Pour les configurer, il faut utiliser les registres TRISA et TRISB, un bit à 1 mets une ligne en entrée ; un bit à 0 le met en sortie.

Vous pourrez lire ou commander un port d'un seul coup ou ligne par ligne.

Voir plus bas pour des exemples

4 - Les Opérateurs

Il existe pour le PIC16F84 pres de 35 instructions : ces instructions traitent des variables, des données, des bits, etc...
La liste complete est disponible à la page 56 (sur 124) du datasheet du PIC 16F84.

Vous verrez parfois le terme "dir". En fait vous devrez mettre soit W ou f : si vous mettez W, le resultat de l'opération sera mis dans W. Pour f, il sera dans la variable elle-même (par ex : decf  variable,W : le resultat de la decrementation de variable sera dans W, et la variable ne changera pas ; si on a decf   variable,f, c'est variable qui est reellement decrementée)

DES EXEMPLES !

+ Je veux mettre une certaine valeur dans une variable :
movlw    valeur
movwf    variable

+ Je veux configurer des E/S :

bsf    STATUS,RP0 (on doit aller dans le bank 1, car utilisation de TRIS)
movlw    B'00101100'
movwf    TRISB (ou TRIS     PORTB)
bcf    STATUS,RP0 (on repart dans le bank 0)

Ainsi le port RB sera configuré : lignes 2, 3, 5 en entrées et les autres en sortie)

Ou séparement :

bsf    STATUS,RP0
bsf    TRISB,5
bcf    STATUS,RP0

Pouir mettre en entrée (bsf = mise à 1) la ligne RB5

+ Je veux lire le port A

movf    PORTA,W (on copie la valeur du port A dans le registre W)
movwf    variable (ce registre W est alors deplacé vers la variable)

Vous obtiendrez alors une variable de forme binaire B'xxxxxxxx', avec le premier bit à droite. Les lignes RA5-7 (non utilisées) seront lues comme 0.

+ Je veux lire seulement une partie du port

=> 2 solutions !

Soit avec une routine de test :

btfsc    PORTB,1 (si port B, ligne 1 et à 0, on saute l'instruction suivante)
instruction 1  (si on a lu 1, alors cette instruction est activée)
instruction 2  (si on a lu 0, on est passé directement à l'instruction 2)

Ou avec des  operation logiques sur le resultat :

Par exemple si je veux connaitre la valeur des 4 premiers bits du port A (pour lire une valeur codée sur 4 bits par ex.) sans se soucier des autres bits :

movf    PORTA,W
andlw    B'00001111'
movwf    variable

Ini on lit le port A entierement, puis on fait un ET logique avec la valeur lue (contenue temporairement dans W). Ainsi, tout bit conservera sa valeur normalle avec le ET avec un 1, et reviendra à 0 si on fait ET 0 : ainsi les 4 premiers bits garderont leur valeur normalle, les autre seront mis à 0. Le resultat dans W sera recopié dans la variable.

+ Je veux lire une valeur dans l'EEPROM (64 octets disponibles librement) :

movf    adresse,w (ici on mets l'adresse contenue dans la variable 'adresse' dans le registre W)
mowf    EEADR (on la met dans EEADR)
bsf    STATUS,RP0
bsf    EECON1,RD (on lit)
bcf    STATUS,RP0
movf    EEDATA,W (on deplace la valeur lue EEDATA à l'adresse indiquée, dans W...)
movwf    variable (...et on la deplace dans la variable)

Mais au fait, comment on fait pour écrire une valeur ?...

+ Je veux écrire une valeur dans l'EEPROM

movf    adresse,w (l'adresse contenue dans une variable à vous nommée 'adresse' ou autre est deplacée dans W)
movwf    EEADR
movf    valeur,W (on mets la valeur àecrire dans W puis dans EEDATA)
movwf    EEDATA
bsf    STATUS,RP0
clrf    EECON1
bsf    EECON1,WREN
movlw    55h   (procédure de 4 lignes obligatoire)
movwf    EECON2
movlw    AAh
movwf    EECON2
bsf    EECON1,WR
btfsc    EECON1,WR   (on vérifie par ces dernieres lignes que l'ecriture a bien eu lieu)
goto    $-1
bsf    EECON1,WREN
bcf    EECON1,EEIF
bcf    STATUS,RP0

Et voila !

+ Je veux passer d'une routine à une autre

C'est simple, soit vous faites un GOTO et le programme saute sur la routine ou vous voulez (en quittant l'autre)

Sou vous faites un CALL sous-routine et votre programme execute la sous-routine appelée, puis lorsque celle-ci est terminée, revient là où il était (la sous-routine devra finir par un RETURN)

5 - Les Interruptions

6 - Le Timer interne

C'est pas encore, désolé. Mais moi aussi il faut que j'apprenne !

7 - Resources, liens

N'hesitez pas à télécharger des programmes un peu partout et de vous en inspirer !