La Briquothèque de Pictrain
  Les pages regroupées sous ce titre “Briquothèque” comprendront, non plus des programmes complets exécutables, mais des pièces détachées, des “briques” à assembler par vous pour résoudre des problèmes courants : une réserve de morceaux de raisonnements logiques, à copier-coller dans vos oeuvres...
Aujourd´hui :
B1. LES DÉLAIS

B1.1 Théorie
Il est fréquent d´avoir à “attendre” durant le fonctionnement d´un programme un certain “délai”, c´est-à-dire d´effectuer une pause avant de continuer les traitements.
Ce délai est en général définit comme un minimum, par exemple “attendre au moins 5 mS”
On présentera dans ce premier chapitre de la logithèque PicTrain comment obtenir ces délais d´une manière simple et standardisée par l´introduction de boucles mettant un temps déterminé pour s´exécuter.
Remarque : ce que nous appelons “délai” est souvent appelé dans la littérature “tempo” pour temporisation, mais on parle de la même chose.
Remarque importante : il s´agit ici de “délai”, c´est-à-dire de la possibilité d´interrompre occasionnellement le programme durant un temps déterminé. Il n´est pas question de génération de signal périodique de fréquence donnée, comme abordé dans PicTrain2, et qui s´obtient en utilisant les Timer0 et Timer1 avec prédiviseur.
B1.1.1. Horloge et cycle
Dans nos montages PicTrain, nous utilisons toujours l´horloge interne du PIC, qui fonctionne à la vitesse de 4 MHz (Fosc = fréquence oscillateur).
Ces PIC peuvent tourner jusqu´à 20 MHz, mais avec un oscillateur externe. Petite curiosité, ils peuvent aussi tourner plus lentement, sans limite, on pourrait faire tourner un PIC avec une horloge à 1 Hz, même si cela ne présente pas beaucoup d´intérêt.
Un cycle de programme est toujours égal à Fosc/4, c´est-à-dire que dans notre cas d´horloge à 4 MHz, le programme s´exécute à 1 MHz, c´est-à-dire que les instructions de notre programme s´exécutent toujours à la vitesse de :
1 cycle (1 microseconde, écrit µS en abrégé ou uS sur le web) pour toutes les instructions, sauf
Les instructions incluant un débranchement : call, goto, return, retlw, retfie, qui utilisent toujours 2 cycles,
Et les instructions de test qui utilisent un cycle si le résultat du test est négatif (faux) et deux cycles s´il est positif (vrai) : decfsz, incfsz, btfsc, btfss
En d´autres termes, une instruction coûte toujours un cycle, plus un cycle supplémentaire s´il y a débranchement dans le programme.
En appliquant ces règles, on voit donc que l´on peut calculer très précisément la durée d´un morceau de programme, et on va utiliser cette possibilité pour calculer nos délais.
B1.1.2. Instruction NOP
L´instruction NOP est une instruction qui ne fait rien (NOP = no operation).
Cela peut paraître idiot, une instruction qui ne fait rien, mais l´important est qu´elle “consomme” un cycle d´horloge. On va donc souvent s´en servir dans la programmation de nos délais.
Cas simple, si vous voulez obtenir un délai de 5 microsecondes entre deux instructions, vous mettez 5 instructions NOP entre les deux instructions.
B1.1.3. ATTENTION : Interruptions
Tous les calculs sur les délais présentés ci-après ne sont valables que si aucune interruption n´est utilisée dans votre programme. Par exemple, dans le programme PicTrain2 on utilise l´interruption Timer0 pour générer un signal de sortie à 240 Hz.
Si par hasard une interruption se déclenche pendant l´exécution de la boucle de délai, la durée de la boucle sera augmentée du temps de traitement de l´interruption.
En bref, si vous voulez obtenir un délai précis et sans surprises, il faut suspendre les interruptions pendant ce temps.

B1.2 Pratique
B1.2.1. Délai de base : 100 micro secondes
La brique de base pour obtenir les délais est un délai de 100 microsecondes, c´est-à-dire de 100 cycles d´horloge :

Remarque : l´expression “$-1” est une facilité d´écriture de MPLAB qui permet, lors d´un débranchement, d´aller à une adresse de programme proche (ici la précédente) sans utiliser d´étiquette.
Remarque : le registre VA_CPT_delai1 doit être défini dans les registres généraux.
Instruc- tion Nb de cycles
     Détail Total
