Quelques "Trucs" sur MPLAB et leMPLAB0s PIC 16F

1 BANQUES ou BANK
2 Les opérations mathématiques
3 Les interruptions
4 Contrôle profondeur du stack en assemblage conditionnel
5 Variables locales et globales
6 Longueur de buffer ou de table
7 Les résistances élevées pour le convertisseur AD
8 Un mot sur les tables RETLW ?
9 Les horloges
10 Baud Rates et BRGH
11 Autres "trucs" rencontrés

Si vous arrivez directement sur cette page par un moteur de recherche, vous pouvez avoir accès à la table des matières et à chaque article, en page d'accueil.    L'accès se fait par l'un des deux liens en tête de colonne de droite ----->


Préambule

Je suis toujours aussi impressionné par ces petits processeurs, si grands en performances. Que ce soit d'une marque ou d'une autre, mais je préfère rester sur mon choix de MICROCHIP, car cette marque me semble très professionnelle et figure je pense au premier rang des Sociétés pionnières ayant développé cette variété de µ processeurs dits RISC ("Reduced ...." bref... à jeu d'instructions réduit)
Je regrette que cette marque ne figure pas en bonne place dans une revue d'électronique amateur très connue, car le fabricant a réellement fait un bon plan de mettre à disposition les outils de développement pour tout un chacun, et je salue cette idée qui ravi les amateurs, et qui doit aussi favoriser les ventes, je n'en doute pas (et ça n'est qu'un juste retour qui rétribue les développements logiciels).
Les programmateurs de micro-contrôleurs (tous spécifiques ?) jouent également un rôle dissuasif dans la dispersion d'utilisation des marques.
J'apprécie sans compter la disponibilité la qualité et la gratuité de la documentation sur le site de MICROCHIP tout en regrettant un peu la philosophie des noms génériques qui perturbent un peu les recherches, car on ne sait pas très bien parfois, de quoi il s'agit, ni où aller.

Fini en tous cas les produits bas prix mais avec les logiciels de développement incontournables à prix exorbitant. (Je pense aux petits automates du commerce par exemple !)
µ Processeurs ou µ contrôleurs ?

A chacun de faire son choix, mais pour moi ce sont des µ processeurs à part entière.
Bien entendu, avec un tel concentré de technologie, on ne peut pas TOUT faire, mais on peut faire déjà beaucoup de choses.
Savoir utiliser toutes les possibilités de MPLAB est aussi un élément important et pour l'instant je n'ai pas trop cherché à acquérir systématiquement de nouvelles connaissances, mais elles viennent petit à petit...

IL est évident que ce concentré d'électronique numérique se paye tout de même d'une façon ou d'une autre, et qu'il faut non seulement savoir programmer en assembleur, mais aussi s'imprégner de la philosophie de la structure de ces µ, et avoir des notions de temps réel.
Aussi ce petit article destiné à être complété par la suite va énumérer quelques "topics" rencontrés au gré de mes développements.
Il est évident qu'après quelques erreurs soldées par des problèmes de mise au point, toute personne normalement "pro", va essayer de ne pas retomber dans les mêmes erreurs et d'en faire profiter les autres. C'est un peu mon but en donnant quelques pièges dans lesquels je suis tombé.

J'ai oublié de dire l'essentiel qui est que je ne m'intéresse pour l'instant qu'au langage assembleur, car c'est réellement la proximité du hard et du soft qui m'intéresse. C'est d'ailleurs pourquoi la rubrique  est intitulée "électronique et informatique".

1 BANQUES ou BANK

Le sujet est réellement d'actualité, car le choix des banques (BANK) est réellement un choix très important.
Je crois qu'il faut privilégier la BANK0 en priorité pour les variables les plus fréquemment utilisées.
Les zones non banquées 70H à 7FH (etc... et suivant les PIC) devraient contenir les données très fréquemment accédées depuis toutes les pages programme. Je pense en particulier à un mot  presque obligatoire qui est la sauvegarde de W lors des interruptions.
Je pense aussi à beaucoup de mots qui constituent des Flags importants des programmes, et qui sont testés de façon très récurrente.
Enfin dans les systèmes ou l'heure doit être traitée de façon permanente, il est utile de placer les unités qui "bougent" le plus rapidement dans cette zone ((minutes), secondes et fractions de secondes) et particulièrement celles que les séquences de délais utilisent le plus. On notera (voir ci après) une coïncidence intéressante entre TMR0 et des éventuelles variables en BANK2....

Dans un autre chapitre, j'avais suivi les recommandations d'origine pour les changements de BANK en traitant les  deux bits du registre de STATUS. Ceci est une bonne chose mais prend de la place (uniquement dans les PIC ayant plus 2 pages mémoire programme).
Aussi, après mise au point parfaite, je pense qu'il peut être utile de développer des macros qui ne traitent qu'un seul bit à la fois, car on y gagne en place mémoire ainsi qu'en rapidité.
Pour ces opérations de pages et de bank, je préfère des macros, car au moins je me rappelle que c'est pour ce sujet, alors qu'avec un bcf ou bsf, c'est moins évident dans l'esprit et cela pourrait être vu de très haut comme une opération spécifique au programme. Alors une macro d'une seule instruction c'est peut-être ridicule, mais ça se voit ! 

D'une façon générale, je pense que les buffers doivent être séparés des variables et des données isolées, aussi les placer dans les banques de rang élevé me semble très utile, car cela laisse la place aux variables courtes et octets de contrôle dans la BANK0.
De plus les opérations plus compliquées sur les buffers sont beaucoup moins fréquentes que dans la structure du corps de programme, ou se réalisent plus particulièrement en indirection.
On peut donc se permettre d'utiliser à peine  plus de code mais une seule fois (et le plus souvent en interrupt d'ailleurs). C'est là un avantage certain de procéder ainsi, d'autant que les buffers étant traités en indirection il n'y a pas à BANKER (hormis IRP).

