Atelier du 8 avril 2008 |
La programmation c'est si simple : atelier 8 |
Dans la routine DoDelay, le même principe s'applique, mais nous utilisons une variable différente. Que serait-il arrivé si nous avions utilisé la même variable TEMPA? Le contenu de celle-ci aurait été modifié par DoDelay et aurait été mise à zéro en retournant à la sous-routine Delay, cette routine Delay ne se serait exécuté qu'une fois, peut importe le nombre demandé par COUNTER. |
Je parlais plus tôt de GOSUB et RETURN, voici comment le CPU réagis à ces commandes. Parmi les registres internes du CPU, on retrouve le Program Counter. C'est lui qui pointe la prochaine instruction à exécuter. Après un reset son contenu est chargé avec le contenu de la case $0000, dans la section de la mémoire flash (endroit ou notre programme sera emmagasiné), c'est ce que l'on appelle un "reset vector". L'interpréteur ira placer dans cette case l'adresse correspondant au début de notre programme (MAIN). Donc le "programme counter" (PC) est chargé avec l'adresse de notre programme, le CPU lira l'instruction emmagasiné à cet endroit et incrémentera le PC, pour qu'il pointe à la prochaine instruction. Après chaque instruction de notre programme, le PC sera incrémenté. Si le CPU rencontre une instruction GOTO, il chargera le PC avec la valeur de l'adresse pointé par le GOTO (il oubliera donc ou il était rendu, mais nous avons décidé de faire un saut inconditionnel). Le CPU commenceras donc à faire les instructions en commençant par celle pointé par le GOTO, il incrémente le PC et continue sa tâche. |
Si par contre il rencontre une instruction GOSUB, il comprend qu'il devra revenir à l'instruction suivante (pointé par le PC), il devra donc conservé la valeur contenue dans le PC. Les millions de transistors internes du CPU effectueront la tâche suivante; ils prendront le contenu du PC et le sauvegarderont dans une région de la mémoire vive appelé "STACK" ou pile si vous préférez. Cette mémoire spéciale est du type LIFO (Last In First Out), elle redonne donc toujours la dernière information qu'elle à reçue. Après que le PC est sauvegardé, le PC prendra la valeur de l'adresse de notre routine et l'opération se continue. Quand durant l'exécution de la routine, le CPU reçoit la commande RETURN, il sait qu'il doit retourner au programme appelant. Il chargera donc le PC avec le dessus de la pile, qui contient l'adresse de sa prochaine instruction. La grosseur de la mémoire "STACK", déterminera le nombre de sous-routines imbriquées que nous pourrons utiliser. Dans le cas du PIC™ on ne dispose que de 8 cases mémoires. Dans le programme que nous avons réalisé, nous n'excédons pas cette limite interne du CPU. Regardons en détails en supposant les adresses de la page suivante. (tableau 18) |
Éventuellement la routine DoDelay sera terminée, et nous rencontrerons la commande RETURN |
Nous voyons donc que notre programme, ne peut jamais dépasser les possibilités du stack. Si jamais nous dépassons, les possibilités du stack, la dernière valeur "poussée" dans le stack sera perdu et la suite de notre programme ne pourras reprendre normalement. C'est probablement ce qui se passe, dans certains ordinateurs se servant de Windows© , (ALT-CTRL-DELETE) |
Tableau 18 |
En plus du "reset vector", il existe un "interrpt vector", localisé à la case $0004. Ce vecteur pointera vers l'adresse du programme que nous voulons exécuter lors d'une interruption. Mais qu'est-ce qu'une interruption? Vous êtes en train d'effectuer une tâche, comme regarder le football et vous entendez "Chéri vient souper". Vous devez arrêter ce que vous faites et aller vous occuper à autre chose. Aussitôt votre repas avalé, vous vous empressez de retourner à vos autres tâches. Malheureusement le match de football est terminé, hélas. |
C'est un peut la même chose
pour un CPU, il est en train d'accomplir des calculs, faire un affichage et soudain, il reçoit une commande de tout laisser en place et d'exécuter une autre tâche plus importante. Le mécanisme d'interruption peut être déclenché par une multitude de raisons et ce sera à nous de voir lesquelles de ces conditions nous intéressent. Le CPU réagira à une interruption de la même façon qu'avec l'instruction GOSUB. Le contenu du PC sera conservé dans le STACK et l'adresse contenue dans le vecteur d'interruption sera chargé dans le PC. Comme nous n'avons pas choisi le moment pour cette interruption, il faudra conserver avec du code machine (asm) le contenu de l'accumulateur et le registre STATUS. A la fin de notre routine d'interruption il faudra rétablir ces registres, avant d'émettre la commande de retour (retfie). Cette commande fait le travail équivalent à la commande RETURN. La valeur du PC est chargé par le contenu du stack et le programme suit son cours, comme si ne s'était rien passé. |
Les sources d'interruptions sont variées, timer 0, timer 1, timer 2, comparateur, interface série, pin extérieure, changement au portb etc…Chacune de ces sources peuvent être masquées, grâce à deux registres INTCON et PIE1, dans le cas d'un PIC™ 16F628. Nous pourrons nous servir de ces outils d'interruption dans nos programmes, comme par exemple ont aurait pu avertir notre programme de traduction, par interruption. (il existe un interrupt qui est généré si on change le portb) Nos routines de délais aurait pu être effectués à l'aide des timer internes du CPU et générer une interruption quand leur temps est terminé. Chacune de ces sources d'interruptions, qui peuvent être masquées, génère aussi des "flags" pour nous indiquer que l'interruption pourrait se produire. Je m'explique; supposons que nous sommes intéressés par la fonction timer 0, (overflow) Si le timer 0 passe de $FF à $00, le bit T0IF (flag) sera placé à 1. Si le bit T0IE (enable) est à 0, l'interruption ne se produira pas , mais rien nous empêche de regarder le bit T0IF et décider si le timer 0 à effectivement basculé vers $00. Il nous est possible de vérifier le contenu du timer 0, pour voir ou il est rendu dans son compte et de prendre une décision. Ces petites puces sont des merveilles d'intégration et de dire, ce n'est pas possible de faire ça, avec un PIC™, nous en dit beaucoup sur le programmeur |
9-Charger le code dans le CPU |
Maintenant que nous avons écrit ces quelques 160 lignes de code il serait bon de voir, si nous sommes parvenu à nos fins. Vous avez reçu un fichier prog1.bas, je vous recommande de le charger dans MicroCodeStudio et de "bruler" un PIC™ 16F628. La démonstration sera faite en classe de toute façon et le résultat final devrait apparaître sous vos yeux! Si vous ne détenez pas de 16F628, nous pourrons modifier le code pour accommoder votre PIC. |
10-Savourer la victoire! |
Voilà, votre premier
programme roule, félicitations!!!! Bien que ce programme est fonctionnel, il existe une multitude de façons, d'écrire le code machine qui produira le même effet. Nous avons créé notre ordinogramme en établissant toutes les conditions matérielles requises et nous avons adapté cet ordinogramme, pour qu'il représente la réalité du PIC™. Nous nous sommes limités à l'utilisation de MicroCodeStudio et au langage BASIC. Mais ce programme aurait pu être écrit en utilisant le langage C ou l'assembleur. Chacun de ces interpréteurs possède des fonctions, qui aurait pu nous aider dans l'écriture de notre code. C'est le cas de MicroCodeStudio qui possède déjà des fonctions pour initialiser un afficheur LCD et pour lui transmettre du texte, soit en binaire, en décimal ou en HEX directement! Les routines de délais sont aussi incluses dans cet interpréteur. Quand nous voulons accomplir une tâche, il peut devenir très intéressant de voir les outils qui sont à notre portée, de comprendre leurs fonctionnements et au besoin de les modifier. |
La commande LCDOUT prend
pour acquis que nous voulons utiliser un afficheur compatible avec le Hitachi 44780, que nous voulons l'utiliser en mode 4 bits relié au PORTA 0 à 3. La pin de contrôle RS devrait être relié à porta.4 et la pin de contrôle E sur la broche portb.3 . Cet afficheur comportera 2 lignes. Ces choix sont définis dans le fichier Pbppic14.lib, mais nous pouvons les modifier en ajoutant des lignes de code qui redéfiniront les paramètres à utiliser. Dans notre cas nous avons choisis d'utiliser le porta pour nos lignes RS et E, dans le but de libérer le portb. Nous devrons indiquer ces changements à l'interpréteur, de la façon suivante. |
Tout le reste est compatible, nous pourrons donc nous servir de la commande LCDOUT sans faire d'autres modifications. Même l'initialisation de l'afficheur est pris en charge par BASIC, la seule considération est que nous devons attendre 500 mS avant d'accéder au LCD, cette pause pourra être réalisé en écrivant simplement PAUSE 500. |
L'écriture au LCD sera aussi plus simple, certaines commandes de base pour le contrôle sont incluses dans le programme de l'interpréteur comme: |
Nous venons de réduire considérablement le nombre de lignes de code à rédiger, mais ces fonctions ne sont qu'incluses dans le BASIC et ne nous serons d'aucune utilité avec d'autres interpréteurs, malheureusement. Vous pouvez regarder le programme PROG2.bas qui vous à été remis et vous rendre compte de la différence au niveau du nombre de lignes. Par contre cette deuxième solution pourra prendre plus de place dans notre PIC™, certaines de ces fonctions charge plus de code machine qu'il n'en faut. |
Voici l'écriture du programme PROG1.BAS terminé! |
Plus bas vous avez une version PROG2.BAS très amélioré! |
'**************************************************************** '* Name : PROG1.BAS '* Author : Michel-A Allard '* Notice : Copyright (c) 2007 '* : All Rights Reserved '* Date : 22/12/07 '* Version : 1.0 '* Notes : Programme servant à effectuer une conversion '* : binaire et donne l'équivalent en décimal et HEX '* : Utilise des commutateurs pour l'entrée binaire et '* : un afficheur LCD (Hitachi 44780) pour la sortie '**************************************************************** @ DEVICE PIC16F628, INTRC_OSC_NOCLKOUT,WDT_OFF,MCLR_ON,PWRT_ON,BOD_ON,CPD_OFF,PROTECT_OFF CMCON = 7 'désactive les comparateurs VRCON = 0 'désactive le voltage de référence CCP1CON = 0 'désactive le PWM define OSC 4 'DÉCLARATION DES VARIABLES NEW VAR BYTE 'Conserve l'états des commutateurs inversés(lecture 0 = commutateurs ON) OLD VAR BYTE 'Conserve l'états des commutateurs précédents(lecture 0 = commutateurs ON) NEWBIN VAR BYTE 'Conserve la valeur du code d'entrée *** BIN is a reserved name *** NEWDEC VAR BYTE 'Conserve la valeur du code d'entrée *** DEC is a reserved name *** NEWHEX VAR BYTE 'Conserve la valeur du code d'entrée TEMPA VAR BYTE 'Conserve la valeur de l'accumulateur COUNTER VAR BYTE 'Utilisé par la routine Delay ***COUNT is a reserved name DATABIT VAR BIT 'Utilisé par la routine SendLCD COMMAND var byte 'Utilisé pour la routine SendLCD COMSAVE var byte 'Utilisé par la routine SendLCD TEMPB VAR BYTE 'Utilisé par la routine DoDelay LCD_RS var PORTA.7 'Pointe le bit 7 du port A, LCD Register Select pin LCD_E VAR PORTA.6 'Pointe le bit 6 du port A, LCD Clock pin TRISA = %00100000 'port A en sortie sauf RA5, reset TRISB = $FF 'port B en entrée OPTION_REG.7 = 0 'active les pullups du port B MAIN: DATABIT = 0 'Pour l'initialisation des commandes command = $33 'Pour l'initialisation COUNTER = 10 'Environ 5 mS GOSUB SendLCD 'Le travail sera executé command = $32 'Pour l'initialisation GOSUB SendLCD 'Le travail sera executé COUNTER = 1 'Environ 500uS COMMAND = $28 'Place le LCD en mode 4 bits,2 lignes, font 5x7 GOSUB SendLCD 'DO IT COMMAND = $08 'Display OFF GOSUB SendLCD 'DO IT COMMAND = $0C 'Display ON, cursor off, no blink GOSUB SendLCD 'DO IT COMMAND = $06 'Auto increment cursor position GOSUB SendLCD 'DO IT new = portb 'Lit l'état de commutateurs DoMore: old = NEW 'Conserve l'état des commutateurs NEWBIN = ~NEW 'Inverse les bits databit=0 'S'assurer que l'on est en mode contrôle COMMAND= $01 'Effacer le LCD et placer le curseur au début GOSUB SendLCD 'DO IT databit=1 'S'assurer que l'on est en mode data COMMAND= "B" 'envoyer la lettre B GOSUB SendLCD 'DO IT COMMAND= "I" 'envoyer la lettre I GOSUB SendLCD 'DO IT COMMAND= "N" 'envoyer la lettre N GOSUB SendLCD 'DO IT COMMAND= ":" 'envoyer le caractère : GOSUB SendLCD 'DO IT COMMAND= " " 'envoyer un espace GOSUB SendLCD 'DO IT 'Cette partie envoie les valeurs binaire au LCD 'Le caractère HEX pour est $30 pour le zéro et $30 pour le 1 command= $30 + NEWBIN.7 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT command= $30 + NEWBIN.6 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT command= $30 + NEWBIN.5 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT command= $30 + NEWBIN.4 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT COMMAND= " " 'envoyer un espace GOSUB SendLCD 'DO IT command= $30 + NEWBIN.3 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT command= $30 + NEWBIN.2 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT command= $30 + NEWBIN.1 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT command= $30 + NEWBIN.0 'Si c'est un 0 COMMAND =$30 sinon $31 GOSUB SendLCD 'DO IT DATABIT =0 'On se place en mode command COMMAND=$C0 'On positionne le curseur sut la deuxième ligne GOSUB SendLCD 'DO IT DATABIT =1 'On se place en mode data COMMAND= "D" 'envoyer la lettre D GOSUB SendLCD 'DO IT COMMAND= "E" 'envoyer la lettre E GOSUB SendLCD 'DO IT COMMAND= "C" 'envoyer la lettre C GOSUB SendLCD 'DO IT COMMAND= ":" 'envoyer le caractère : GOSUB SendLCD 'DO IT COMMAND= " " 'envoyer un espace GOSUB SendLCD 'DO IT 'Cette partie envoie les valeurs décimale au LCD en utilisant 3 digits 'Le caractère HEX pour les chiffres est $30 + digit EX. 7 =$37 NewDEC=NEWBIN/100 'On conserve la valeur des centaines COMMAND= $30+newdec 'On additionne l'offset ASCII GOSUB SendLCD 'On l'envoie au LCD TEMPB=NEWBIN-(NEWDEC*100) 'On soustrait les centaines NEWDEC=TEMPB/10 'On extrait la valeur des dizaines COMMAND= $30+newdec 'On additionne l'offset ASCII NEWDEC=TEMPB-(NEWDEC*10) 'On soustrait les dizaines GOSUB SendLCD 'DO IT COMMAND= $30+newdec 'On additionne l'offset ASCII GOSUB SendLCD 'DO IT COMMAND= " " 'envoyer un espace GOSUB SendLCD 'DO IT COMMAND= "H" 'envoyer la lettre H GOSUB SendLCD 'DO IT COMMAND= "E" 'envoyer la lettre E GOSUB SendLCD 'DO IT COMMAND= "X" 'envoyer la lettre X GOSUB SendLCD 'DO IT COMMAND= ":" 'envoyer le caractère : GOSUB SendLCD 'DO IT COMMAND= " " 'envoyer un espace GOSUB SendLCD 'DO IT 'Cette partie envoie les valeurs hexadécimale au LCD en utilisant 2 digits 'Le caractère HEX pour les chiffres est $30 + digit EX. 7 =$37 'Les caractères pour A,B,C,D,E et F sont $41 @ $46 NEWHEX = NEWBIN /16 'On extrait les MSB IF NEWHEX > 9 then NEWHEX = NEWHEX +$07 'Ajoute l'offset pour ABCDEF COMMAND= $30+NEWHEX 'On additionne l'offset ASCII GOSUB SendLCD 'DO IT NEWHEX= NEWBIN & $0F 'On extrait les LSB IF NEWHEX > 9 then NEWHEX = NEWHEX +$07 'Ajoute l'offset pour ABCDEF COMMAND= $30+NEWHEX 'On additionne l'offset ASCII GOSUB SendLCD 'DO IT 'Le travail est terminé! on regarde maintenant si on doit recommencer LOOP: new = portb 'Lit l'état de commutateurs if new != old then goto DoMore goto LOOP SendLCD: COMSAVE=COMMAND 'Sauvegarde une copie de COMMAND command= command/16 'Décalage à droite de 4 bits if databit =1 then command = command+$80 PORTA = COMMAND 'Placer le data au LCD, E est à 0 gosub Delay LCD_E =1 'Place le clock à 1 gosub Delay LCD_E =0 'Place le clock à 0 gosub Delay Command= comsave 'Recharge la commande initiale SendLSB:command= command & $0F 'Masque les MSB if databit =1 then command = command+$80 PORTA = COMMAND 'Placer le data au LCD, E est à 0 gosub Delay LCD_E =1 'Place le clock à 1 gosub Delay LCD_E =0 'Place le clock à 0 gosub Delay RETURN Delay: tempa=counter 'On sauvegarde la valeur de COUNTER TEMPA=TEMPA + 1 'On ajuste la valeur DlyLoop:TEMPA=TEMPA - 1 'On diminue le nombre de fois gosub DoDelay 'environ 500 uS if TEMPA = 0 then return 'si on a fini on retourne goto DlyLoop ' sinon on continue DoDelay:TEMPB=$F8 '248 en décimal Moredly:TEMPB=TEMPB-1 'On le décrémente if TEMPb = 0 then return 'si on a fini on retourne goto Moredly 'si non on continue |
Deuxième version : |
'**************************************************************** '* Name : PROG2.BAS '* Author : Michel-A Allard '* Notice : Copyright (c) 2007 '* : All Rights Reserved '* Date : 22/12/07 '* Version : 1.0 '* Notes : Programme servant à effectuer une conversion '* : binaire et donne l'équivalent en décimal et HEX '* : Utilise des commutateurs pour l'entrée binaire et '* : un afficheur LCD (Hitachi 44780) pour la sortie '* : Utilise les fonctions du LCD '**************************************************************** @ DEVICE PIC16F628, INTRC_OSC_NOCLKOUT,WDT_OFF,MCLR_ON,PWRT_ON,BOD_ON,CPD_OFF,PROTECT_OFF CMCON = 7 'désactive les comparateurs VRCON = 0 'désactive le voltage de référence CCP1CON = 0 'désactive le PWM define OSC 4 'DÉCLARATION DES VARIABLES NEW VAR BYTE 'Conserve l'états des commutateurs inversés(lecture 0 = commutateurs ON) OLD VAR BYTE 'Conserve l'états des commutateurs précédents(lecture 0 = commutateurs ON) NEWBIN VAR BYTE 'Conserve la valeur du code d'entrée *** BIN is a reserved name *** TRISA = %00100000 'port A en sortie sauf RA5, reset TRISB = $FF 'port B en entrée OPTION_REG.7 = 0 'active les pullups du port B DEFINE LCD_RSREG PORTA 'Le bit RS est maintenant relié au port A DEFINE LCD_RSBIT 7 'Le bit RS est maintenant relié au bit 7 DEFINE LCD_EREG PORTA 'Le bit E est maintenant relié au port A DEFINE LCD_EBIT 6 'Le bit E est maintenant relié au bit 6 MAIN: PAUSE 500 'Attendre 500 mS new = portb 'Lit l'état de commutateurs DoMore: old = NEW 'Conserve l'état des commutateurs NEWBIN = ~NEW 'Inverse les bits LCDOUT $FE,1 'Effacer le LCD LCDOUT "BIN: " 'envoyer les lettres BIN: 'Cette partie envoie les valeurs binaire au LCD LCDOUT BIN4 NEWBIN/16 'envoie le MSB en utilisant 4 caractères lcdout " ", bin4 newbin 'envoie le LSB en utilisant 4 caractères lcdout $FE, $C0 'Place le curseur sur la 2e ligne LCDOUT "DEC: " 'envoyer les lettres DEC: 'Cette partie envoie les valeurs décimale au LCD en utilisant 3 digits LCDOUT DEC3 NEWBIN 'envoie la valeur décimale en utilisant 3 caractères LCDOUT " HEX: " 'envoyer le texte HEX: 'Cette partie envoie les valeurs hexadécimale au LCD en utilisant 2 digits LCDOUT HEX2 NEWBIN 'envoie la valeur hexa en utilisant 2 caractères 'Le travail est terminé! on regarde maintenant si on doit recommencer LOOP: new = portb 'Lit l'état de commutateurs if new != old then goto DoMore goto LOOP |
Voici la simulation du convertisseur avec Proteus : |