http://www.didel.com/ [email protected] http://www.bricobot.ch/ [email protected] www.didel.com/pic/Bases.pdf Bases : nombres, constantes, variables, programme La lecture de ce document devrait logiquement se faire aveant de commencer à programmer. Dans les faits, avoir exécuté au préalables des programmes simples facilite la compréhension de ce document. 1. Signification d’un mot binaire Le processeur travaille sur des mots binaires sans savoir ce qu’ils représentent. Si le programmeur écrit Move #2’00111001,W il peut avoir des intentions très différentes : Il veut afficher le chiffre 4 sur un affichage 7 segments et la prochaine instruction sera un transfert sur le registre de sortie qui commande l’affichage. Les bits sont numérotés de 0 à 7. A noter qu’un bon programmeur aurait déclaré Quatre = 2’00111001 et écrit Move #Quatre,W Il veut préparer la valeur 83 dans un décompteur pour attendre par exemple 83 fois une seconde. Il aurait du écrire Move #83,W l’assembleur aurait traduit en binaire. Avec les poids binaire, on peut faire de tête la conversion dans les deux sens : de 83, on ne peut pas extraire 128, on extrait 64, il reste 19, on ne peut pas extraire 32, on extrait 16, etc. A droite on a les bits de poids faible, et à gauche les bits de poids forts. Il veut calculer en décimal codé binaire (BCD) avec deux groupes de 4 bits pour les chiffres de 0 à 9. Il devrait donc écrire de préférence Move #16’53,W car le BCD se code comme l’hexadécimal, mais en n’utilisant que les chiffres de 0 à 9. Il veut préparer la lettre S pour l’envoyer via une ligne série et il utilise le code ASCII. Ce code représente les lettres, les chiffres et les signes et il est connu de l’assembleur. Il faudrait donc écrire Move # "S",W Note 1: Les notations utilisées ici sont celles de CALM, logiques et intuitives. D’autres assembleurs utilisent des notations du type 03FH ou H’3F’. Note 2: Wikipedia vous donne plus d’information sur BCD, ASCII, etc. Il faut souvent ajouter un mot qui focalise la recherche : porte ET, registre PIPO. 2 Bits et octets Tous les systèmes informatique travaillent en binaire, avec seulement des zéros et des uns, plus facile à coder de façon fiable par des états électriques, magnétiques ou lumineux. Un bit est l'élément d'information qui n'a que deux valeurs: 0 et 1. Plusieurs bits sont utilisés pour coder une information quelconque, voire reconnaître un objet, puisqu'il suffit de coder le numéro de l'objet. Un octet (byte en anglais et en franglais des informaticiens) est un mot de 8 bits, qui peut donc représenter 256 combinaisons, le plus souvent les nombres de 0 à 255 en décimal. Les signaux binaires peuvent être transmis à des distances quelconques, mémorisés, transformés par combinaison de signaux. Les opérations principales sont l'inversion (NOT), le OU, le ET, le OU Exclusif, l'addition. Appliqués à des mots binaires (juxtaposition de bits), ces opérations dites logiques permettent de superposer, masquer, inverser des bits dans des mots binaires. Le ET sera très utilisé pour masquer des bits qui ne sont pas significatifs, et doivent être mis à zéro. Le OU permet de fusionner deux mots, ou de forcer un certain nombre de bits à un. Le OU Exclusif XOR permet d'inverser certains bits, alors qui le NOT inverse tous les bits. L'addition est un peu trop compliquée à expliquer pour le moment. 3 Les Fanions Les fanions Z N C sont mis à jour à la fin de l’opération. Z (zero) est activé à 1 si le résultat de l’opération est nul. Par exemple, Move #0,B va activer Z N (negatif) est activé si le bit 7, dit «bit de signe» est à 1. Ceci concerne les nombres négatifs qui seront vus plus loin, mais cela peut être pratique de savoir que ce bit est à un (c’est le cas dans l’exemple d’addition ci-contre) N n’existe pas sur les PIC 10F 12F 16F C (carry/borrow) est activé si l’addition déborde, ou que la soustraction s’est terminée par un emprunt. C est inversé sur les PIC, qui ajoutent le complément Les instructions Move n’ont pas de raison de modifier le Carry. Mais pour les PICs, le fanions Z est mis à jour pour l’instruction Move Reg,W. 4 Opérations logiques L’opération ET (AND) donne un bit à 1 dans la destination si les deux bits correspondant sont à 1. On l’a utilisée en page 28 et 38 pour mettre à zéro des bits non utiles (on dit masquer ces bits). L’opération OU (OR) donne un bit à 1 dans la destination si l’un des deux bits correspondant est à 1. Par exemple pour savoir si A et B sont tous deux à zéro, on peut écrire Or A,B ; superpose A et B Jump,EQ TousLesDeuxAZero L’opération OU exclusif (XOR) donne un bit à 1 dans la destination si l’un des deux bits correspondant est à 1, mais pas les deux. Cette instruction est très utile sur un écran, pour faire passer un pointeur de souris par dessus l’information, sans la perdre. Opérations logiques et symboles 5 Opérations à un opérande L’instruction Clr dest mets tous les bits à zéro. Clr W existe. L’instruction Not dest inverse tous les bits. Not W n’existe pas, on écrit Xor #-1,W L’instruction Inc dest ajoute 1. Si le résultat est nul (le compteur a fait un tour), le fanion Z est mis à 1. L’instruction Dec dest soustrait 1. Si la valeur initiale est 0, après décrémentation, le résultat est H’FF (des 1 partout). Attention, Dec dest,W copie dans W et la valeur augmentée n’est que dans W. De même pour Not dest,W et Inc dest,W, qui sont des instructions souvent très utiles. Inc W et Dec W doivent être remplacés par Add #1,W et Add #-1,W Comment faire pour qu’un compteur se bloque en arrivant au maximum H’FF ? On teste si le compteur est arrivé à zéro et on décompte pour annuler le comptage : Inc A Jump,NE Next Dec A Next : suite ; saute à Next si fanion Z=0 Comment faire pour qu’un décompteur se bloque à zéro ? De même pour qu’un compteur se bloque à la valeur H’7F ou à la valeur B’31 (par exemple) ? C’est ce qu’on appelle saturation. (détails sous www.didel.com/pic/Sature.pdf ) 6 Décalages L’instruction RL dest « Rotate Left » fait tourner le mot de 8 bits sélectionné sur lui-même. Le bit qui fait le tour est copié dans le Carry, RR dest « Rotate Right » fait la même chose dans l’autre sens. Ces deux instructione n’existent pas dans les PICs Les instructions RLC « Rotate Left through Carry » traversent le Carry il faut 9 décalages pour retrouver la même valeur. RRC « Rotate Right through Carry » fait la même chose dans l’autre sens. Ces instructions permettent par exemple de compter le nombre de bits à un dans le registre A. On initialise un compteur par 8 et on décale dans la boucle. Chaque fois que le carry est à un, on incrémente un compteur. Les instructions RLC dest,W et RRC dest,W existent et ne modifient pas dest, seulement W. 7 Codage Avec n bits, on peut obtenir 2^n combinaisons différentes, qui peuvent être associées de façon naturelle ou arbitraire à des nombres ou objets. La liste de ces combinaisons est en général ordonnée comme l'on compte d'habitude. En décimal avec 3 chiffres et en mettant les zéros non significatifs, on compte 000 001 002 .. 009 .. 010 011 012 .. 098 099 100 101 .. 999 Chaque nombre peut être associé à un objet, par exemple une maison dans une rue. On sait bien distinguer d'après leur position les unités, dizaines, centaines, dont le poids est 1, 10 ,100 (puissance des 10). En binaire, il n'y a que les chiffres 0 et 1, et avec trois bits il n'y a que 2^3= 8 combinaisons: 000 001 010 011 100 101 110 111 que l'on associe aux valeurs 0..7 ou que l'on considère comme des codes pouvant représenter 8 objets différents. Le poids des bits (exprimé en décimal) selon leur rang est 1, 2, 4 (puissances de 2). Si l'on veut compter plus loin, coder plus d'objets, il suffit de prendre plus de bits, en moyenne 3,3 fois plus qu'en décimal, c'est simple. Avec 10 bits, on compte jusqu'à 1023, cela fait 1024 combinaisons, le kilo des informaticiens. Avec 20 bits, c'est un Mega. Avec les futurs microprocesseurs 64 bits, un mot mémoire peut représenter beaucoup de choses. Avec les processeurs 8 aussi: il suffit de prendre plusieurs mots consécutifs, selon les besoins. Si l'on veut compter les secondes dans un mois, environ 2,6 millions, soit un nombre décimal de 7 chiffres, cela donne un nombre de 23 bits. On codera cette durée dans 3 mots de 8 bits, appelés registres ou variables 8 bits 8 Nombres Les processeurs travaillent en binaire, par mots de 8 bits pour le PIC. On peut toujours prendre plusieurs mots de 8 bits pour avoir plus de précision ou plus d'information. Le programmeur aime bien l'hexadécimal, qui permet d'écrire 2 chiffres plutôt qu'une suite de huit 0 et 1. Le décimal est utilisé pour les paramètres de l'application, car nous y sommes habitués, et les compteurs, voltmètres, horloges, donnent toujours des valeurs en décimal. La traduction de décimal en binaire se fait par l'assembleur ou par un programme. Pour les nombres 8 bits, le tableau suivant donne les correspondances. Pour éviter toute confusion, les nombres binaires sont précédés de 2'. les nombres hexa de 16'. Les nombres décimaux peuvent être précédés d'un 10', mais cela n'est pas nécessaire puisque le décimal est la base dite "par défaut". Le tableau suivant compare quelques valeurs 8 bits dans les 3 systèmes de numération. Il est important d'être à l'aise avec ces notations. La conversion du binaire en décimal se fait en tenant compte des poids: les chiffres binaires, en commençant par la droite, ont les poids 1,2,4,8,16,32,64,128. Le bit de poids fort est parfois considéré comme le bit de signe, avec une représentation des nombres dite en complément à 2 que nous verrons plus loin. Ceci ne s'utilise que rarement avec le PIC, qui n'est pas prévu pour faire des calculs. Il faut toutefois se souvenir que -1 se code 16'FF = 2'11111111 (on dit que c'est le complément à 2 de 1). Le compteur d'une voiture neuve qui commencerait par faire un kilomètre en marche arrière afficherait 99999 kilomètres! C'est la représentation naturelle des nombres négatifs, mais il faut lever l'ambiguité entre positifs et négatifs en mettant une frontière entre deux, en général à 128 (le bit de poids fort est alors appelé bit de signe. Par exemple, si on veut coder la vitesse d'un moteur, une solution est de mettre dans une variable une vitesse positive (entre 0 et 255 ou moins) et dans une autre variable la direction. L'autre solution n'utilise qu'une variable contenant la vitesse signée. 00000001 est la vitesse minimale positive, et 11111111 (-1) la vitesse minimale négative. 1000000 est l’arrêt. Exercice: Ecrire en binaire 16'C4. Ecrire en hexadécimal -6. Quelle est la valeur en décimal de 16'64? Quelle est la valeur en binaire de 20? 9 Nombres négatifs Si vous prenez une voiture neuve et que vous faites 1 km en marche arrière, le compteur va indiquer 99999 km. De même, si un registre contenant zéro est décrémenté (ou on soustrait 1), il va afficher FF. C’est la représentation naturelle de nombres négatifs, dite en complément à 2. On peut aussi travailler avec une représentation signée, en mettant la valeur absolue dans un registre et le signe dans un autre registre. Comment savoir si H’FF représente le nombre –1 ou le nombre 255 ? Le processeur ne peut pas vous aider. Pour lui, c’est 8 bits à un, rien de plus. Le programmeur décide et doit gérer les dépassements de capacité différemment selon qu’il a mis dans les registres des nombres positifs ou signés (positifs ou négatifs). La meilleure représentation est de placer les nombres 8 bits sur un cercle. Avec les nombres positifs, si on passe la frontière ente FF et 00, il y a débordement que le Carry va signaler. Avec les nombres en complément à 2, la frontière est entre 7F et 80, quand le bit 7, dit bit de signe, change. Si on veut comparer deux nombres, il faut savoir s’ils sont positifs ou signés. Les processeurs de plus de 8 bits ont des instructions qui facilitent la gestion des nombres négatifs, voire des nombres en virgule flottante. Avec notre processeur simplifié, il faut tester le bit de signe N et le Carry pour décider si le résultat d’une addition ou soustraction est valide. 10 Notion de programme Imaginons un petit robot qui sait avancer et tourner. Pour lui donner différents comportements, on peut imaginer une machine qui décode et exécute (on dit interprète) 4 comportements, que l'on va coder en binaire. 00 Attend une certaine durée 01 Tourne de 90 degrés 10 Avance 11 Stoppe Pour dessiner un carré, il faut exécuter en séquence 10 00 01 10 00 01 10 00 01 10 00 01 11. C'est un programme en langage machine. Pour éviter de se perdre dans des bits, on utilisera des mots clés et c'est un programme (assembleur, compilateur) qui fera la traduction en langage machine. Il est plus lisible d'écrire Avance Attend Tourne Avance Attend Tourne Avance Attend Tourne Avance Attend Tourne Stoppe Remarquons que certaines instructions mettent notre robot dans un mode (Avance, Stoppe). D'autres instructions durent le temps d'une action (Attend, Tourne). Dans les deux cas, il serait bon de pouvoir modifier la vitesse ou la durée d'attente (ce qui changerait la dimension du carré); une instruction doit donc pouvoir avoir un paramètre, qui est soit fixe (le paramètre est une constante qui fait que le carré dessiné aura toujours un dimension qui dépends du programme), soit variable (le paramètre est une variable qui permettra par exemple de dessiner des carrés emboités en incrémentant (ajouter 1) la dimension variable); il faudra donc rajouter cette instruction qui incrémente, ou plutôt une instruction qui additionne. Exercice: Comment programmer un 8 (un peu carré bien sûr) ? 11 Fonctionnement du processeur Un microcontrôleur est formé d'une mémoire contenant les instructions du programme, d'un séquenceur et d'une mémoire (formée de registres) pour les variables. Dans le cas du PIC, les instructions sont codées dans une mémoire programme en lecture seulement de 14 bits (la mémoire dite flash). Un compteur d'adresse (PC Program Counter) sélectionne l'instruction à exécuter, et passe automatiquement à l'instruction suivante, au rythme de l'oscillateur (une instruction toutes les microsecondes avec un oscillateur à 4 MHz). Au "reset", le compteur est remis à zéro et le processeur commence à la première instruction. Il passe à la suivante sauf si c'est une instruction de saut (Jump, Goto que nous appellerons ici AllerA), qui permet de continuer le programme à une adresse quelconque, ou recommencer une séquence d'instructions. Schéma simplifié du processeur PIC Le processeur commande un registre de travail W (Work register) et une mémoire en lecture et écriture pour les registres associés au processeur et au programme de l'utilisateur (variables). Deux registres, appelés ports communiquent avec l'extérieur. Sur le PIC 16F84, le Port A a cinq bits et le port B huit, qui peuvent être des entrées ou des sorties. Les opérations que l'on peut faire sont les opération simples vues dans la figure 1. L'unité de calcul s'appelle ALU (Arithmetic Logic Unit) et les instructions combinent W et un registre, avec le résultat dans W ou dans le même registre. Le décodeur d'instruction commande l'aiguillage et l'opération à effectuer. Ces opérations élémentaires du processeur ne seront décrites que dans PICG-C; pour permettre de faire plus vite des choses intéressantes sans se perdre dans toutes les subtilités d'un processeur, les premiers programmes utilisent des groupes d'instructions (appelés macro-instructions) désignées par un nom explicite. 12 Constantes et variables Les instructions permettent de copier une valeur constante faisant partie d'une instruction dans le registre W, de faire une opération sur W (ET, OU, Addition) et d'agir sur un registre, en combinaison avec W. W et les registres sont comme des tiroirs, dans lesquels on peut mettre des valeurs. On assignera des noms aux tiroirs (nom des variables) et quand on agira sur une variable, c'est de son contenu dont il s'agira. Seuls les registres à partir de l'adresse 16'C sont accessibles à l'utilisateur, mais PICGAB cache ce genre de détails qui dépendent du processeur. Les numéros de ces registres sont toujours remplacés par des noms, définis par le fabricant jusqu'à l'adresse 16'B, et par l'utilisateur après. En résumé, les registres sont comme les tiroirs d'une armoire, le nom de variable associé, c'est l'étiquette collée sur le tiroir, et le contenu, c'est un mot de 8 bits. Le processeur travaille avec les numéros de tiroir, mais l'utilisateur se dépèche de coller une étiquette qui correspond à ce que l'on va ranger dans chaque tiroir. Un tiroir n'est jamais vide. Il contient un mot binaire qui représente un nombre ou un mot dont certains bits décideront si on allume certaines lampes ou si on fait tourner un moteur dans un sens ou l'autre. En "ouvrant le tiroir", le processeur peut lire la valeur, ce qui ne la modifie pas. Il peut copier le contenu de W ou le résultat d'une opération dans un tiroir voulu. Par exemple, si le bit 0 de la variable PortA (qui est un registre dont les sorties sont connectées avec l'extérieur) allume une lampe quand ce bit est à un, il faut écrire Move #2'00000001,W Move W,PortA Evidemment, s'il y a d'autres lampes sur le même PortA, elle vont toutes être éteintes. Avec les instructions logiques, on évite ce problème: on lit le PortA, on modifie le bit 0 de notre lampe, et on réécrit la nouvelle valeur dans le PortA Move PortA,W Or #2'00000001,W ; force le bit de poids faible, ne modifie pas les autres Move W,PortA Le processeur a une meilleure façon de faire, vue plus loin. Un saut "conditionnel" est essentiel pour que le programme dépende d'une condition interne ou externe (Si Egal AllerA). Une caractéristique du PIC est d'avoir une instruction de "skip conditionnel" qui selon la condition testée, passe par dessus l'instruction suivante. PICG-B cache cette instruction très efficace et utilise le saut conditionnel si ... allera, plus facile à comprendre. Question: comment éteidre cette lampe? Indication: utiliser un ET logique avec le bon masque. 13 Constantes et variables Les constantes sont des nombres, que le processeur va copier, utiliser pour un masque ou pour une comparaison. Elles sont connues immédiatement lors de la traduction du programme (d’où leur nom ‘’valeur immédiate’’), et sont codées dans l'instruction. En CALM, elles sont précédées du signe # (prononcé valeur). Par exemple #2'1001000 ou #VitesseInitiale, avec la valeur du symbole définie ailleurs (par un VitesseInitiale = 3 par exemple). Les variables par contre sont des cases mémoire vides, dans lesquelles le processeur peut lire et mettre des valeurs. Elles ont toujours un nom, bien que ce nom corresponde à un nombre attribué par l'assembleur, et que l'on pourrait utiliser ce nombre si on le connaissait. D'où la nécessité du signe # pour distinguer les valeurs immédiates des variables. Le Basic et le C distinguent les constantes et variables par le contexte ou par des déclarations initiales. 13 Symboles L'assembleur CALM impose la contrainte que les noms sont une suite de caractères sans espace. Ils peuvent inclure des chiffres et les deux signes _ et ? (souligné et point d'interrogation). Les minuscules et majuscules sont identifiées. Les lettres accentuées ne sont pas acceptées. Dans nos habitudes, les majuscules sont utilisées pour la première lettre des mots significatifs, afin d'augmenter la lisibilité. 14 Numérotation des bits Les bits sont numérotés de la droite vers la gauche. Le bit de droite est le bit 0, le bit de poids faible, avec un poids 1 (2^0| noté 2**0 pour l'assembleur). Le bit de gauche (dans un mot de 8 bits) est le bit 7, le bit de poids fort, avec le poids 128 (=2**7). La double étoile est utilisée pour l'exposant. Les signes +, -, *, / ont la même signification qu'en Basic (addition, soustraction, multiplication, division). Il faut bien comprendre que le programme que l'on écrit est analysé par un programme (l’assembleur) qui sait mieux calculer que le processeur. Si on écrit Move #342/5,W ce programme va effectuer le calcul, trouver 68 (le résultat est 68,4, mais le ,4 est ignoré car le processeur ne travaille qu'avec des nombres entiers), convertir en binaire et préparer pour le processeur, qui ne comprends que le binaire, l'instruction Move #2'01000100,W. (les bits 6 et 2 sont à 1, donc la valeur est 2^6+2^2 = 64 + 4 = 68. 15 Action sur des bits Le PIC est très efficace pour agir sur un bit d'une variable ou registre. Cela s'utilise en particulier pour tester ou modifier une ligne d'entrée/sortie. Pour désigner un bit, il faut dire dans quelle variable se trouve ce bit et quel est le numéro du bit (valeur constante immédiate). Pour adresser la ligne RA3, liée au bit 3 du PortA, il faut écrire PortA:#3. On peut mettre cette ligne à 1 avec l'instruction Set PortA:#3. Les exemples plus loin permettront de se familiariser avec cette notation efficace. En PIC-Basic, on écrit High 0 pour activer RB0, mais cela génère 40 instructions et prend 25 microsecondes! L'instruction PIC Set PortB:#0 ne prend qu'une seule microseconde (avec un oscillateur à 4 Mhz). 16 Instructions, procédures et macros Le processeur exécute des instructions. Le programme, ensemble de toutes les instructions et déclarations, est en général décomposé en procédures correspondant aux tâches, fonctions, groupes d'opérations à exécuter. Une procédure est appelée routine ou macro-instruction en assembleur, la distinction sera vue plus tard. Le comportement d'une procédure ou macro est enrichi par des paramètres. Par exemple, la procédure qui commande un moteur a un paramètre "vitesse" et éventuellement un 2e paramètre "nombre de tours à effectuer à cette vitesse". Les exigences de l'assembleur CALM sont d'écrire le nom de la procédure sur une seule ligne (les espaces ou tabulateurs devant sont ignorés), et d'utiliser un ou plusieurs espaces et/ou tabulateurs avant de donner la liste des paramètres, séparés par une virgule (l'assembleur accepte des espaces et/ou tabulateurs devant et/ou derrière cette virgule, mais en général on n'en met pas). 17 Etiquettes Dans un programme, il faut pouvoir aller à une instruction donnée, pour passer par dessus des instructions que l'on ne veut pas exécuter à cause d'une condition, ou pour répéter un groupe d'instructions. L'instruction AllerA (la plupart des langages disent Goto ou Jump) permet de faire ces sauts. L'instruction destination (l'endroit où on veut aller) est précédée par une étiquette, c'est à dire un nom vu comme un symbole par l'assembleur, suivi du signe : (deux points). On le verra dans les programmes. 18 Paramètres par défaut Ce concept est important. Une procédure est le plus souvent utilisée avec un paramètre évident (le moteur tourne à sa vitesse maximum), et c'est embêtant de devoir chaque fois taper ce paramètre. La procédure peut être définie pour que, par défaut d'indication explicite d'un paramètre, le paramètre "évident" (paramètre par défaut) soit utilisé. S'il y a plusieurs paramètres dans une procédure, deux virgules consécutives indiquent qu'il y a entre les deux virgules un paramètre par défaut. Dans les 3 cas suivants, les 1er, 2e, 3e paramètres sont "par défaut": Moteur ,Vmax,Distance Moteur Vmin,,Distance Moteur Vmin,Vmax, (la dernière virgule peut être omise) Si on écrit simplement Moteur sans paramètre explicite, les trois paramètres seront pris par défaut. L'ordre des paramètres doit être respecté car l'assembleur leur assigne des numéros d'ordre. La gamme de valeurs peut parfois être vérifiée par l'assembleur (si on donne une valeur trop grande pour une valeur 8 bits), et il y aura un message d'erreur plus ou moins facile à interpréter. Ces 3 dernières sections ont probablement été indigestes. Il est temps de passer à la pratique ! jdn090819/100925