<< 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 4

Téléchargement des fichiers originels ici
Publié le 26/10/2001

18. Astuces de programmation (suite)

Index coursIndex du cours
Chapitre précédent18. Astuces de programmation
Chapitre suivant19. La norme ISO 7816

 

18. Astuces de programmation (suite)
18.5 Adressage indirect pointant sur 2 zones différentes

Voici encore une astuce. Imaginez que vous devez copier 15 variables d’un emplacement mémoire vers un autre (ou encore comparer 2 zones mémoires différentes etc.). En fait vous allez rapidement vous heurter à un problème. Vous ne disposez que d’un seul pointeur FSR pour pointer sur vos variables.

Réalisons donc ce programme : mem1 est l’adresse de départ de la première zone, mem2 l’adresse de départ de la seconde zone

  movlw mem1 ; on pointe sur la première zone
  movwf FSR ; on initialise le pointeur sur la zone source
  movlw 15 ; 15 variables à transférer
  movwf cmpt ; sauver dans compteur de boucles
loop      
  movf INDF , w ; charger source
  movwf tampon ; on doit sauver dans un emplacement tampon
  movlw mem2-mem1 ; écart d’adresse entre les 2 emplacements
  addwf FSR , f ; pointer sur destination
  movf tampon , w ; recharger valeur source
  movwf INDF ; sauver dans destination
  movlw (mem1-mem2)+1 ; Ecart entre adresse de destination et adresse
      ; de la source suivante, d’où le +1
  addwf FSR,f ; ajouter au pointeur
  decfsz cmpt , f ; décrémenter compteur de boucles
  goto loop ; pas dernier emplacement, suivant

Ce programme est valable lorsque vous n’avez pas le choix des zones de départ et de destination. Maintenant, qui vous empêche de placer vos variables dans des endroits qui vous arrangent ? Plaçons mem1 et mem2 dans la zone de variables :

  org 0x10 ; on choisit une zone mémoire dans la zone RAM
    mem1 : 15 ; 15 emplacements pour la source
  org 0x30 ; on choisit une zone RAM
    mem2 : 15 ; 15 emplacements destination.

Maintenant, la source va de B’00010000’ à B’00011111 ‘. Et la destination de B’00110000’ à B’00111111’

Il n’y a donc que le bit 5 de FSR qui change entre source et destination. Pour modifier des bits, pas besoin de l’accumulateur, donc pas besoin de sauver et recharger la valeur à transférer.

Nous pouvons donc modifier notre programme de la manière suivante :

  movlw mem1 ; on pointe sur la première zone
  movwf FSR ; on initialise le pointeur sur la zone source
  movlw 15 ; 15 variables à transférer
  movwf cmpt ; sauver dans compteur de boucles
loop      
  movf INDF , w ; charger source
  bsf FSR , 5 ; on pointe sur destination
  movwf INDF ; sauver dans destination
  bcf FSR , 5 ; on pointe sur source
  incf FSR , f ; pointer sur suivant
  decfsz cmpt , f ; décrémenter compteur de boucles
  goto loop ; pas dernier emplacement, suivant

Avec les PICs qui utilisent plusieurs banques, comme la 16F876, vous pouvez également utiliser la même adresse dans 2 banques différentes, et passer d’une à l’autre en modifiant les bits RP du registre STATUS.

18.6 Les tableaux en mémoire programme

Supposons que nous avons besoin d’un tableau de taille importante. Par exemple un tableau de 200 éléments. Où placer ce tableau ? Dans la RAM il n’y a pas assez de place, dans l’eeprom non plus. Ne reste donc plus que la mémoire de programme.

Le problème, c’est que la 16F84, contrairement à la 16F876, par exemple, ne dispose d’aucune méthode pour aller lire les données dans la mémoire programme. La seule méthode pour y avoir accès est d’utiliser des instructions.

Supposons un cas simple : nous voulons créer un tableau contenant le carré des nombres de 0 à 15. Nous pourrions utiliser un petit sous-programme de la forme suivante :

  • On teste si le nombre passé en argument = 0. Si oui, on retourne 0
  • On teste si le nombre passé en argument = 1. Si oui, on retourne 1
  • On teste si le nombre passé en argument = 2. Si oui, on retourne 4
  • Et ainsi de suite…

Vous voyez tout de suite qu’il faudra plusieurs instructions par valeur du tableau. En cas d’un tableau de 200 éléments, on n’aura pas assez de mémoire programme pour tout écrire. Nous allons donc utiliser une autre astuce.

Le cœur de l’astuce est d’utiliser l’instruction retlw qui permet de retourner une valeur passée en argument. Ecrivons donc notre tableau sous forme de retlw, cela donne :

  retlw 0 ; carré de 0 = 0
  retlw 1 ; carré de 1 = 1
  retlw 4 ; carré de 2 = 4
  retlw 9 ; carré de 3 = 9
  retlw 16 ; carré de 4 = 16
  retlw 25 ; carré de 5 = 25
  retlw 36 ; carré de 6 = 36
  retlw 49 ; carré de 7 = 49
  retlw 64 ; carré de 8 = 64
  retlw 81 ; carré de 9 = 81
  retlw 100 ; carré de 10 = 100
  retlw 121 ; carré de 11 = 121
  retlw 144 ; carré de 12 = 144
  retlw 169 ; carré de 13 = 169
  retlw 196 ; carré de 14 = 196
  retlw 225 ; carré de 15 = 225