call 2 2 Appel du délai dans le programme principal
movlw 1
11
 
movwf 1 1  
Les deux instructions suivantes sont exécutées 30 fois
decfsz 1 30  
goto 2 60  
  Au 31 passage, il y a débranchement
decfsz 1 2 decfsz consomme 2 cycles (débranchement), mais le goto n´est pas exécuté
nop 1 1 Ne font rien, mais consomment deux cycles supplémentaires pour obtenir 100
nop 1 1
return 2 2  
Entre le call initial et le retour à l´instruction après le call le PIC a exécuté exactement 100 cycles, soit 100 microsecondes.
B1.2.2. Délai d´une milliseconde
Ce délai peut être obtenu de deux manières différentes :
Au moins une milliseconde : on appelle 10 fois de délai de 100 µS, mais avec les instructions de commande, on obtiendra un tout petit peu plus que une  milliseconde,
Exactement une milliseconde.
B1.2.2.1. Au moins 1 mS
Dans ce cas, nous allons réutiliser le sous-programme de 100 µS défini précédemment :


Instruc- tion Nb de cycles
     Détail Total
call 2 2 Appel du délai dans le programme principal
movlw 1
1
 
movwf 1 1  
Les trois instructions suivantes sont exécutées 9 fois
call 100 900  
decfsz 1 9  
goto 2 18  
  Au 10 cycle
call 100 100  
decfsz 2 2 Au 10 passage, decfsz consomme 2 cycles (débranchement), mais le goto n'est pas exécuté
return 2 2  
Le total est de 1035 cycles, soit 1,035 milliseconde, ce qui est en général suffisant comme approximation pour la plupart des applications.
B1.2.2.2. Exactement 1 mS
Notre boucle de base durant 3 µS, il nous faut l´exécuter approximativement 1000/3 = 333 fois. En tenant compte des instructions d´appel et de chargement, on obtient le sous-programme suivant, avec 331 exécutions :

Comme nos compteurs ne vont que jusqu´à 256, il faut faire une première boucle avec 256 passages, puis une deuxième avec 75.
Instruc- tion Nb de cycles
     Détail Total
call 2 2 Appel du délai dans le programme principal
clrf 1
1
 
Les deux instructions suivantes sont exécutées 255 fois
decfsz 1 255  
goto 2 510  
  Au 256 cycle, il y a débranchement
decfsz 1 2 decfz consomme 2 cycles (débranchement) mais le goto n'est pas exécuté
movlw 1 1  
movwf 1 1  
Les deux instructions suivantes sont exécutées 74 fois
decfsz 1 74  
goto 2 148  
Au 75 passage, il y a débranchement
decfsz 1 2 decfz consomme 2 cycles (débtranchement) mais le goto n'est pas exécuté
nop
nop
1
1
1
1
ne font rien mais consomment deux cycles supplémentaires pour obtenir 1000
return 2 2  

B1.2.3. Délai : 1 seconde
Une seconde représentant 1 000 000 cycles, il est nécessaire d´utiliser d´autres tailles de boucle pour “consommer” ce temps. Même une “méga boucle” de 256 x 256 x 3 ne fait que 196 608 cycles, il faudra donc l´exécuter au moins cinq fois.

En appliquant les règles sur chacune des boucles, on obtient les résultats suivants :
boucle 1 : 3 * 256 — 1 = 767
boucle 2 : 256 * boucle 1 + 3 * 256 — 1 = 197 119
boucle 3 : 5 * boucle 2 + 3 * 5 — 1 = 985 609
Avec les autres instructions le total est de 985 617, soit pratiquement 0,98 seconde, ce qui est une très bonne approximation. Si vous voulez obtenir une seconde exactement, il vous faudra ajouter une boucle supplémentaire pour consommer les 15 000 cycles qui manquent (je vous laisse le faire !).
PTI+   HORLOGE ! Ce n´est pas comme cela que l´on construit une montre ! Pour ce faire, il faut générer un signal d´une fréquence donnée, et donc utiliser les timers. Par ailleurs, pour simplifier les calculs ont utilise généralement dans ce cas un oscillateur externe avec un quartz adaptés, par exemple de 3,670016 MHz, qui nous donne exactement 256*256*14*4.
B1.2.4. Délai variable
En partant des sous-programmes ci-dessus, on peut aussi obtenir des délais variables, par exemple de N fois 100 microsecondes.
Si nous déplaçons la chargement initial du compteur dans l´appel du sous-programme comme suit :



