Cette rubrique PicTrain est toute nouvelle, elle a débuté il y a un mois  ! Vous pouvez aisément nous rattraper et sauter en marche !
Aujourd´hui à notre menu :
5.
5.1.
5.2.
5.3.
5.4.

5.5.
 Les sous-programmes
   Premier sous-programme : émission du son 'ti'
   Deuxième sous-programme : émission du son 'ta'
   Troisième sous-programme : l'interruption
   Initialisation des paramètres :
        Bank, Trisio, Wpu, Intcon, Pie...
   Résumé des instructions rencontrées jusqu'ici
5. Les sous-programmes
Pour réaliser tout ce que la page 4 présentait, nous allons donc définir trois sous-programmes :
les sous-programmes
SON_Tii
et
SON_Taa, qui mettent en place les paramètres de chaque son,
le sous-programme
inttimer1, exécuté à chaque interruption (inttimer1 est le nom que j´utilise en standard pour le sous-programme de Timer1 interrupt).

5.1. Premier sous-programme : émission du son ti
;***********************************************************************
; son Tii, 660 Hz
;***********************************************************************
SON_Tii
movlw d'253' ; valeur poids fort du timer
movwf SAV_TMR1H ; sauvegarde
movwf TMR1H ; chargement timer poids fort
movlw d'27' ; valeur poids faible du timer
movwf SAV_TMR1L ; sauvegarde
movwf TMR1L ; chargement timer poids faible
bsf T1CON, TMR1ON ; lancement du timer
movlw d'255' ; chargement compteur durée
movwf CPT_256
boucle_Tii
btfsc T1CON, TMR1ON ; test fin durée (=timer arreté)
goto boucle_Tii ; attente fin durée
  return  
Que fait ce programme  ? Il ne fait que charger les valeurs que nous avons calculées avant pour le son ti dans le Timer1 et aussi dans deux registres généraux que nous utiliserons plus loin, et ensuite il lance le comptage. Enfin, il attend (boucle) que la durée soit écoulée et revient à l´appel du sous programme. Nous allons décortiquer ce sous-programme ligne par ligne :
SON_Tii
Ceci est une étiquette, c'est-à-dire le nom du sous-programme.
movlw d'253' ; valeur poids fort du timer
movwf SAV_TMR1H ; sauvegarde
movwf TMR1H ; chargement timer poids fort
Ici on charge la valeur 253 (poids fort) dans les registres SAVTMR1H et TMR1H.
5.1.1. Pourquoi ? Nous aurons besoin de cette valeur plus loin, on l´écrit donc dans un coin pour pouvoir la retrouver plus tard, et on charge une première fois le Timer1.
Comment
 ? Rappelez-vous qu´on ne peut pas utiliser deux registres dans la même instruction, et qu´il faut donc passer par le registre de travail W :