Il ne nous reste plus qu’à trouver une astuce pour nous brancher sur la bonne ligne de notre tableau/programme. Si nous nous souvenons que nous pouvons effectuer des opérations sur le PCL, nous pouvons utiliser cette propriété.

Supposons donc que le nombre à élever au carré soit contenu dans le registre w. Si, une fois sur la première ligne du tableau, nous ajoutons w à PCL, nous sauterons directement sur la bonne ligne. Complétons donc notre sous-routine. Il nous suffit donc d’ajouter avant notre tableau la ligne :

carre      
  addwf PCL , f ; ajouter w à PCL

Donc, si on charge 4 dans w et qu’on effectue un appel " call carre ". Le programme se branche sur cette ligne, le PCL est incrémenté de 4 et le programme exécute donc alors la ligne " retlw 16 ". En effet, le PC pointe toujours sur l’instruction suivante, donc au moment de l’exécution de " addwf PCL , f ", celui-ci pointait sur la ligne " retlw 0 ".

A ce stade, vous devez vous rappeler que l’adresse pour une opération sur le registre PCL est complétée par le contenu de PCLATH. Vous devez donc initialiser correctement celui-ci avec le numéro de la page de l’adresse de votre tableau.

Par exemple, si votre tableau est à l’adresse 0x200, vous devrez mettre 0x02 dans PCLATH.

De plus, l’opération sur PCL ne modifiera pas automatiquement PCLATH, donc votre tableau ne devra pas dépasser 256 éléments, et, de plus, ne devra pas déborder sur la page suivante. Une page étant la plage adressée par les 256 valeurs possibles de PCL, sans toucher à PCLATH.

Comment éviter ceci ? Tout simplement en imposant au tableau une adresse de départ telle que le tableau tient tout entier dans la même " page " de 256 emplacements.

Ceci permet alors d’utiliser un tableau de 256 éléments. Voici donc les emplacements disponibles pour un tableau d’une telle taille :

Adresse B’00000 00000000’, soit adresse 0x00. Pas utilisable, car adresse de reset
Adresse B’00001 00000000’, soit adresse 0x100. Premier emplacement utilisable
Adresse B’00010 00000000’, soit adresse 0x200. Second emplacement utilisable
Adresse B’00011 00000000’, soit adresse 0x300. Dernier emplacement utilisable.

Il va de soi que si vous avez besoin d’un plus petit tableau, vous pouvez le commencer ailleurs, en veillant bien à ne pas occasionner de débordement. Vous pouvez donc ajouter la ligne : org 0x300 avant l’étiquette de début de sous-routine. Résultat final :

;*********************************************************************
; TABLEAU DES CARRES *
;*********************************************************************
;--------------------------------------------------------------------------------------------
; Ne pas oublier de valider le radix décimal par défaut dans le nœud
; principal du projet. N'oubliez pas que la directive "END" doit se
; trouver après la dernière ligne de votre programme. Ne laissez donc
; pas cette directive dans le programme principal.
;--------------------------------------------------------------------------------------------
  org 0x300 ; adresse du tableau
carre      
  addwf PCL , f ; ajouter w à PCL
  retlw 0 ; carré de 0 = 0
  retlw 1 ; carré de 1 = 1
  retlw 4 ; carré de 2 = 4
  retlw 9 ; carré de 3 = 9
  retlw 16 ; carré de 4 = 16
  retlw 25 ; carré de 5 = 25
  retlw 36 ; carré de 6 = 36
  retlw 49 ; carré de 7 = 49
  retlw 64 ; carré de 8 = 64
  retlw 81 ; carré de 9 = 81
  retlw 100 ; carré de 10 = 100
  retlw 121 ; carré de 11 = 121
  retlw 144 ; carré de 12 = 144
  retlw 169 ; carré de 13 = 169
  retlw 196 ; carré de 14 = 196
  retlw 225 ; carré de 15 = 225
  END   ; directive fin de programme

Il ne nous reste plus qu’à construire un petit programme principal qui fasse appel à cette sous-routine.

;**********************************************************************
; DEMARRAGE SUR RESET
;**********************************************************************
  org 0x000 ; Adresse de départ après reset
;**********************************************************************
; PROGRAMME PRINCIPAL
;**********************************************************************
start      
  clrf nombre ; effacer nombre
loop      
  movf nombre , w ; charger nombre
  call carre ; prendre le carré du nombre
  incf nombre , f ; incrémenter nombre
  btfss nombre , 4 ; tester si nombre >15
  goto loop ; non, nombre suivant
  goto start ; oui, on recommence à 0

Compilez votre programme puis passez-le au simulateur. Vous constaterez…. Que cela ne fonctionne pas. Pourquoi ?