le délai résultant (en microsecondes) est de 6 + N * (100 + 3) — 1, par exemple :
N=3 : 315
N=10 : 1 035 (heureusement identique à notre sous-programme précédent)
N=97 : 9 996 soit pratiquement 10 ms
N=194 : 19 987 soit pratiquement 20 ms
On peut donc choisir au moment de l´appel la valeur du délai entre 108 et 26 373 microsecondes (N =1 à N =0, c´est-à-dire 256).
B1.2.5. Durée d´exécution
Dans tout ce que nous avons vu ci-dessus, nous n´avons tenu compte dans nos calculs que du temps d´exécution du sous-programme “délai” lui-même.
Si vous voulez exécuter une action à un intervalle régulier, par exemple allumer/éteindre une led toutes les secondes, il faut ajouter au délai le temps d´exécution du programme d´allumage/extinction lui-même. Dans ce cas, il ne s´agit que de quelques µS, et cela ne change pas fondamentalement le résultat.
Si par contre vous voulez exécuter toutes les millisecondes un morceau de programme qui lui-même prend 500 microsecondes, le total sera de 1 500 microsecondes, soir une durée supérieure de 50 %.
Même si je me répète, pour obtenir une périodicité (fréquence), il ne faut pas utiliser cette méthode, mais utiliser les interruptions.
Les délais sont à réserver vraiment aux “pauses”, c´est-à-dire aux interruptions ponctuelles. C´est pour cela aussi qu´en général on parle de délai “d´au moins NµS”, car seul le minimum est garanti, le délai maximum peut varier.
Nous verrons les utilisations plus concrètes avec d´autres briques de la Briquothèque, comme le traitement des entrées (traitement des rebonds) ou l´écriture en mémoire EEPROM.

B1.3 Mise en œuvre
Tous les sous-programmes ci-dessus ont été regroupés dans un fichier .inc que vous devez intégrer dans votre projet, pour ainsi utiliser directement tous les “briques” contenues.
Pour utiliser ces sous-programmes, il vous faut :
copier ce fichier “SP_delai_V1_1.inc” (disponible ici) dans le répertoire de votre projet MPLAB (voir l´encadré ci-dessous) ;
placer dans la partie “DÉCLARATIONS DE VARIABLES SPÉCIFIQUES” de votre programme les réservations suivantes :
VA_CPT_delai1
VA_CPT_delai2
VA_CPT_delai3
 
placer en fin de votre programme, juste avant la commande END la ligne suivante : #include <SP_delai_V1_1.inc>

Remarque : fonctionnement de la commande #INCLUDE : cette commande permet simplement d´insérer dans votre programme les lignes de code contenues dans le fichier “.inc”, à l´endroit de cette commande. En d´autres termes, c´est comme si vous faisiez un copier/coller du contenu de ce fichier inc.
Cela veut dire aussi que la taille de votre programme se trouve augmentée du nombre d´instructions contenues dans le fichier.
Normalement, on n´indique pas de chemin (répertoire) dans cette commande, MPLAB va d´abord chercher dans le répertoire du projet, ensuite dans le répertoire du programme MPLAB.
Donc pour être plus élégant, au lieu de copier le fichier “SP_delai_V1_1.inc” dans votre répertoire de projet, je vous recommande de le copier dans le répertoire “MPASM Suite” du logiciel MPASM (dans les programmes), il sera à ce moment disponible dans tous vos projets, et vous n´aurez plus à vous en occuper.

Psi

9/11/07
N.B. — Textes, schémas, programmes © Psi pour Ptitrain. Photos Jidé ou D.R. quand signalé. — Toutes vos remarques et commentaires sont bienvenus, et les pages de Ptitrain ne sont pas statiques : les erreurs sont corrigées sitôt connues, les améliorations, éclaircissements, etc. feront l´objet de mises à jour fréquentes.
Ptitrain, l´e-magazine du train éclectique. — Directeur de la publication : Christophe Franchini.
Rédacteur en chef : Jean-Denis Rondinet