Par movlw, nous chargeons notre valeur 253 dans le registre de travail ; movlw signifie “Move Literal to W”, c'est-à-dire copie du littéral dans W ; “littéral” est le terme utilisé pour indiquer une valeur dans une ligne de programme.
movwf SAV_TMR1H copie le registre de travail dans le registre SAV_TMR1H
movwf TMR1H copie le registre de travail dans TMR1H ; movwf signifie “Move from W to File”, c'est-à-dire copie du contenu de W dans un registre.
movlw d'27' ; valeur poids faible du timer
movwf SAV_TMR1L ; sauvegarde
movwf TMR1L ; chargement timer poids faible
Ensuite nous faisons exactement la même chose avec la valeur et les registres de poids faibles.
bsf T1CON, TMR1ON ; lancement du timer
Et on lance le comptage Timer1.
5.1.2. Pourquoi ? Jusqu´à présent nous n´avons fait que paramétrer notre timer, mais il est toujours à l´arrêt. Cette instruction lance le comptage.
Comment ? L´instruction bsf (Bit Set in File) met à 1 un bit dans un registre, le bit TMR1ON dans le registre T1CON. Ce registre est le registre de contrôle du Timer1, qui contient différents paramètres qui ne nous intéressent pas ici, sauf le bit TMR1ON qui en quelque sorte l´interrupteur du Timer1 : s´il est à 1, le timer compte, s´il est à zéro, il est arrêté.
Il existe bien entendu l´instruction inverse, bcf (Bit Clear in File), qui met le bit à 0.
Remarque : lorsque l´on manipule un seul bit comme dans l´instruction bsf, l´opérande est toujours “Registre, Bit”. Pour les registres spéciaux, les bits ont en général des noms, comme ici le bit T1CON du registre TMR1ON, cependant on peut aussi écrire T1CON, 0, car TMR1ON est le bit 0 de ce registre.
Retenez donc que pour les registres spéciaux les bits peuvent être appelés par des noms plutôt que par leur position dans le registre, ce n´est qu´une facilité de programmation, le résultat est le même.
Voici, par exemple,le registre INTCON (qui contrôle les interruptions), montrant les huit bits de son contenu : du bit n° 0 (le plus à droite), qui s´appelle GPIF et qui est actuellement à zéro, jusqu´au 7e, GIE qui actuellement vaut 1... Ces noms de baptême ne tombent pas du ciel, ils sont “enseignés” à l´assembleur par l´intermédiaire du fichier P12F675.inc. On pourrait même les changer si on voulait (mais on ne veut pas). [Note de Jidé]
bsf T1CON, TMR1ON ; lancement du timer
movlw d'255' ; chargement compteur durée
movwf CPT_256  
Ici on charge 255 (valeur maximum) dans un compteur (registre général), comme plus haut en chargeant un “littéral” dans W puis en copiant W dans le registre.
C´est ce registre que nous utiliserons pour contrôler la durée du son.
boucle_Tii
btfsc T1CON, TMR1ON ; test fin durée (=timer arrêté)
goto boucle_Tii ; attente fin durée
Enfin, nous avons la boucle d´attente qui tourne en rond jusqu´à la fin du son.
Nous avons déjà vu l´instruction btfsc, qui teste la valeur d´un bit, et qui saute l´instruction suivante si le bit est à zéro ; dans ce cas, on teste l´interrupteur du Timer1, tant qu´il est en fonction (=1) on boucle, sinon on saute (on ignore) le goto.
  return  
Ceci est toujours la dernière instruction d´un sous-programme, elle renvoie l´exécution du programme vers l´appel du sous-programme, c'est-à-dire vers le call correspondant.
Normalement, avec tout ce que nous avons vu jusqu´à présent, vous devez avoir compris le fonctionnement de ce sous-programme.