Et bien, en réalité, toute opération dont le PC est la destination fait intervenir PCLATH. Or nous avons laissé PCLATH à 0. L’adresse de calcul n’est donc pas la bonne. Avant le saut sous aurions du ajouter :

  movlw 03  
  movwf PCLATH  

Afin de permettre à PCLATH de pointer sur la page 0x300 lors du calcul d’incrémentation de PCL.

Vous pouvez avantageusement remplacer la ligne org 0x300 par

repere      
  ORG (repere+31)& 0x3E0 ; adresse du tableau

Dans ce cas, pas besoin de modifier PCLATH car on reste en page 00

Le but de cette manipulation est d’obtenir automatiquement la première adresse disponible pour laquelle il n’y aura pas de débordement de notre PCL. Pour un tableau de 256 éléments, utilisez "ORG (repere+255) & 0x300". Faites des essais à la main sur un bout de papier pour vous en convaincre. Par exemple, on calcule la fin du tableau (repere+255). Org pointe alors sur la bonne page. On élimine ensuite les bits correspondants (255 = 8 bits) de la taille mémoire totale du programme (0x3FF). Reste donc & 0x300.

Le fichier obtenu est disponible sous la dénomination " tableau.asm " [ NDLR : Fichiers exemples ici ]

18.7 Les variables locales

Un des reproches qu’on entend souvent au sujet des PICs est le nombre restreint des variables disponibles. Il ne faudrait cependant pas croire que, sous prétexte que nous programmons en assembleur, nous ne disposons pas de variables locales.

En fait, l’utilisation de ce type de variable est très simple et réduit considérablement le nombre de variables nécessaires dans un programme. Voici ma méthode personnelle d’utilisation de ce type de variables.

Pour rappel, ce sont des variables qui ne seront utilisées qu’à l’intérieur d’une sous-routine, et qui ne servent plus, une fois la sous-routine terminée. Partons donc d’un programme imaginaire qui utilise deux sous-routines.

  • La première sous-routine s’appelle " tempo ", elle utilise 2 compteurs pour effectuer un comptage de temps.
  • La seconde est la sous-routine " fonction ". Elle reçoit un paramètre dans W, doit sauver le résultat dans une variable, et utilise 2 autres variables pour ses calculs intermédiaires.
  • La condition pour l’utilisation de variables locales identiques, est que une des routines n’appelle pas l’autre.

18.7.1 Détermination des variables locales

Si nous examinons les routines, nous constatons que les 2 variables de la sous-routine " tempo " ne sont utiles qu’à l’intérieur de celle-ci. Nous pouvons donc utiliser des variables locales.

La variable résultat de la sous-routine " fonction " devra être conservé après exécution de cette sous-routine, ce n’est donc pas une variable locale. Nous dirons que c’est une variable globale. Par contre, les 2 variables utilisées pour les calculs intermédiaires ne seront plus d’aucune utilité. Ce sont des variables locales.

18.7.2 Construction sans variables locales

Que serait notre zone de variables sans l’utilisation des variables locales ? Et bien créons notre zone de variables :

  CBLOCK 0X0C  
    Cmpt1 : 1 ; compteur 1 pour routine tempo
    Cmpt2 : 1 ; compteur 2 pour routine tempo
    Resultat : 1 ; résultat pour routine fonction
    Interm1 : 1 ; résultat intermédiaire1 pour fonction
    Interm2 : 1 ; résultat intermédiaire2 pour fonction
  ENDC    

Nous voyons que nous aurons besoin de 5 variables. Utilisons maintenant les variables locales.

18.7.3 Construction avec variables locales

Premièrement, nous allons réserver les emplacements mémoires nécessaires pour les variables locales. Le plus grand nombre de variables locales utilisées par une fonction, est de 2. Nous aurons donc 2 variables locales. Déclarons-les. Ensuite, il nous reste une variable globale à ajouter. Créons notre zone data :

  CBLOCK 0X0C  
    Local1 : 1 ; variable locale 1
    Local2 : 1 ; variable locale 2
    Resultat : 1 ; résultat pour fonction
  ENDC    

Il ne nous faudra donc plus que 3 variables au lieu des 5 initiales. Pour mieux nous y retrouver, nous pouvons attribuer les mêmes noms que précédemment pour nos variables locales, en ajoutant simplement des DEFINE ou des EQU.

#DEFINE cmpt1 Local1 ; compteur1 = variable locale1
#DEFINE cmpt2 Local2 ; compteur2 = variable locale2
#DEFINE interm1 Local1 ; résultat intermédiaire1 = variable locale1
#DEFINE interm2 Local2 ; résultat intermédiaire2 = variable locale2

Et voilà, pas besoin du C pour utiliser les variables locales très simplement.

18.8 Conclusion

Vous voici en possession de quelques astuces de programmation. Le but était de vous montrer les méthodes de raisonnement afin de vous permettre de développer vos propres trucs et astuces.

 

Index cours
Index du cours
Chapitre précédent
18. Astuces de programmation
Chapitre suivant
19. La norme ISO 7816

 


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

Page http://fribotte.free.fr/bdtech/cours/pic16f84/PART1_cours18b.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