samedi 24 mars 2012

hello DEL

Avant de passer au projets dont je parlai2 dans le message précédent je vais présenter quelque chose de très simple. Si vous avez déjà fait de la programmation vous connaissez le fameux hello world que tous les débutants en informatique ont probablement programmé un jour. En language C ça donne quelque chose d'aussi simple que:


#include <stdio.h>
void main(){
puts("hello world");
}


Dans le monde de la programmation de MCU l'équivalent de hello world consiste à faire clignoter une DEL1. Le code est un peut plus long mais ça demeure simple.
Voici ce que ça donne en MPASM pour le PIC10F202

explication du code

Les programmeurs C remarqueront certaines similitudes, d'abord la directive #include et les directives #define. Elle sont utilisées de la même façon qu'en C.

__CONFIG

les controlleurs PIC possèdent des registres d'options de configuration qui déterminent certains paramètres de fonctionnement du MCU qui généralement ne sont pas modifiables au moment de l'exécution (runtime). Dans l'exemple ci-haut le watchdog timer (WDT_OFF) , la protection du code (CP_OFF) et le master clear sur GP3 (MCLRE_OFF> sont désactivés.

macros de substitution

Comme en C le pré-processeur substitue le premier symbole par les symboles qui suivent dans le texte source du programme. Par exemple chaque fois que le pré-processeur rencontre le mot DEL dans le code source il le remplace par GPIO, GP2.

macros d'assembleur

MPASM est un macro assembleur, c'est à dire qu'on peut créer des macros avec arguments que l'assembleur va substituer dans le code source dans la première phase de l'assemblage. Il s'agit d'un language de programmation en lui-même qui est beaucoup plus puissant que les macros de substitution du pré-processeur. On peut y définir des variables locales, des conditions d'assemblage et même des boucles de contrôle. Les 2 exemples montrés ici sont des plus simples et pourrait-être définit par des macros de substitution. Les macros d'assemblage permettent en quelque sortent de créer des sous-routines in-line.

les variables

Les variables sont des noms symboliques donnés à des adresses de mémoire qui vont contenir des valeurs qui seront modifiées en cours d'exécution. En MPASM il y a plusieurs façon de définir des variables, la directive cblock est une de ces méthodes. Le chiffre 8 qui apparait après la directive est la première adresse mémoire occupé par ce bloque de variables. Donc la variable compteur se trouve à l'adresse 8. Mais cette variable réserve 3 octets. Elle se termine donc à l'adresse 10. En réalité le symbole compteur sera remplacé par le chiffre 8 par l'assembleur, donc si je veux lire ou écrire dans le deuxième octet de compteur je peux écrire compteur+1 dans le code pour aller à l'adresse 9 (voir sous-routine minuterie à ce sujet).

le segment de code

la directive org indique que les instructions de code qui suivent commence à l'adresse indiquée après la directive. Dans ce cas à l'adresse 0 qui est l'adresse pointé par le compteur ordinal après une réinitialisation du mcu.

le code programme lui-même

Pour faire clignoter une DEL c'est très simple:

  • Allumer la DEL
  • partir la minuterie et attendre qu'elle revienne à zéro
  • Eteindre la DEL
  • partir la minuterie et attendre qu'elle revienne à zéro
  • retourner à la première étape et recommencer
Mais avant d'exécuter cette boucle il faut initialiser certains paramètres de fonctionnement
du MCU. Le PIC10F202 utilise un oscillateur interne de 4Mhz calibré en usine avec une précision de 1%. La valeur de calibration est conservée dans la dernière adresse de l'espace programme sous la forme d'une instruction RETLW xx. Où xx est la valeur de calibration qui sera retournée dans le registre W lors de la réinitialisaton du MCU. Donc si on veut que l'oscillateur fonctionne à la fréquence de calibration d'usine la première instruction à exécuter est movlw OSCCAL qui copie cette valeur dans le registre spécial de calibration de l'oscillateur appellé OSCCAL. Ensuite on utilise l'instruction goto initialisation pour déplacer le compteur ordinal à la première instruction après l'étiquette initialisation. Lorsque cette initialisation est complétée on tombe directement dans la procédure principale. Contrairement au C il n'y a aucune obligation de nommer cette procédure main, je l'ai baptisé ainsi par habitude.