Résumé des instructions rencontrées jusqu´ici
5.1.3. Remarque : si vous n´avez pas haussé un sourcil dans mon explication ci-dessus, c´est que vous n´avez pas tout à fait regardé dans le détail : la valeur chargée dans le registre TMR1 de poids faible (TMR1L) est de 27 et non de 10 comme nous l´avions calculé.
Ce n´est pas une erreur, il y a une explication bien précise. On verra plus loin qu´à chaque interruption nous allons exécuter un certain nombre d´instructions, notamment pour recharger le compteur avec les valeurs sauvegardées. Entre l´instant où l´interruption de déclenche (le timer vient de passer à zéro) et le moment où on le relance pour un nouveau comptage, il s´est passé un certain temps, qui vaut 17 µS .
Il faut donc diminuer le temps de comptage de cette valeur pour ne pas compter ces 17 µS deux fois (c'est-à-dire augmenter le départ du compteur puisque l´on fonctionne “à l´envers”).
Ces 17 µS ne sont pas évaluées “au pif”, mais calculées en tenant compte du temps d´exécution des instructions entre l´interruption et le redémarrage du compteur, sachant que la plupart des instruction s´exécutent en 1 µS (= un tic d´horloge). Attention, n´essayez pas de calculer vous-mêmes, car une partie des instructions ne vous est pas visible car dans le fichier “caché”.
Cette correction s´applique bien entendu aussi dans le sous-programme ta.

5.2. Deuxième sous-programme : émission du son ta
;***********************************************************************
; son Taa, 370Hz
;***********************************************************************
SON_Taa
movlw d'250'
movwf SAV_TMR1H
movwf TMR1H
movlw d'201'
movwf SAV_TMR1L
movwf TMR1L
bsf T1CON, TMR1ON
movlw d'255'
movwf CPT_256
boucle_Taa
btfsc T1CON, TMR1ON
goto boucle_Taa
return
Je n´insisterai pas, ce sous-programme est identique au précédent, sauf que les valeurs chargées dans les registres sont celles du son ta.

5.3. Troisième sous-programme : l´interruption
À chaque interruption (c'est-à-dire à chaque fois que le Timer1 passe par zéro), un sous-programme spécifique est appelé qui effectue les opérations suivantes :
Arrêt du timer et rechargement des valeurs pour le cycle suivant,
Inversion de la sortie,
Décrément du compteur de durée, test si fin durée (compteur=0)
Sinon, relancer le Timer1 pour une autre période.
;***********************************************************************
; interruption TMR1
; appelée à chaque interruption TMR1
;***********************************************************************
inttimer1
bcf T1CON, TMR1ON ; arret timer
movfw SAV_TMR1H ; chargement timer poids fort
movwf TMR1H
movfw SAV_TMR1L ; chargement timer poids faible
movwf TMR1L
movlw b'000000010' ; inversion sortie
xorwf GPIO,f
decfsz CPT_256,f ; décrément compteur durée
bsf T1CON, TMR1ON ; relancer timer
return
;*******************************************************************
; interruption TMR0
; appelée à chaque interruption TMR0 *
;*******************************************************************
inttimer0
  return  
  END  
On va aussi suivre ce sous-programme ligne par ligne.
inttimer1
Etiquette nom du programme
bcf T1CON, TMR1ON ; arret timer
Arrêt du timer : on met l´interrupteur à zéro (le bit TMR1ON de l´octet T1CON). Ceci est une précaution car on va juste après modifier la valeur du timer, et le modifier alors qu´il continue de tourner risque de lui faire perdre les pédales.
movfw SAV_TMR1H ; chargement timer poids fort
movwf TMR1H
movfw SAV_TMR1L ; chargement timer poids faible
movwf TMR1L
On charge les valeurs sauvegardées dans les sous-programmes précédents dans le Timer1 pour un nouveau cycle.
On a ici une nouvelle instruction movfw (Move File to W), qui est l´inverse de movwf, c'est-à-dire qu´elle charge le contenu du registre dans W.
Je pense que vous savez maintenant qu´il faut procéder ainsi : pour charger une valeur MaValeur dans un registre MonRegistre, il faut d´abord copier MaValeur dans W, puis de W dans ce MonRegistre.
Encore une fois : il n´existe pas d´instruction qui utilise deux registres, il faut toujours passer par l´intermédiaire du registre de travail W.
movlw b'000000010' ; inversion sortie
xorwf GPIO,f
Chargement du littéral (en binaire) 00000010 dans W, et nouvelle instruction xorwf (eXclusive Or W with File), ou exclusif ici entre W et le registre spécial GPIO.
bit 1
bit 2
résultat
0 0 0
0 1 1
1 0 1
1 1 0
  Un “ou exclusif” entre deux bits donne 1 si les deux bits sont 0 - 1 (différents), et 0 si les bits sont 0 - 0 ou 1 - 1 (identiques).
Voir en rubrique la Techno facile, la page consacrée à cette fonction logique méconnue.
Si le bit 1 est à 0, le résultat vaut le bit 2 ; si le bit 1 est à 1 le résultat est l´inverse de bit 2.
xorwf est toujours effectué sur tous les bits du registre concerné, ici GPIO, mais seul le bit 1 (deuxième à partir de la droite) est concerné = il est automatiquement inversé.
D´une manière générale, lorsque vous modifiez le registre GPIO, tous les bits déclarés en sortie (uniquement le bit 1 du haut-parleur dans notre exemple), sont écrits, c'est-à-dire la valeur électrique sur la patte est modifiée.
Le f après GPIO indique que le résultat de l´instruction est rangé dans le même registre.
decfsz CPT_256,f ; décrément compteur durée
bsf T1CON, TMR1ON ; relancer timer
L´instruction decfsz (Decrease File and Skip if Zero) décrémente de 1 le registre, et saute l´instruction suivante si le compteur passe à zéro. Pour nous, ce compteur (chargé à 255 au lancement d´un son) est décrémenté à chaque période.
Lorsqu´il arrive à zéro, le programme saute la remise en route du compteur Timer1, et l´émission du son s´arrête ; s´il n´est pas à zéro, on relance le compteur pour une autre période. Ici aussi, le f indique que la valeur initiale moins un est rangée dans le même registre.
return
Fin du sous-programme.
;*******************************************************************
; interruption TMR0
; appelée à chaque interruption TMR0
;*******************************************************************
inttimer0
return
END
Pour des besoins de construction de mon programme, il est nécessaire d´avoir un sous-programme inttimer0 “bidon”, c'est-à-dire juste avec l´instruction return.
END signale simplement la fin des lignes de programme.

5.4. Initialisation des paramètres
Au début du programme, j´avais volontairement sauté cette partie du code :
;*******************************************************************
; PARAMETRES SPECIFIQUES *
;*******************************************************************
BANK1
clrf TRISIO ; port en sortie
bsf TRISIO,5 ; sauf 5 pour déclenchement
bsf WPU,5 ; résistance de rappel sur le port 5
; registre controle interruptions
clrf INTCON
bsf INTCON,GIE ; interruptions activées en permanence
bsf INTCON,PEIE ; autorise interruption périphériques
; (notamment Timer1)
; registre interruptions périphériques
clrf PIE1
bsf PIE1,TMR1IE ; ; autorise les interruptions Timer1
BANK0
bcf GPIO,1 ; sortie à 0
On va maintenant également la passer en revue ligne par ligne, cela devrait être facile après avoir compris le reste du programme.
Rappel : ce bout de programme n´est exécuté qu´une seule fois, au début du programme, avant d´entrer dans la boucle principale.
BANK1
5.4.1. Pour des raisons physiques, les registres spéciaux du microP sont rangés dans deux endroits différents nommés BANK0 et BANK1. Le déroulement normal du programme se fait en général en BANK0, mais pour certains registres particuliers comme ceux que nous allons initialiser ici, il faut passer en BANK1. Ces deux instructions permettent de naviguer entre les deux parties.
clrf TRISIO ; port en sortie
bsf TRISIO,5 ; sauf 5 pour déclenchement
5.4.2. Le registre TRISIO est le registre qui permet de définir le sens des entrées/sorties, les bits 0 à 5 correspondent aux E/S 0 à 5. Un bit à 1 indique une E/S configurée en entrée, un bit à zéro une E/S en sortie. En général, il vaut mieux mettre les E/S non utilisées en sortie, pour ne pas récupérer de parasites.
Dans notre cas, seule l´E/S 5 est en entrée (c'est le bouton poussoir), les autres en sortie.
clrf est une nouvelle instruction (Clear File), qui met à zéro les huit bits du registre.
bsf, déjà vu plus haut, met à 1 juste le bit 5 du registre.
clrf WPU ; pas de résistances de rappel sauf
bsf WPU,5 ; résistance de rappel sur le port 5

5.4.3. WPU : Il est possible de mettre en action dans le microP des résistances de rappel (ou de tirage, ou pullup) sur les E/S R de l´ordre de 20 kΩ) [comme dans la réalité, voir “Résistances de tirage” dans la FAQ du Meccano], et cela permet d´éviter d'utiliser des composants physiques hors du montage.
C´est le registre WPU qui commande la mise en service par ses bits 0, 1, 3, 4 et 5 : bit à un (set) = R en service, bit à zéro (clear) = R hors service. Dans notre cas, seule l´E/S 5 est utilisée. Voici le schéma général du microP et de son registre WPU. La patte GP3 n'en a pas, elle est un peu “spéciale”.

; registre contrôle interruptions
clrf INTCON
bsf INTCON,GIE ; interruptions activées en permanence
bsf INTCON,PEIE ; autorise interruption périphériques
; (notamment Timer1)
5.4.4. Le registre INTCON (oui, le “tiroir” de tout à l´heure !) commande toutes les interruptions existantes dans le microP.
Dans notre cas, nous mettons à 1 (on, set) deux bits :
le bit GIE, qui autorise les interruptions d´une manière générale,
le bit PEIE, qui autorise une catégorie particulière d´interruptions appelés “interruptions périphériques”, et dont le Timer1 fait partie.
; registre interruptions périphériques
clrf PIE1
bsf PIE1,TMR1IE ; autorise les interruptions Timer1
5.4.5. Le registre PIE est le registre spécifique aux interruptions périphériques. Dans notre cas nous mettons à 1 le bit correspondant à Timer1 (TMR1IE = Timer1 Interrupt Enabled).
Attention, tous ces paramétrages ne sont que des paramétrages, c´est-à-dire que l´on ne fait que préparer le microP à l´utilisation ultérieure du timer, il ne se passe rien pour l´instant.
BANK0
5.4.6. À la fin de ces paramétrages, il ne faut pas oublier de repasser en BANK0 pour le déroulement du programme.
bcf GPIO,1 ; sortie à 0
À titre de précaution, on met la sortie haut-parleur à zéro. D´une manière générale, il est préférable de mettre les sorties à zéro (c'est-à-dire à la masse) au début du programme, car les sorties peuvent être dans une position “ indéterminée” lorsque l´on met le courant.

5.5. Résumé des instructions rencontrées jusqu´ici
Sur les 35 instructions de notre microprocesseur, nous n´en avons finalement utilisé que 13 pour ce klaxon.

Psi

15/12/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