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 :