Comme on peut le voir la boucle de la procédure principale mime exactement l'algorithme décris ci-haut. Contrairement au C où il suffit de nommer une fonction pour l'appeller, en assembleur MPASM il faut utiliser l'instruction call suivit du nom de la fonction.

la sous-routine minuterie

Le code le plus complexe de ce petit programme est dans cette routine. Mais c'est en fait assé simple. On inscris la valeur constante DEMI_PERIODE dans la variable compteur ensuite on entre dans une boucle qui incrémente cette variable jusqu'à ce qu'elle retourne à la valeur 0. La complexité viens du fait qu'il y a 3 boucles imbriquées car une variable de 24 bits (3 octets) est utilisée pour obtenir un compte assé long pour un délais de 1/2 seconde. La DEL clignote donc à une fréquence de 1hz. La boucle intérieure est celle qui s'éxécute le plus souvent et qui détermine principalement la durée du délais. Pour calculer la valeur de la constante DEMI_PERIODE, j'ai divisé 0,5 seconde par le temps d'exécution d'une seule itération de la boucle intérieure (celle qui incrémente l'octet faible du compteur). Soit Tcy le temps d'exécution d'un cycle d'instruction qui est de 1usec. la boucle dure 3*Tcy, 1 Tcy pour l'instruction incfsz compteur et 2 Tcy pour l'instruction goto boucle. On obtient donc qu'il faut 166667 itérations de cette boucle pour une durée de 0,5 secondes. Puisque j'incrémente le compteur plutôt que de le décrémenter la valeur réelle utilisée est DEMI_PERIODE=0xFFFFFF-0x28B0B. Les 2 boucles extérieures n'ont pas d'influence significatives sur la durée car elles s'exécutent trop rarement par rapport à la bouche intérieure, l'erreur induite par celles-ci est inférieure à 0,5%.

Pourquoi pas 'Hello World!'

En fait pour créer Hello World! sur un micro-controlleur il suffit d'envoyer le message à l'ordinateur qui va se charger de l'afficher. On envoie le message en protocol UART sur un port sériel (COM) de l'ordinateur. Sur l'ordinateur il suffit d'ouvrir un programme émulateur de terminal et le message va s'imprimer dans la console. Voici le montage à faire sur la platine sans soudure.

GP2 est utilisé comme sortie UART. La DEL est branchée anode au +5Volt de sorte qu'elle n'allume que lorsqu'il y a envoie d'un caractère car autrement GP2 est gardé au niveau 5volt.

hello-led-3.asm

Cette version est réécrite à partir de zéro donc c'est différent de la précédente. On a 2 routines de délais _delais_usec qui est une macro pour les délais courts, moins de 1023 usec. _delais_msec est une macro pour les délais plus longs en millisecondes (jusqu'à 65 535 msec). Cette macro prépare le compteur_delais et appelle la sous-routine delais_msec qui fait le gros du travail. Cette sous-routine utilise la macro _delais_usec pour créer un délais de 1msec de sorte que compteur_delais est décrémenté à toutes les millisecondes jusqu'à zéro.

La sous-routine uart_send est responsable d'envoyer le caractère vers l'ordinateur. Elle utilise les variables carac et compteur_bits ainsi que la macro _delais_usec pour le délais de bit. l'envoie se fait à 9600 BAUD, 8 bits, pas de parité et 1 stop bit. Il n'y a pas de contrôle flux. Sous linux on peut utiliser picocom pour recevoir le message:
exemple: picocom -b 9600 -d 8 -f n /dev/ttyS0
Utilisez le port qui convient à votre installation.
Sous windows 7 l'application terminal peut-être utilisée.

La table message est créée dans dans la mémoire programme avec le contenu: "Hello World!\r\n",0. La procédure main ne fait que lire les caractères dans cette table en l'indexant avec la variable idx_carac et appelle uart_send. Lorsque le zéro terminal est rencontré l'envoie se termine et un saut vers pause est effectué. Après un délais 1 seconde le programme boucle vers main et le message est renvoyé.





notes

1) Diode Électro-Luminescente, abréviation anglophone LED

Aucun commentaire:

Enregistrer un commentaire