Il y a aussi des règles à établir pour faciliter le changement de BANK. En point principal, il faut privilégier les BANK adjacentes ou non, mais dont le passage de l'une à l'autre ne se réalise que par le changement d'un seul bit RPx.
Dans les BANK à rendre "logiquement consécutives" (avec le changement d'un seul bit RPx) il faut sélectionner dans les 4 possibilités suivantes, celles qui font penser au code "GRAY" avec un seul bit qui change :

RP1   RP0
0      0    BANK0
0      1    BANK1
1      0    BANK2
1      1    BANK3

On voit donc que les associations BANK1/BANK2 et BANK0/BANK3  ne conviennent pas du tout, car 2 bits changent en même temps.

Par contre les 4 autres associations sont favorables :
BANK0/BANK1  BANK2/BANK3  BANK0/BANK2  BANK1/BANK3.

Ainsi le passage d'une BANK à la suivante ne nécessitera le changement que d'un seul bit RP donc une seule instruction. On préfèrera de plus les banques adjacentes en N°, soit 0,1 ou 2,3

La création des noms de variables est aussi une méthode utile pour savoir où se trouve ce que l'on manipule. En effet, si l'on a beaucoup de variables dans plusieurs BANK, pourquoi ne pas terminer le nom de la variable par un chiffre (précédé d'un "_" au besoin) indiquant clairement la BANK d'appartenance ?
Je sais qu'il y a des pseudo-instructions crées pour cela, (BANKISEL) mais j'ai du mal de les utiliser !

2 Les opérations mathématiques

Sur 8 bits, on arrive vite à saturation, et beaucoup d'applications nécessitent de pouvoir compter beaucoup plus loin que 256.
Aussi à partir des opérations de base 8 bits, il est possible de compter beaucoup plus haut en entiers.
(Je ne parle que des nombres ENTIERS (non signés) et non des réels avec virgule ou point décimal)

Il y a des exemples d'applications donnés par le constructeur ou par quelques sites dont le très bon site "Doumai". Ces programmes assembleur ne sont pas toujours évidents à comprendre, mais fonctionnent correctement avec quelques restrictions de tester par exemple la non nullité ou de vérifier qu'en soustraction il soit possible de retirer la valeur de l'autre nombre...
Cette dernière clause indique de façon non ambiguë qu'il faut savoir quel est le nombre le plus grand ! C'est donc une opération de comparaison préalable à effectuer, mais je ne l'ai pas trouvée, alors en voici une dont les résultats au niveau STATUS sont identiques aux opérations sur 8 Bits :
   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Comparaison de valeurs 24 bits N comparé à D
; N2 et D2 PF à N0 et D0 Pf valeurs NON SIGNEES
; opération Nx - Dx si carry N >= D sinon N < D
; Variable auxiliaire utilisée K0
; les résultats sont donnés par Carry et Z comme en 8 bits
; C=1 si N >= D,  C=0 si N < D, Z=1 si N = D
;
COMP24      movlw    256-3      ; -3 test du zero sur 3 octets ?
            movwf    K0         ; Manoeuvre (locale)
;
            movfw    D2         ; subtrac W from File ici N-D
            subwf    N2,w       ; N2-D2 si carry, N2>=D2
   
            btfss    STATUS,C   ; test si N2 >= D2
            goto     COMP24_Ret ; C=0 N2 < D2 fin avec carry=0 et Z=0
            btfss    STATUS,Z   ; N2 >= D2 test si =
            goto     COMP24_Ret ; N2 > D2 fin avec carry=1 et Z=0
            incf     K0,f       ; N2=D2 +1/K0 suite test N1-D1
   
;
            movfw    D1         ; N2>=D2 test N1
            subwf    N1,w       ; N1-D1 si carry, N1>=D1
            btfss    STATUS,C
            goto     COMP24_Ret ; C=0 N1 < D1 fin avec carry=0 et Z=0
            btfss    STATUS,Z   ; N1 >= D1 test si =
            goto     COMP24_Ret ; N1 > D1 fin avec carry=1 et Z=0
            incf     K0,f       ; N1=D1 +1/K0 suite test N0-D0
;
            movfw    D0         ; N1>=D1 test N0
            subwf    N0,w       ; N0 - D0 si carry, N0>=D0
            btfsc    STATUS,Z   ; Carry est le résultat final
            incf     K0,f       ; Si tout à ZERO Z sera à 1
                                ; Le carry sera la réalité si on arrive
                                ; à la 3ème soustraction
       
COMP24_Ret  movfw    K0         ; reprise du Z, C inchangé
            RETURN              ; retour avec Carry et Z positionnés (règles 8 bits)


(Vous pouvez recopier cette séquence directement en * .TXT ou *.ASM). (Ne vous trompez pas sur les poids forts et faibles PF =Poids Forts, et Pf=Poids faibles).

Je me suis aperçu que les résultats de soustractions sont utiles, mais pas toujours nécessaires, car il est souvent suffisant de connaître quel est le plus grand de 2 nombres, alors cette comparaison est plus rapide que l'opération de soustraction proprement dite, et de toutes façons elle devra être effectuée préalablement.

Pour l'anecdote voici ce qui m'a conduit à réaliser cette petite séquence sans prétentions. J'avais simplement fait l'erreur de dire qu'en testant le signe du nombre N2 (résultat de poids fort PF), je saurais si il y avait eu report ou non... Eh bien non, c'est une erreur ! Car il peut y avoir report sur N2 (Bit7=1) car les nombres ne sont pas signés et cela n'indique pas pour autant l'aspect négatif du résultat.

3 Les interruptions

Là aussi il m'est arrivé de "me prendre les pieds dans le tapis", car j'avais succombé à la tentation de traiter un problème par une assez grosse séquence déjà existante...je n'avais pas bien mesuré ni la profondeur  des appels de sous programme (call) ni la bêtise de succomber à la facilité, et effectivement suivant les instants du temps réel, il arrive naturellement un plantage dû au stack limité à 8 niveaux.
Alors je crois qu'il n'y a qu'une seule règle à respecter, qui est de ne JAMAIS faire appel à un sous programme dans une routine d'interruption. Ceci est important car pour une interruption, cela vous limite d'autant dans votre profondeur d'appel à des sous-programmes dans tout le reste d'un programme.
Il est possible de déporter un tel problème en positionnant un bit FLAG indiquant la nécessité de faire un traitement dans le programme principal cette fois, et non dans la réponse à interrupt.
Je pense que personne n'aura essayé de faire des "interrupts interruptibles" ? (cela devrait en principe ne pas pardonner)

4 Contrôle profondeur du stack en assemblage conditionnel

C'est un procédé que je n'ai pas encore utilisé mais que je vais essayer ultérieurement. C'est une idée un peu baroque peut-être, mais qui est de comptabiliser tous les appels de sous programmes par CALL durant la mise au point (+1 sur un byte en tête de chaque sous programme).
Le principe me parait simple, on ajoute en début de chaque sous programme une instruction conditionnelle qui est un incrément d'un byte. A chaque RETURN et juste avant, on décrémente ce même byte.
Il est ainsi possible de connaître la profondeur des appels en surveillant ce byte lors du debug du programme. Une fois la mise au point terminée, l'instruction et le mot de comptage sont supprimés par l'assemblage conditionnel. Il serait même possible d'ajouter les interrupts sans aucune complication...

Il n'est pas possible apparemment d'accéder au stack et d'en connaître le niveau, j'ai par contre noté que le stack est un buffer circulaire...

5 Variables locales et globales

Cela fait partie des langages généralement de niveau plus élevé, où l'on distingue les variables selon leur vision possible ou non, depuis le programme principal.
Ainsi une variable GLOBALE est connue de l'ensemble d'un programme et procédures ou fonctions. Contrairement à cela, une variable LOCALE ne sera connue que d'une procédure ou SSP. Cependant il manque en assembleur la possibilité de libérer ou non cette variable suivant le cas.

En effet, une variable non affectée doit être "marquée" comme telle. Dès son affectation réelle elle doit être "utilisée" de façon à ce qu'elle ne serve pas à plusieurs utilisations en même temps.

Alors la méthode assembleur des #define  ou des EQU ne procure que des équivalences de noms mais ne permet aucunement la certitude d'une utilisation exclusive.
Alors je reste très prudent sur ce sujet et il me semble possible et utile d'utiliser cette facilité que pour des sous programmes de dernier niveau (SANS autres CALL imbriqués).
Ainsi on peut seulement être sûr de l'absence d'une autre utilisation parallèle... oubliée.

Dans le même esprit, l'utilisation de variables en réponse aux interrupts et réutilisées dans les programmes est à proscrire, (sauf si c'est une suite logique (adresse et longueur de buffer par exemple) et en exclusivité). Ces utilisations, notamment de variables temporaires, doivent rester spécifiques aux interrupts et ne jamais être réutilisées ailleurs. 

6 Longueur de buffer ou de table

Un tout petit "truc" : Il est souvent utile de ne pas mettre de façon absolue la longueur d'une zone. Aussi il est préférable de laisser l'assembleur le faire à notre place. Ainsi, si la zone change de taille la longueur en question va suivre automatiquement.

TblALT      addwf PCL,f  ; Table scan code
            Retlw 0x0D   ; SCAN TAB
            Retlw 0x9    ;
                      ; ajout d'octets possible
            Retlw 0x5B   ; ¤
            Retlw 0x7F   ;
            Retlw 0x66   ; ARR
            Retlw 0x8    ;
LtblALT     EQU    $-TblALT-1 ; Longueur table
 
Dans cet exemple, "LtblALT" qui représente la longueur va suivre le nombre d'octets réels dans la table. La "addwf" est exclue du compte.
Dans le cas où il n'y a qu'une seule "addwf" pour plusieurs tables, alors la longueur de chaque table individuelle est simplement donnée par $-Etiquette_debut.
La valeur de l'EQU manque et je n'ai pas trouvé d'éléments formels pour simplement vérifier cette valeur.
A ce stade je regrette que le listing assemblé ne donne pas la valeur de ces équivalences qui ne sont élaborées qu'au moment de l'assemblage.

("$" Représente l'adresse courante -sans l'affecter-. Symbole souvent utilisé dans d'autres assembleurs)

7 Les résistances élevées pour le convertisseur AD

J'avais fait un essai de mesure de la tension de batterie de 8.75V avec un pont diviseur de 150 k et 100K, avec une tension permanente et un condensateur de 1µ aux bornes pour que ce soit réellement une moyenne stable. Pour des questions d'économies d'énergie, j'ai été obligé de remplacer (pour des entrées CMOS pas de problème) les résistances ci dessus par 1.5 M et 1 M et toujours le 1µ.
Les circonstances ont fait que cette tension devenant maintenant présente sur une commande explicite, j'ai eu la désagréable surprise de voir que la mesure sur une autre entrée réagissait sur cette dernière et de façon très lente. Je me suis rappelé la documentation MICROCHIP qui donne de façon claire le petit schéma de principe et la résistance équivalente maximum pour une entrée ANA.
En premier lieu j'ai enlevé le condensateur, mais là, la variation n'était plus lente mais assez aléatoire, comme les autres entrées en l'air...
Cette fois j'ai vite compris le problème des résistances bien trop élevées pour le convertisseur AD.
Ceci est bien expliqué par le schéma de principe MICROCHIP et ce n'est que le principe de charge d'un condensateur de mesure qui se trouve ici mis en évidence et qui dirige tout.
A bon entendeur, attention aux impédances en mesures ANA, sous peine de voir des résultats pour le moins surprenants...C'est écrit dans la doc, et là aussi je ne peux que souligner la documentation (PIC16F877 dans ce cas)

8 Un mot sur les tables RETLW ?

Ne pas oublier le PCLATH, mais cela se voit parfaitement bien en debug. Se rappeler surtout que ce n'est plus seulement une question de PAGES mémoire, mais seulement de modules de 256 octets avec frontières précises, et la "pagination" doit alors se faire plus finement en modules de 256 octets. Voir pour cela la très bonne explication en pages  25 ou 26 des data sheets 16F628 16F877 et 16F873.
On aura remarqué que le retour ne nécessite aucune opération particulière puisque l'adresse de retour est en totalité dans le stack suite au CALL.

On notera la possibilité de n'avoir qu'une seule "addwf  PCL,f"  en début de table et de calculer seulement les index de plusieurs messages ou tables par exemple. Un intérêt d'avoir des messages de longueur fixe est particulièrement utile dans cet esprit.
J'ai expérimenté avec un display LCD de 16 caractères et c'est pratique d'utilisation, sans perte de place et  sans stockage d'un N°, mais avec un simple calcul (rotate dans ce cas  !)

     Compléments Décembre 2010

Un dernier point en date de décembre 2010 concerne les tables RETLW qui débutent sur une frontière de PAGE mémoire (et non de module de 256)...Le truc du "addwf PCL,f"  ne fonctionne pas, et c'est normal ! J'ai cherché un peu passionément pour quelle raison, mais c'est évident, car le PCLATH pointant sur la ADDWF n'est plus à jour.
Mon ami Riri m'a confirmé et indiqué une technique qui nécessite quelques instructions avant la frontière, mais qui permet de garder les 256 octets au complet. Personnellement, je préfère m'arranger pour ne pas être à la frontière de pages mémoire. Je n'arrive pas d'ailleurs à retrouver ce morceau de code...Je regrette un peu que Microchip ne l'ai pas développé dans la notice spécifique DS00556.

9 Les horloges

Les horloges sur les PIC 16F628, 873 et 877 sont délicates à définir et parfois à bien utiliser…Je m'explique.

Les vitesses des PIC de cette génération sont le plus souvent à 4 ou 20 MHz, aussi si vous voulez un temps horaire précis en référence, vous serez obligé d'utiliser le PIC à une vitesse qui ne sera certainement pas la plus rapide, mais qui permettra d'avoir des divisions modulo2 voire 256 qui vous permettront d'exploiter les unités de temps que vous avez choisies et en accord avec le prescaler.
Si vous voulez vitesse maxi ET horloge pouvant donner des temps en secondes, multiples et sous multiples, alors il faudra se résoudre à utiliser deux sources distinctes d'horloges.

Quelques applications peuvent se contenter d'un quartz à 32.768 KHz, et dans ce cas on a des performances faibles, mais une économie certaine en énergie et la facilité de calcul du temps.

Pour le temps horaire (temps en secondes et sous multiples), vous êtes partagé entre la nécessité de ne pas perturber trop souvent par les interruptions le processeur et le désir d'avoir le temps le plus fin possible disponible.
Pour la précision vous devrez impérativement obtenir le report sur la valeur 256, générateur d'une interruption. En pré-chargeant TMR0 il est aussi possible d'avoir une interrupt MAIS, l'écriture du TMR0 fait le reset du prescaler ce qui cause des divergences qui peuvent ne pas être négligeables.
C'est donc un choix délicat, et qui dépend de ce que vous voulez faire. (Si vous allumez une LED pour 1 seconde, ce ne sera pas bien dramatique si c'est 1,1 seconde ou 0.9 !)

Pourtant dans les programmes on a souvent besoin de délais courts en µs pour pouvoir mettre les délais sur convertisseurs AD par exemple, et de délais assez longs pour horodater des événements par exemple ou créer des délais comme pour la lecture du DCF77 (délais de l'ordre de 5 à 200 ms).

Prenons l'exemple d' une "horloge temps" générée en externe au PIC, avec quartz spécifique et une période de 19.53125 µs. Cette fréquence sera rentrée sans prescaller sur le Timer0.

Lorsqu'il y a ainsi 2 horloges, la précision donnée par le TIMER0 n'est effective qu'au moment de l'interruption sur passage à 0 du registre TMR0. Ceci est donc précis et représente la seule vraie certitude. Ainsi dans ce cas on aurait 5 ms de résolution.

Si on veut utiliser un processus "qui fait sa vie" de son côté, on ne va pas tomber malheureusement sur une "frontière" précise de 5 ms, mais simplement "n'importe où" entre 2 interrupts.
Attendre 2 interrupts pour obtenir une précision correcte est absolument faux, car on aura alors un temps compris entre 10 et 5 ms. On ne peut donc pas parler de "précision" !...
C'est le cas, car quand vous décidez de mesurer le temps, vous arrivez durant un cycle de 5ms et vous ne savez pas où ! Alors comment faire pour rendre synchrones le processus et l'horloge temps ? (Sur la base de la résolution)
Au moment où l'on lance la mesure, il faut lire TMR0 et mémoriser cette valeur (peu importe la valeur précise). Il faut ensuite  attendre que cette valeur soit disparue à la prochaine impulsion, puis attendre de nouveau cette même valeur initiale. A ce stade ou aura dans l'exemple 5 ms à 1/256 soit une précision de 0.25%, ce qui est "honorable".

Compter directement en instructions machine est-il plus précis ? Oui et Non !
Oui, SI les interruptions ne sont PAS validées. Mais dans la majorité des cas ce ne sera pas possible, donc lorsque l'on compte en instructions machine, les interruptions se déroulent sans que l'on en ait connaissance, et donc notre comptage sera toujours faux et plus petit que le temps réel.
Cette méthode est utilisée pour les très petits délais, mais il faut être certain qu'un temps plus long ne dérange pas le processus, car cela arrivera (Je précise que pour quelques µs j'oublie régulièrement à tort cet aspect...!).

On peut encore ajouter une 3ème solution qui consiste à mémoriser TMR0 au départ, puis compter le nombre entier d'interrupts d'horloge, et enfin lors de l'arrêt de prendre également le temps TMR0. C'est aussi une solution, et lorsque les temps doivent être "empilés", la valeur du timer0 de l'arrêt est aussi la valeur de départ du suivant. Alors il faudra affecter pour l'un des temps, la valeur depuis 0 et pour l'autre, la valeur de résolution moins le précédent ! Cette solution permettra d'utiliser partiellement les interrupts et donc de faire d'autres calculs pendant ce temps.

C'est pas clair ? Vous avez raison, mais au moins vous saurez qu'il faut faire attention et que ce sont des pièges à éviter. Alors ne manquez pas de vérifier véritablement la justesse de vos séquences.
Eventuellement si vous préchargez le Timer0 vous aurez à ajuster par une ou plusieurs instructions la réponse INT du Timer0 pour que globalement sur de nombreuses heures, les écarts soient assez petits.

Voici un exemple de délais précis qui fonctionne et que vous pourrez modifier sans problèmes
il vous suffira de partir avec la valeur de délais dans W et de rentrer en DL2, la valeur par défaut et Dly10ms pour 10 ms.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Délai Précis à 19.53125 µs près pour 5 ms de base
; BANK2 et TMR0 sont compatibles sans changement : chance !
; Dly10ms en direct et autres valeurs en DL2
; Autre appel : valeur de délai fois 5ms en positif dans W et goto DL2
;
Dly10ms   movlw  2    ; 2 fois 5 ms= 10 de base

DL2   
     BANK2
          movwf  Val5p   ; save nb fois 5 ms
   ;
          movfw  TMR0   ; Init save Timer 0 au pas de 19 µs
          movwf  SVPrecis  ; save timer initial au pas de 19 µs

ELA       movfw  SVPrecis  ; reprise valeur initiale
          xorwf  TMR0,w   ; attente premier changement après l'appel
          btfsc  STATUS,Z
          goto   ELA    ; toujours inchangé
 
ELB       movfw  SVPrecis  ; reprise valeur initiale
          subwf  TMR0,w   ; compare
          btfss  STATUS,Z
          goto   ELB    ; pas encore un tour de 256
         ; il y a ici 5ms précises à 19 µs de précision
   
          decfsz VAL5p,f   ; -1 sur nombre de fois 5 ms
          goto   ELA
     BANK0
          RETURN

(Les macros BANKx positionnent les Bits RP du registre STATUS)
(avec SVPrecis et Val5P définis en BANK2)

Encore une petite astuce car TMR0 est également visible en BANK2, alors tout ce qui a trait à ce genre de calculs peut largement être effectué facilement avec des variables en BANK2.

Pour des délais plus petits que la résolution ou différents de la résolution, le calcul est un peu plus compliqué, car il semble nécessaire de compter dans un ou plusieurs octets en partant de zéro et en surveillant l'évolution de TMR0, (qui ne manquera pas de repasser à zéro potentiellement).
Ceci consistera dans l'esprit à incrémenter un octet (ou plusieurs) depuis zéro en prenant la référence de changement de valeur de TMR0 depuis le départ et faire ainsi +1 à chaque changement du TMR0. (Il ne faudra alors rien faire de plus durant la séquence d'interrupt, tout se faisant indépendamment une fois le retour)
(Tout changement se matérialise alors avec un XOR entre la valeur précédente et la valeur actuelle, suivi d'un test du bit Z du STATUS)

          Compléments Décembre 2010

Un dernier point qui concerne autant  le DCF77 que l'horloge elle-même...
Lors de recherche DCF77, on a besoin d'avoir une horloge pour créer des temps compatibles à la recherche des impulsions toutes les secondes, aussi, j'ai laissé l'horloge du timer 0 tourner. Il n'y avait que lors de la mise à l'heure finale que je faisais le point zéro avec les secondes et SOUS multiples.
J'avais cependant oublié une seule chose qui est que les minutes arrivent en tout début des signaux et que l'horloge PIC fonctionnant était capable de créer un report sur les minutes.

Cela était bien réel et tout à fait aléatoire, et j'avais donc parfois une minute de plus très précisément. Pour ne pas bloquer l'horloge qui est nécessaire, j'ai utilisé un bit "inhibit" qui empêche l'incrément des minutes au niveau de la réponse interruptible du TIMER 0.  Ce bit est monté par la séquence DCF77 lorsque l'on fait de la synchronisation de temps. Ce bit est remis à zéro définitivement au retour.

Ce petit encart sur la synchronisation d'horloges est utile aussi avec d'autres procédés, car le bug tout à fait aléatoire n'est pas facile à mettre en évidence et concerne tous les procédés de synchronisation d'horloges EN FONCTIONNEMENT.

10 Baud Rates et BRGH

Le sujet est pourtant simple à priori, mais après être passé du 16F628 au 16F877, j'ai gardé les docs spécifiques  et je savais que le 16F628 pouvait travailler  à la vitesse à 115 200 bps.
Lorsque j'ai regardé de plus près les tableaux de baud rate du 877, j'ai vu qu'il n'y avait pas le 115 200 mais seulement 57 600, avec des pourcentages de déviation assez élevés.

Je n'ai pas repris les formules de calcul, ou plus exactement j'ai du faire "quelques erreurs" en mélangeant les BRGH les tableaux et les lignes des 628 et 877...

Et puis lors des essais sur le datalogger, (publication imminente), je n'ai pas pu aller plus haut que 19 200, car je n'avais pas de valeurs intermédiaires avant 57 600 bps en BRGH=0.
J'ai conclu un peu hâtivement que c'était la doc qui faisait foi. Je n'ai même pas voulu faire les essais avec BRGH=1, car je pensais que les écarts étaient encore trop importants...

Dans la partie BDS du datalogger, il me fallait impérativement pouvoir travailler à une baud rate élevée, alors, après réflexion j'ai pensé que ce n'était pas possible que le constructeur n'ait pas pensé à cela, et qu'il fallait impérativement essayer avec BRGH=1.

J'ai donc revérifié quelques valeurs de baud rate sur la base des formules du 628 et 677...(Ce sont les mêmes) !
Alors les tableaux se valent OUI, mais à la différence près que les "VALUE" ne sont pas identiques, (exemple 57600 avec SPBRG=1 et 20 MHZ :  value 21 en 628 et 20 en 877, et il y en a d'autres...) ce qui explique des différences de pourcentages.

Ceci explique aussi que, voyant des écarts encore plus importants, je n'ai même pas voulu perdre mon temps à faire des essais.
En réalité tout devrait être exact dans les chiffres, aussi bien dans un cas que dans l'autre, tout n'est que question de choix de "value". On remarquera que les tableaux du 628 sont plus complets et mieux détaillés et doivent s'appliquer au 877 sans restrictions puisque les formules de calcul sont identiques.

Alors pour 20 MHZ on se trouve finalement avec les plus faibles erreurs de vitesse (-1.36%) ceci avec BRGH=1, mais Value 21 pour 57 600 et 10 pour 115 200.

Et finalement tout marche à 115 200 théorique avec seulement -1.36% d'écart !
J'en ai également profité pour peaufiner la précision pour 19 200 en passant aussi en BRGH=1. (+0.16% contre 1.72% ou 1.73% suivant le PIC...!)
J'aurais bien essayé encore 250 000 bps mais je n'ai pas la correspondance en face mais seulement 256 000 !...
Mes essais ont été faits sur un ordi de bureau ET sur un Netbook avec câble USB---> RS232 ATEN.

Cette fois, c'est moi qui vous pose une question en relation directe avec ce problème. J'avais refait le calcul suite à ma déception initiale et j'allais chercher un quartz de 20.2752 MHZ...Mais avez vous essayé de monter très légèrement un PIC de 20 MHZ à 20.2752 pour avoir 0% d'écart à 115 200 ?
Un PIC fonctionne-t-il encore correctement pour une fréquence légèrement supérieure ?

11 Autres "trucs" rencontrés

....article à enrichir ultérieurement, par moi-même ou par vous, qui avez eu quelques expériences intéressantes aussi....

PIC et PIC et TRUCS...

____ ( retour début d'article ) ____

____ ( retour accueil bricolsec) ____
____ ( retour accueil lokistagnepas) ____