<< The Fribotte Homepage >>
Un club de passionnés en robotique participant à la coupe de France E=M6.
[Accueil] [Qui sommes-nous ?] [Robots] [Coupe e=m6] [BD Technique] [Forum] [Reportages] [Liens] [WiKiFri]

Fribotte

 

La Programmation des pics
par Bigonoff
Premiere partie - pic 16f84 - Révision 5

Téléchargement des fichiers originels ici
Publié le 27/02/2002

11. Réalisation d’un programme embarqué (Suite)

Index coursIndex du cours
Chapitre précédent11. Réalisation d’un programme embarqué
Chapitre suivant12. Les interruptions

 

2. Réalisation d’un programme embarqué (Suite)
11.11 Exemple d’application

Toujours en partant de notre schéma, nous désirons allumer la LED lorsque nous pressons le bouton, et l’éteindre lorsque nous le relâchons. Voici un exemple du programme nécessaire (attention, prenez garde que le niveau sur RB2 passe à 0 lorsque le bouton est enfoncé (connexion à la masse)

  bsf STATUS , RP0 ; on passe en banque 1
  bcf OPTION_REG, RBPU ; résistance de rappel en service
  bcf TRISA , 2 ; bit 2 de TRISA à 0 = sortie pour RA2
      ; inutile de configurer TRISB, car il est
      ; en entrée par défaut au reset de la PIC
  bcf STATUS , RP0 ; on repasse en banque 0
       
boucle     ; étiquette de début de la boucle principale
  btfss PORTB , 2 ; tester RB2, sauter si vaut 1
  bsf PORTA , 2 ; RB2 vaut 0, donc on allume la LED
  btfsc PORTB , 2 ; tester RB2, sauter si vaut 0
  bcf PORTA , 2 ; RB2 vaut 1, donc LED éteinte
  goto boucle ; et on recommence

 

11.12 La routine d’initialisation

Examinons les instructions contenues à partir de l’étiquette " init "

Les 2 premières lignes ,

  clrf PORTA ; sorties portA à 0
  clrf PORTB ; sorties portB à 0

... mettent les sorties des PORTs à 0. Comme ça, lorsque vous configurerez les ports en sortie, ils enverront par défaut des niveaux 0V. Si votre électronique demande, pour être sécurisée, d’autres valeurs, à vous de vous en charger.

La ligne suivante permet de se connecter sur la banque1. Jusqu’à nouvel ordre, les instructions suivantes utilisent des registres situés banque1.

Ensuite nous trouvons la ligne :

  clrf EEADR ; permet de diminuer la consommation

Ne vous inquiétez pas pour cette ligne. Sachez seulement qu’une valeur différente dans ce registre augmente légèrement la consommation desPIC version 16C84. Cette ligne est donc un petit plus pour les montages alimentés par pile. Le registre EEADR sera vu lors des accès à l’EEPROM interne. Notez que ce bug a été corrigé sur la 16F84. Cette ligne n’est donc pas nécessaire dans ce cas.

Ensuite, nous trouvons :

  movlw OPTIONVAL ; charger masque
  movwf OPTION_REG ; initialiser registre option

Rappelez-vous que OPTIONVAL est une constante. Sa valeur a été définie précédemment par nos soins en 0x08. Cette valeur sera envoyée ici dans le registre OPTION (OPTION_REG). Souvenez-vous que OPTION_REG est le nom déclaré dans MPLAB pour le registre OPTION.

Suit la petite routine suivante, destinée à effacer la RAM. Souvenez-vous, et c’est très important, qu’à la mise sous tension, la RAM contient des valeurs aléatoires. Pour éviter les mauvaises surprises, j’ai intégré cette routine à chaque démarrage, qui assure que le RAM ne contient que des 0.

    ; Effacer RAM  
       
  movlw 0x0c ; initialisation pointeur
  movwf FSR ; pointeur d'adressage indirect
init1      
  clrf INDF ; effacer ram
  incf FSR,f ; pointer sur suivant
  btfss FSR,6 ; tester si fin zone atteinte (>=0x40)
  goto init1 ; non, boucler
  btfss FSR,4 ; tester si fin zone atteinte (>=0x50)
  goto init1 ; non, boucler
      ; ici se trouve la suite

Voilà donc un exemple concret de l’utilisation de l’adressage indirect.

En premier lieu, on initialise le pointeur vers la zone à manipuler (ici, la zone RAM utilisateur). Cette zone commence à l’adresse 0x0C (voir tableau 4-2) et se termine à l’adresse 0x4F incluse. La zone située dans la banque 1 n’existe pas pour cette PIC (image de la banque0) et n’a donc pas besoin d’être initialisée.

Puis vous trouvez l’instruction clrf INDF, qui signifie donc, si vous avez suivi, effacer l’emplacement mémoire dont l’adresse se trouve dans le registre FSR. On efface donc la mémoire située à l’emplacement 0x0C.

Vient ensuite l’incrémentation de FSR, donc, maintenant, FSR POINTE sur 0x0D. On trouve alors 2 tests. Pour sortir de cette routine et arriver à la ligne que j’ai indiqué (ici se trouve la suite), il faudra donc éviter les 2 lignes goto.

Vous tombez sur le premier goto si le bit 6 de FSR vaut 1. Pour éviter ce goto, FSR devra donc valoir au moins B’01000000’, ou encore 0x40. A ce moment, les adresses 0x0C à 0x3F sont donc mises à 0.

On arrive au deuxième test. Le goto suivant sera donc sauté si le bit 4 de FSR est à 1. On arrivera donc à la ligne ‘ici se trouve la suite’ uniquement lorsque les bits 4 et 6 de FSR seront égaux à 1. Ceci donne l’adresse suivante : B’01010000’, soit 0x50.

Notez pour ceux qui ont suivi que vous auriez pu également utiliser un compteur de boucles et vous servir de l’instruction decfsz. Ceci aurait cependant nécessité l’utilisation d’une variable, variable qui se serait effacée elle-même par la présente routine, avec plantage du programme. Donc, la méthode présentée ici est donc la plus simple.

Effacez ensuite les lignes suivante :

  bcf TRISA,0 ; Bit PORTA.0 en sortie (exemple)
  bcf STATUS,RP0 ; Sélectionner banque 0
  movlw INTERMASK ; masque interruption
  movwf INTCON ; charger interrupt control

car elles ne nous intéressent pas pour notre programme.

Voyons maintenant ce que nous devons ajouter :

Tout d’abord, nous devons initialiser RA2 en sortie, pour pourvoir agir sur la LED. L’instruction sera donc :

  bcf TRISA , 2  

Or, nous avons déclaré dans les DEFINE, LED comme étant égal à PORTA,2. Pour permettre un remplacement facile de la LED sur une autre pin ,que se passe-t-il si nous écrivons :

  bcf LED  

Certains vont dire " on éteint la LED ". Et bien, pas du tout. Cette instruction sera remplacée par l’assembleur par :

  bcf PORTA , 2  

Autrement dit par :

  bcf 0x05 , 2  

Or, RP0 est toujours positionné à 1au moment de l’exécution de cette ligne. On pointe donc toujours sur la banque 1. Si vous regardez ce qu’il y a à l’adresse 0x05 dans la banque1, vous trouvez TRISA. Donc, l’opération a donc placé RA2 en sortie.

En changeant donc simplement le DEFINE LED au début du programme, vous pouvez donc changer toutes les références à RA2 dans tout le programme, même pour TRISA. Placez un petit commentaire d’en-tête, et vous obtenez :

    ; initialisations spécifiques
       
  bcf LED ; LED en sortie (banque1)

Repassez ensuite en banque0 avant de quitter l’intialisation. C’est une bonne pratique, car beaucoup d’erreurs sont provoquées par des erreurs de banque, ajoutez donc :

  bcf STATUS , RP0 ; Repasser en banque 0

Terminez avec la ligne :

  goto start  

qui branche le programme principal. Pour le moment, cela peut paraître inutile, car le programme principal suit directement cette instruction, mais nous allons ajouter plus loin un sous-programme qui sera intercalé entre les deux.

Voici à quoi devrait maintenant ressembler votre routine d’initialisation, que vous devriez maintenant avoir entièrement comprise (pas besoin d’un aspro ?).

Rassurez-vous, cela a l’air dur au début, mais, une fois assimilé, c’est très simple. De plus, vous avez vu pratiquement tout ce qu’il faut savoir pour réaliser un programme sans routines d’interruptions.

;*********************************************************************
; INITIALISATIONS
;*********************************************************************
       
init      
  clrf PORTA ; sorties portA à 0
  clrf PORTB ; sorties portB à 0
  bsf STATUS, RP0 ; sélectionner banque 1
  clrf EEADR ; permet de diminuer la consommation
  movlw OPTIONVAL ; charger masque
  movwf OPTION_REG ; initialiser registre option
       
    ; Effacer RAM  
       
  movlw 0x0c ; initialisation pointeur
  movwf FSR ; pointeur d'adressage indirect
init1      
  clrf INDF ; effacer ram
  incf FSR,f ; pointer sur suivant
  btfss FSR,6 ; tester si fin zone atteinte (>=0x40)
  goto init1 ; non, boucler
  btfss FSR,4 ; tester si fin zone atteinte (>=0x50)
  goto init1 ; non, boucler
       
    ; initialisations spécifiques
       
  bcf LED ; LED en sortie (banque1)
  bcf STATUS , RP0 ; Repasser en banque 0
  goto start ; sauter au programme principal

Descendons maintenant dans le programme principal, et supprimons la ligne :

  clrwdt   ; effacer watch dog

 

11.13 Les résultats de la compilation

Lancez la compilation par la touche <F10>.

Dans la fenêtre de résultat de compilation, vous devez trouver une ligne du type :

Message[302] D:\DOCUME~1\LESSONS\DATAPIC\LED_CLI.ASM 101 : Register in operand not in bank 0. Ensure that bank bits are correct.

Ceci est un message d’avertissement (warning) qui vous signale qu’à la ligne 101 (dans mon cas, mais suivant vos espaces ou commentaires, vous aurez un autre numéro de ligne), vous avez accédé à un registre qui n’est pas situé en banque0.

MPASM vous demande de vérifier que votre bits RP0 est positionné correctement. Allez dans l ‘éditeur sur la ligne dont vous avez le numéro. Vous tombez sur la ligne :

  movwf OPTION_REG ; initialiser registre option

Un coup d’œil sur le tableau 4-2 vous indique que le registre OPTION est dans la banque1. Mais comme votre RP0 a été positionné sur 1 à cet endroit, il n’y a pas de problème. Quand vous aurez réalisé de gros programmes, vous aurez énormément de warning de ce type .

11.14 Le programme principal

Nous voulons faire clignoter notre LED à une fréquence de 1Hz (1 clignotement par seconde) . Nous allons donc créer un programme de la forme :

debut

  • J’allume la LED
  • J’attends ½ seconde
  • J’éteins la LED
  • J’attends ½ seconde
  • Je retourne au début

Nous voyons que nous utilisons 2 fois la temporisation de ½ seconde. Nous créerons donc un sous-programme que nous appellerons " tempo ". Notre programme principal aura donc l’aspect suivant :

start      
  bsf LED ; allumer la LED
  call tempo ; appeler la tempo de 0.5s
  bcf LED ; éteindre LED
  call tempo ; appeler la tempo de 0.5s
  goto start ; boucler

On aurait pu écrire également (en utilisant nos macros) :

start      
  LEDON   ; allumer la LED
  call tempo ; appeler la tempo de 0.5s
  LEDOFF   ; éteindre LED
  call tempo ; appeler la tempo de 0.5s
  goto start ; boucler

Choisissez la méthode que vous préférez. L’important, c’est d’avoir compris les deux méthodes.

11.15 La sous-routine de temporisation

Nous n’avons pas encore vu le timer0, ni les interruptions. Le but ici est de vous faire comprendre le fonctionnement de la 16F84. Pour réaliser une tempo, il suffit dans notre cas de faire perdre du temps à la 16F84 entre chaque inversion de la LED.

Nous devons donc perdre approximativement 0.5s. Les secondes ne sont pas appropriées pour les pics, qui travaillent à une vitesse beaucoup plus élevée. Nous utiliserons donc des unités de temps compatibles avec la PIC.

Notre PIC est cadencée à la fréquence de notre quartz, soit 4MHz. Or, la PIC exécute un cycle d’instruction tous les 4 cycles de l’horloge principale. La PIC exécutera donc (4.000.000/4) = 1 million de cycles par seconde.

La plupart des instructions (hormis les sauts) s’exécutent en 1 cycle, ce qui vous donne approximativement un million d’instructions par seconde. Vous verrez parfois la dénomination MIPS. Ceci signifie Million d’Instructions Par Seconde. Notre PIC avec ce quartz a donc une puissance de traitement de près de 1MIPS.

Chaque cycle d’instruction dure donc 1 millionième de seconde, ou encore une microseconde (µs). Voilà donc l’unité pour travailler avec notre PIC. Donc, notre tempo de 0.5s est donc une tempo de 500.000 microsecondes. Autrement dit, nous devons " perdre pour rien " 500.000 cycles dans notre routine de temporisation. Vous voyez donc que votre PIC, pour notre application, va passer son temps à ne rien faire d’utile.

La première idée qui vient à l’esprit est de réaliser une boucle qui va incrémenter ou décrémenter une variable.

Réalisons-la. Commençons donc par déclarer notre variable (cmpt1) dans la zone de RAM. Ajoutons donc cette déclaration . Nous obtenons :

;*********************************************************************
; DECLARATIONS DE VARIABLES
;*********************************************************************
       
  CBLOCK 0x00C ; début de la zone variables
    cmpt1 : 1 ; compteur de boucles 1
  ENDC   ; Fin de la zone

Créons maintenant l’ossature de notre sous-routine, que nous placerons entre la routine d’initialisation et le programme principal. N’oubliez pas de toujours utiliser des commentaires :

;*********************************************************************
; SOUS-ROUTINE DE TEMPORISATION
;*********************************************************************
;--------------------------------------------------------------------------------------------------------
; Cette sous-routine introduit un retard de 500.000 µs.
; Elle ne reçoit aucun paramètre et n'en retourne aucun
;--------------------------------------------------------------------------------------------------------
tempo      
  ; nous allons placer notre code ici
  return   ; retour de la sous-routine

Réalisons maintenant notre boucle.

tempo      
  clrf cmpt1 ; effacer compteur1
boucle1      
  decfsz cmpt1 ; décrémenter compteur1
  goto boucle1 ; si pas 0, boucler
  return   ; retour de la sous-routine

Lançons la compilation (F10) :

Nous obtenons dans la fenêtre des résultats une ligne supplémentaire de la forme :

Message[305] D:\DOCUME~1\LESSONS\DATAPIC\LED_CLI.ASM 134 : Using default destination of 1 (file).

Le numéro de ligne peut varier suivant votre code source. Comme la compilation s’est effectuée correctement, il s’agit une fois de plus d’un message de type warning. Positionnez-vous dans l’éditeur sur la ligne incriminée (ligne 134 pour moi). Vous êtes sur la ligne :

  decfsz cmpt1 ; décrémenter compteur1

En effet, l’instruction decfsz est de la forme " decfsz f , d ". Nous avons donc oublié d’indiquer la destination de l’opération. MPLAB vous signale qu’il a utilisé pour vous ‘,f’

Faites donc attention, car si vous aviez voulu obtenir le résultat dans w, votre programme était faux. Modifiez donc votre commande en ajoutant ‘,f’

  decfsz cmpt1, f ; décrémenter compteur1

Maintenant, nous allons calculer la durée de cette tempo.

tempo      
  clrf cmpt1 ; 1 cycle
boucle1      
  decfsz cmpt1, f ; 1 cycle si on ne saute pas, 2 si on saute. Donc, on ne sautera
      ; pas 255 fois et on sautera 1 fois
  goto boucle1 ; 2 cycles multiplié par 255 passages
  return   ; 2 cycles.

Le temps total est donc de :

  2 cycles pour l’appel de la sous-routine (call tempo)
  1 cycle pour le reset de la variable
257 cycles pour les 256 décrémentations
510 cycles pour les 255 goto
  2 cycles pour le return.

Soit un total de 772 cycles. On est loin des 500.000 cycles nécessaire. Pour la suite des calculs, nous allons négliger les 2 cycles du call et les 2 cycles du return (comparés aux 500.000 cycles nécessaires).

Bon, nous allons allonger notre routine, en réalisant une seconde boucle qui va forcer la première boucle à s’exécuter 256 fois. Commençons par déclarer une nouvelle variable cmpt2 :

    cmpt1 : 1 ; compteur de boucles 1
    cmpt2 : 1 ; compteur de boucles 2

Ecrivons donc les 2 boucles imbriquées :

tempo      
  clrf cmpt2 ; effacer compteur2
boucle2      
  clrf cmpt1 ; effacer compteur1
boucle1      
  decfsz cmpt1, f ; décrémenter compteur1
  goto boucle1 ; si pas 0, boucler
  decfsz cmpt2 , f ; si 0, décrémenter compteur 2
  goto boucle2 ; si cmpt2 pas 0, recommencer boucle1
  return   ; retour de la sous-routine

Vous voyez que notre première boucle est toujours là, mais au lieu d’effectuer le return une fois terminée, nous recommençons la boucle tant que cmpt2 ne soit pas également égal à 0. Nous allons donc exécuter 256 fois notre boucle 1

Quelle est la temporisation obtenue ? Calculons approximativement :

Durée de la boucle 1 : 257 cycles + 510 cycles + 1 cycle (clrf cmpt1) = 768 cycles.

Or cette boucle va être exécutée 256 fois, donc 768*256 = 196608 cycles, auquel il convient d’ajouter les quelques cycles d’initialisation etc.

Or, nous désirons 500.000 cycles. Nous devrons donc utiliser cette boucle (500.000/196608) = 2, 54 fois. Nous ne savons pas faire de demi boucle. Nous effectuerons donc 2 boucles. Nous allons nous arranger pour que nos deux premières boucles durent 500.000/2 = 250.000 cycles.

Chaque cycle ajouté dans la boucle1 est exécuté 256*256 fois. Chaque cycle exécuté dans la boucle 2 est exécuté 256 fois. Chaque cycle extérieur aux boucles est exécuté 1 fois. Nous avons donc la possibilité de réaliser des temporisations très précises. Ce n’est pas nécessaire ici. Cependant, nous allons quand même améliorer la précision.

Si nous ajoutons 1 cycle inutile dans la boucle1, nous ajouterons 256*256 = 65536 cycles. Nous devons en ajouter 250.000 – 196608 = 53392. Cela nous donnerait une erreur de 12000 cycles, soit 12 millièmes de seconde (12ms). La précision est largement suffisante pour notre application, mais je rappelle que vous pouvez affiner cette précision en effectuant les calculs précis. Je vous conseille même de le faire vous-même comme exercice. Vous pourrez vérifier vos résultats avec un chronomètre.

Pour perdre un cycle, nous ajouterons simplement l’instruction NOP, qui ne fait rien.

Reste donc à réaliser la dernière boucle de 2. Créons une troisième variable cmpt3 et une troisième boucle. (Rassurez-vous, il y a des méthodes plus simple, ceci est expliqué dans un but didactique : il importe simplement d’avoir compris).

;*********************************************************************
; DECLARATIONS DE VARIABLES
;*********************************************************************
       
  CBLOCK 0x00C ; début de la zone variables
    cmpt1 : 1 ; compteur de boucles 1
    cmpt2 : 1 ; compteur de boucles 2
    cmpt3 : 1 ; compteur de boucles 3
  ENDC   ; Fin de la zone

Voici le code final  :

;*********************************************************************
; SOUS-ROUTINE DE TEMPORISATION
;*********************************************************************
;--------------------------------------------------------------------------------------------------------
; Cette sous-routine introduit un retard de 500.000 µs.
; Elle ne reçoit aucun paramètre et n'en retourne aucun
;--------------------------------------------------------------------------------------------------------
tempo      
  movlw 2 ; pour 2 boucles
  movwf cmpt3 ; initialiser compteur3
boucle3      
  clrf cmpt2 ; effacer compteur2
boucle2      
  clrf cmpt1 ; effacer compteur1
boucle1      
  nop   ; perdre 1 cycle *256 *256 *2
  decfsz cmpt1 , f ; décrémenter compteur1
  goto boucle1 ; si pas 0, boucler
  decfsz cmpt2 , f ; si 0, décrémenter compteur 2
  goto boucle2 ; si cmpt2 pas 0, recommencer boucle1
  decfsz cmpt3 , f ; si 0, décrémenter compteur 3
  goto boucle3 ; si cmpt3 pas 0, recommencer boucle2
  return   ; retour de la sous-routine

Lancez la compilation avec <F10>. Si tout s’est bien passé, vous obtenez dans votre répertoire de travail le fichier LED_CLI.HEX

Envoyez- le dans votre PIC à l’aide de votre programmateur.

Placez la PIC sur la carte HORS TENSION.

Envoyez l’alimentation et regardez bien :

VOTRE LED CLIGNOTE A LA FREQUENCE DE 1HZ.

Félicitations, voici votre premier programme embarqué. La porte est ouverte à des tonnes de nouvelles applications.

CONSEIL IMPORTANT

Si, à ce stade, vous sautez au plafond en criant " CA MARCHE " (j’ai connu ça) [ NDLR :Les Fribottes aussi.] , ne vous précipitez pas en hurlant pour faire admirer votre réalisation à votre épouse. Je doute qu’elle partage votre enthousiasme pour " cette petite lampe qui clignote ".

Le fichier tel qu’il devrait être à la fin de cette leçon est disponible dans les fichiers joints.[ NDLR : Fichiers exemples ici ]


 

Index cours
Index du cours
Chapitre précédent
11. Réalisation d’un programme embarqué
Chapitre suivant
12. Les interruptions

 


Complétez cette page, posez vos questions et remarques ici : WiKiFri

Page http://fribotte.free.fr/bdtech/cours/pic16f84/PART1_cours11b.html modifiée le 14/10/2002.
Copyright fribotte@free.fr, libre de droit pour toute utilisation non commerciale.
Reproduction autorisée par simple mail