vendredi 26 juillet 2013

msp430 launchpad, partie 9

interface avec clavier PS/2

Interfacer un clavier PS/2 avec un MCU est plus simple que d'interfacer un clavier USB et ça demande moins de ressources. Ces dernier jours j'ai donc expérimenter avec un mini-clavier connecté au launchpad. Le programme démo contient 3 parties qui s'exécutent l'une à la suite de l'autre.

  1. allumées les 3 LEDs du clavier en séquence.
  2. Contrôler les LEDs avec les touches dédiées.
  3. Clavier musical, mono tonal.
Les fichiers du projet CCS sont sur https://github.com/Picatout/ps2-kbd-on-msp430g2553

Clavier PS/2

Les claviers PS/2 utilisent une interface qui ressemble au I2C. Il y a 2 lignes, 1 clock et 1 data et les 2 lignes peuvent-être contrôlées soit par le clavier soit par le MCU. La communication est dans les 2 sens, le MCU pouvant envoyer des commandes au clavier. C'est le MCU qui contrôle les LEDs qui sont sur le clavier et non le clavier lui-même. J'ai mis à la fin de cette chronique plusieurs liens donnant l'information que j'ai utilisé pour la création de l'interface PS/2 - MSP430.

C'est le clavier qui fourni le signal clock (environ 12Khz) même lorsque c'est le MCU qui envoie une commande au clavier. Lorsque le MCU veut envoyer une commande il maintien la ligne clock à 0 volt pendant au moins 100µsec et ensuite prends le contrôle de la ligne data et la mais aussi à zéro volt. Il libère ensuite la ligne clock pour que le clavier en reprenne le contrôle. Le MCU change la valeur de la ligne data lorsque la ligne clock est à zéro et attend le prochain cycle pour le bit suivant. Le bit le moins significatif est envoyé en premier. Après les 8 bits un bit de parité impair est envoyé. Ensuite c'est le stop bit. I y a donc 11 bits par octet en incluant le start bit. A chaque octet envoyé au clavier le clavier répond par un octet de confirmation 0xFA ou un octet demandant le renvoie 0xFE s'il n'a pas réussi à lire correctement l'octet reçu.

Le clavier envoie au MCU des scancodes et non des caractères ASCII. Le programme doit donc convertir les codes clavier en caractères. Le caractère correspondant à un code particulier dépend de la configuration du clavier, QWERTY, AZERTY, etc. Le fichier QWERTY.H contient les tables pour la transcription des codes pour un clavier du même nom. Pour utiliser un clavier avec une configuration différente il faut créer un fichier avec les tables appropriées et remplacer les #include "qwerty.h" dans le code source.

Le code source est abondamment commenté, vous devriez donc être en mesure de comprendre en le lisant.

photo du montage

  • P1.0 signal clock interface PS/2
  • P1.1 signal data interface PS/2
  • P2.2 sortie audio pour démo 3

Contrôle des LEDs du clavier

Puisque sur un clavier de PC lorsqu'on presse les touches Ver. Maj., Ver. Num. et défil. la LED correspondante sur le clavier s'allume ou s'éteint ou pourrait-être porté à croire que c'est le clavier qui contrôle l'état des LEDs mais ce n'est pas le cas, c'est le PC hôte qui en est responsable.

Après avoir programmé le MCU avec le code du démo, à la réinitialisation le MCU envoie une commande 0xFF (Remise à zéro) au clavier. Cette commande à pour effet de faire exécuter la routine d'initialisation du clavier. Cette routine s'appelle BAT Basic Assurance Test. Au démarrage du BAT les 3 LEDS s'allument simultanément. Si le test passe avec succès le clavier renvoie au MCU le code 0xAA, s'il échoue il renvoie le code 0xFC. A la fin du test les 3 LEDs s'éteignent. Si le MCU reçoit le code 0xFC il allume la LED2 sur la carte launchpad et entre en mode LPM4. En fait chaque fois qu'il y a une erreur clavier le programme entre en mode LPM4 (arrêt d'exécution).

Si le BAT passe avec succès la LED2 du launcpad allume une demi seconde et ensuite le MCU entre dans une boucle while() à l'intérieur de laquelle il envoie à tout les quart de seconde une commande au clavier avec la fonction set_kbd_leds() qui est utilisée pour contrôler l'État des LEDs du clavier. Les 3 LEDs sont allumées en séquence, une à la fois. Il suffit d'enfoncer n'importe quelle touche pour passer au démo 2.

Fonctionnement des touches à bascules

Ce démo est une démonstration de la façon dont un PC contrôle les LEDs du clavier lorsqu'on utilise les touches à bascule Ver. Maj, Ver. Num. et Défil.. En pesant sur l'une de ces touches la LED correspondante bascule entre allumée/éteinte. Pour sortir de ce démo et passer au clavier musical il faut utiliser la touche ESC.

clavier musical

Le 3ième démo est un mini synthétiseur mono tonal (un seule note à la fois). Sur un clavier QWERTY comme celui-que j'ai utilisé la rangée qui débute avec la lettre A est utiliser pour générer les notes non altérées et la rangée au dessus pour les dièses. Le A est le DO4, le W est le DO#4, le S le RÉ4, le E le RÉ#4 et ainsi de suite jusqu'au P qui est le RÉ#5. Si la touche SHIFT du côté gauche est maintenue enfoncée les notes sont transposées à l'octave supérieur. La touche F1 est utiliser en bascule pour altérer le timbre des notes. Il y a aussi un effet vibrato contrôler par les flèches, Haut et bas pour la fréquence du vibrato, et les flèches gauche et droite pour modifier la profondeur du vibrato. Le vibrato est produit en modifiant directement la fréquence du DCO.

API de l'interface clavier PS/2

  • int init_ps2_kbd() initialisation des E/S et RAZ clavier.
  • void enable_keyboard_rx() active l'interruption sur KBD_CLK.
  • void disable_keyboard_rx() désactive l'interruption sur KBD_CLK. Lorsque cette interruption est désactivée le MCU ne lit plus les codes envoyés par le clavier.
  • int get_scancode() lit le prochain code qui est dans la file d'attente. Si la file est vide retourne 0. En fait plusieurs codes peuvent-être lus car parfois un message comprend plus d'un code. Par exemple lorsqu'une touche est relâchée le message commence par 0xF0 suivit du code de la touche. Le code retourné est une entier négatif s'il s'agit d'un relâchement de la touche et positif s'il s'agit d'une touche enfoncée. Il y a aussi un bit XT_BIT qui indique s'il s'agit d'un code étendu.
  • int get_key(int code) fait la conversion du code clavier au code ASCII correspondant. S'il n'y a pas de code ASCII correspondant retourne 0. Le XT_BIT et le REL_BIT sont conservés tel qu'ils sont dans le code passé en argument.
  • int kbd_send(char cmd) est utilisée pour envoyer une commande au clavier ainsi que les arguments de la commande. Un seul octet est envoyé à la fois. Si la commandes requiert des arguments kbd_send() est invoquée autant de fois que nécessaire.
  • int set_kbd_leds(unsigned int leds_state) est utilisée pour modifier l'état des LEDs du clavier. le bit 0 de leds_state correspond au LED Défil., le bit 1 à Ver. Maj. et le bit 2 à Ver. Num. La valeur 1 dans le bit allume le LED et active la fonction correspondante.

Liens

dimanche 21 juillet 2013

msp430 launchpad, partie 8

Mise à jour 2013-07-27

Continuant mes expérimentations avec la génération d'un signal NTSC j'ai appris1 qu'il est possible de simplifier la synchronisation. Premièrement il n'est pas nécessaire de générer des lignes de demi-longueur (9 au total) 3 pulses larges, 3 pulses étroits et 3 pulses larges pour assurer la synchronisation verticale. Il suffit de générer 3 lignes avec des pulses larges (H_LINE-H_SYNC) à toute les 262 lignes. De plus au lieu de compter 262,5 lignes on peut en compter 262. Ça fonctionne tout aussi bien avec cette simplification. Voici le code de ta1_ccr0_isr simplifié. J'ai aussi modifié les constantes suivantes:
#define FIRST_LINE 30 // première ligne avec affichage
#define LAST_LINE 250 // dernière ligne avec affichage

NOTE 1) référence: Programming 32 bits microcontrollers in C, Exploring PIC32, Lucio Di Jasio, Éd. Newnes.
chapitre 13 page 339.

Génération d'un signal NTSC

Ces derniers jours j'ai expérimenter avec la génération d'un signal vidéo NTSC en noir et blanc sur le launchpad avec le processeur G2553. Le programme démo est entièrement en 'C', pas une seule ligne de code assembleur et ça fonctionne. Générer un signal NTSC sur un petit microcontrôleur est grandement simplifié grâce à la technique du progressive scan et à utilisation judicieuse de 2 périphériques, soit une minuterie en mode PWM pour générer le signal de synchronisation et d'un périphérique SPI pour la sortie du signal vidéo. Quand au circuit électronique il est des plus simple puisqu'il ne nécessite que 2 résistances. Je vais expliquer en détail comment ça fonctionne.

Code source

Standard NTSC

Le standard NTSC est le standard de la télé analogique mis au point dans les années 194x. Au départ c'était un standard pour le noir et blanc mais il a été révisé pour ajouter la couleur quelques années plus tard. Dans notre cas on n'est concerné que par la version noir et blanc. Le site internet le plus intéressant sur la description de ce standard est http://www.ntsc-tv.com. C'est grâce aux informations disponibles sur ce site que j'ai pu me débrouiller pour écrire ce programme.

Le signal vidéo NTSC est plutôt complexe, l'image est créé par 2 balayages (fields) en alternance. Un qui affiche les lignes paires et l'autre les lignes impaires. Chaque field est répété 30 fois par seconde. Chaque field est constitué de 262,5 lignes pour un total de 525 lignes. A cause de la persistance rétinienne on a l'impression que les 525 lignes sont affichées simultanément alors que ce n'est pas le cas. Ce système permet de réduire la bande passante du signal mais ça complique le système de synchronisation de l'image. Heureusement il est possible d'ignorer cette alternance paire/impaire et d'envoyer toujours le même field. On ne dispose alors que de 262 lignes d'affichage qui se répètent 60 fois par seconde. De ces 262 lignes 20 sont réservées pour la synchronisation verticale et donc indisponible pour l'affichage. Il ne reste donc que 240 lignes d'affichage soit la moitié d'un signal VGA.

Le circuit électronique est très simple il suffit d'une résistance de 470 ohm branchée sur P1.2 (sortie PWM) pour la synchronisation et d'une autre résistance de 220 ohm branchée sur la sortie P1.7 (sortie SIMO du SPI) pour le signal vidéo. Il n'y a pas de nuances de gris, c'est vraiment du noir et blanc. Les deux résistances sont jointent ensemble et ce point de jonction est la sortie vidéo qui va à l'entrée NTSC du téléviseur.

Configuration des périphériques

La partie synchronisation du signal est générée par PWM grâce à la minuterie TIMER_A1 configurée en mode comparateur avec comptage UP. TA1CCR0 détermine la période qui correspond à la durée d'une seule ligne vidéo soit 62,5µSec. Le DCO étant programmé pour fonctionner à 16Mhz, la période de cycle du SMCLK est de 62,5nSec, ce qui donne 1000 cycles par ligne. De ces 62,5µSec 10,5µSec sont réservés pour l'impulsion de synchronisation plus les seuils avant et arrière. Il reste donc 52µSec disponible pour l'affichage vidéo. Ce qui complique les choses est la synchronisation verticale. A toute les 262,5 lignes il faut générer une série d'impulsions dont la durée varie. 3 courtes, 3 longues et 3 courtes. Ce travail est accomplie dans la sous-routine d'interruption du compteur de période CCR0, appelée dans le programme ta1_ccr0_isr. le registre CCR1 est configuré pour déterminer la longueur de l'impulsion de synchronisation et CCR2 pour la durée du seuil. lorsque le compteur TA1R arrive à la valeur de CCR2 l'ISR ta1_ccrx_isr est appelée et celle-ci est responsable de l'affichage de l'image qui est contenu dans le tableau frame[56][7]. La résolution de l'image n'est que de 56x56 pixels du à la quantité limitée de mémoire RAM qui n'est que de 512 octets. De ces octets une certaine quantité est réservée pour la pile et les autres variables.

L'astuce intéressante est la façon d'utiliser un périphérique SPI pour sortir les bits vidéos. Le périphérique est configuré en mode SPI maître mais au lieu de brancher la sortie SIMO qui normalement envoie les octets vers un périphérique SPI esclave, on s'en sert comme sortie vidéo. le SPI et cadencé par le signal SMCLK divisé par 13. Ce diviseur et calculé pour que les 56 bits par ligne soient envoyées en un temps inférieur à 52µSec. 52µSec/56/62,5nSec= 14,8. J'ai utilisé 13 comme diviseur pour laissé une marge à la fin de la ligne pour être certain que la routine ISR se termine avant l'interruption de fin de ligne. Plus le diviseur est petit plus les pixels sont comprimés horizontalement. Chaque ligne du tableau frame est répétée 4 fois ce qui donne 4*56=224 lignes ce qui nous laisse aussi une marche verticalement.

A titre de démo je fais simplement défilé un texte au centre de l'écran.

Conclusion

Avec un affichage de 56x56 pixels noir/blanc on ne peut pas faire grand chose mais j'ai une idée sur la façon d'augmenter la mémoire vidéo. Puisqu'on utilise un périphérique SPI pour sortir les bits vidéo il serait simple d'utiliser une mémoire RAM externe à interface SPI comme la Microchip 23K256. Avec 32K de RAM SPI on a de la place pour plusieurs buffer de 5760 octets (192x240pixels). On interface la RAM avec le MCU de manière conventionnel mais on branche la sortie SOMI à l'entrée NTSC à travers une résistance de 220 ohm. Donc il suffit que le MCU fasse la lecture de la RAM SPI pour que les bits soient envoyés sur la sortie vidéo. Avec un tel système on pourrait obtenir une résolution vidéo de 320x240.

quelques liens intéressants sur ce sujet

Une image en FLASH affichée avec une résolution de 192x240.
Standard NTSC.
Exemple du jeux pong fourni par Texas Intruments slaa177.

lundi 15 juillet 2013

msp430 launchpad, partie 7

Contrôle d'un servo-moteur

Sujets traités:

  • Ajustement fin de la fréquence du DCO
  • Utilisation d'une minuterie pour contrôler un servo-moteur

Dans cet article je traite de 2 sujets, le premier consiste à ajuster la fréquence du DCO pour obtenir une fréquence en dehors des calibrations fournis par TI. Le deuxième consiste à utiliser une minuterie pour générer le signal PWM qui contrôle le servo-moteur. Le programme démo consiste à lire le voltage d'un potentiomètre branché sur P1.0 et à faire en sorte que la position du servo-moteur suive la position du potentiomètre. Les impulsions qui contrôle le servo sont sortie sur P2.1.

schéma du circuit

La rotation du servo-moteur suis la rotation de RV1.

Configuration de la minuterie

Les registres spéciaux utilisés par une minuterie de type A sont les suivants:

  • TAxCTL fonctionnement général de la minuterie, choix de l'horloge du diviseur, etc.
  • TAxR c'est le compteur 16 bits utilisé par la minuterie.
  • TAxCCTLy registre de contrôle du mode capture ou comparateur pour le registre CCRy
  • CCRy registre qui contient la valeur de comparaison avec TAxR
  • TAxIV registre identifiant la source de l'interruption puisqu'un seul vecteur partage 3 interruptions.
NOTE: 'x' identifie la minuterie 0 ou 1. 'y' identifie le registre comparateur 0,1,2.

Le programme utilise la minuterie no. 1 (TA1). Comme expliqué précédemment chaque minuterie à 3 registres de comparaisons CCRy. Les étapes de configurations d'une minuterie sont les suivantes:
configuration générale de la minuterie par le registre TA1CTL

  1. Choisir le signal source horloge qui va contrôler la minuterie.
  2. Choisir un diviseur pour ce signal.
  3. Choisir le mode de contrôle de la minuterie:
    1. STOP la minuterie est arrêté.
    2. UP TAxR est incrémenté jusqu'à la valeur de CCR0 et recommence à zéro. CCR0 détermine donc la période=Tclk*CCR0
    3. CONTINUOUS TAxR est incrémenté jusqu'à la valeur maximale 0xFFFF et recommence à zéro. Période fixe de longueur maximale=Tclk*65536.
    4. UP/DOWN TAxR est incrémenté jusqu'à la valeur de CCR0 et ensuite décrémenté vers zéro. Période=Tclk*2*CCR0
  4. Choisir s'il y aura une interruption sur débordement du compteur.

Ensuite pour chacun des registres CCRy il y a un registre à configurer TA1CCTLy
  1. Type d'utilisation capture ou comparateur
  2. Pour le mode comparaison choisir le type de sortie. C'est à dire ce qui arrive lorsque le compteur atteint la valeur qui est dans CCRy
    • Mode 0 output, la valeur de la broche de sortie est déterminée par le bit OUTx du registre TAxCCTLy.
    • Mode 1 set, lorsque TAxR atteint CCRy la sortie passe à 1 et y reste jusqu'à un reset du MCU ou un changement de configuration du mode minuterie.
    • Mode 2, Toggle/Reset, la sortie est inversée lorsque TAxR==CCRy et remise à zéro lorsque TAxR==CCR0 (fin de période).
    • Mode 3, Set/Reset, la sortie est mise à 1 lorsque TAxR==CCRy et remise à zéro lorsque TAxR==CCR0.
    • Mode 4, Toggle, la sortie est inversée à chaque fois que TAxR==CCRy. La période du signal de sortie est donc le double de la période du compteur TAxR.
    • Mode 5, Reset, la sortie est mise à zéro lorsque TAxR==CCRy et y reste jusqu'à réinitialisation du MCU ou reprogrammation de la minuterie.
    • Mode 6, Toggle/Set, la sortie est inversée lorsque TAxR==CCRy et est mise à 1 lorsque TAxR==CCR0, on obtient donc un signal complémentaire à celui du mode 2.
    • Mode 7, Reset/Set, la sortie est mise à zéro lorsque TAxR==CCRy et mise à 1 lorsque TAxR==CCR0, on obtient donc un signal complémentaire du mode 3.
  3. Choisir si on veut déclencher une interruption lorsqu'il y a égalité TAxR==CCRy

Code source

Description

La minuterie TA1 est alimenté par SMCLK et fonctionne en mode UP. La sortie est en mode reset/set ce qui donne un signal inverse de celui utilisé par le servo-moteur. Ceci est nécessaire car le transistor Q1 va réinverser ce signal de sorte que le la ligne de contrôle du servo-moteur aura le bon signal, soit une série d'impulsions d'une durée qui varie de 400µsec à 2400µsec se répétant à toutes les 20millisecondes.

Le périphérique ADC10 est utilisé pour lire la valeur du potentiomètre branché sur P1.0. A chaque période du signal servo, une série de 8 lectures du potentiomètre est faite et la moyenne est utilisée pour contrôler la largeur de l'impulstion PWM du servo.

Quelques calculs simples

Le servo-moteur utilisé (Hitec HS-420) est conçu pour être contrôlé par une série d'impulsions dont la largeur minimale est de 400µsec et la largeur maximal de 2400µsec. La valeur centrale est de 1500µsec. Ces impulsions doivent se répétées à toutes les 20millisecondes. Pour que le PWM puisse utiliser les 16 bits du compteur TA1R il faut que la fréquence qui alimente TA1R soit exactement de 65536*Tclk. Où Tclk est la période du signal SMCLK. On obtient donc que le DCO doit fonctionner à 3,28Mhz. Dans le manuel du MSP430G2553 il y a une table avec les valeurs qui servent à configurer les registres DCOCTL et BCSCTL1 pour obtenir la fréquence désirée. Notez que ces valeurs sont approximatives et ne servent qu'à titre indicatif. A partir de cette table DCO frequency j'ai utilisé les valeurs RSELx=10 (range select), DCOx=2 (step selector) et en utilisant un oscilloscope j'ai mesuré la fréquence de sortie du signal PWM sur P2.1. J'ai ajusté la valeur MODx (modulator) pour obtenir 50Hz.

Pour calculer les valeurs MIN_PULSE, MAX_PULSE et CENTER_PULSE il s'agit d'une simple règle de 3.
N=pulse_width/20000*65536
N valeur à mettre dans TA1CCR1.
pulse_width largeur de l'impulsion en µsec.
20000 période PWM en µsec.

la ligne de code suivante nécessite peut-être une explication:

TA1CCR1=MIN_PULSE+(v_mean>>1)+(v_mean>>2);

v_mean contient la somme des 8 échantillons ADC10. Pour obtenir la valeur moyenne il faut diviser cette valeur par 8. Mais je dois multiplier cette valeur moyenne par 6 pour ajuster correctement la valeur à mettre dans TA1CCR1. donc au lieu de divisé par 8, je divise par 2 et par 4 et j'additionne les 2 valeurs ainsi obtenues1. Le résultat est le même mais j'ai éviter l'utilisation de routines de multiplication et division qui auraient occupées plus d'espace de code et seraient plus lentes d'exécution.
NOTE 1: multiplié par 6 = *4 + *2 et diviser par 8 pour ensuite multiplier par 4 est idem à diviser par 2...

Quant à la valeur 6 il s'agit simplement du rapport entre l'intervalle des lectures ADC10 (1023) et de l'intervalle MAX_PULSE - MIN_PULSE. Donc en prenant la valeur lu par ADC10 puis en la multipliant par 6 et en ajoutant à MIN_PULSE, la largeur des impulsions est maintenue dans les spécifications du servo-moteur.

dimanche 14 juillet 2013

msp430 launchpad, partie 6

Sujets traités:

  • Générateur de nombre aléatoire utilisant l'ADC10
  • Utilisation de fonctions en assembleur dans un projet en 'C'
  • Utilisation du PWM pour contrôler un LED RGB

Générateur de nombres aléatoires

Les générateurs de nombres aléatoires sont très utilisés mais la fonction rand() qu'on retrouve dans la librairie standard 'C' est en fait un PRNG (Pseudo Random Number Generator), c'est à dire une fonction chaotique qui génère une séquence de nombres qui est toujours la même si on démarre avec le même seed. De plus la période d'un tel générateur a une valeur finie, ce qui signifie que la séquence va se répétée identique à elle-même après un certain nombres d'itérations. Un véritable générateur de nombre aléatoire ne peut pas être construit sur un algorithme il doit-être lié à un phénomène réellement aléatoire. Le générateur proposé ici utilise le bruit qui existe dans tout composant électronique pour produire un tel générateur.

Comme mentionné dans un article précédent les msp430 qui sont équipés d'un périphérique ADC10 ont aussi une sonde de température interne. Cette sonde est constituée de 2 diodes en série alimentées par un courant constant. Hors comme tout composant électronique chacune de ces jonctions génère un certain niveau de bruit aléatoire. Comme il y a deux jonctions en série ces bruits s'additionnent. En plus du bruit généré par cette sonde le source qui sert de voltage de référence pour l'ADC a aussi son propre bruit. De plus il faut ajouter le bruit électromagnétique généré par le CPU et de sources externes. Tout ça fait que lorsqu'on fait une lecture avec le convertisseur analogue/numérique les bits les plus faibles fluctuent aléatoirement d'une lecture à l'autre. Lors d'une utilisation normale de l'ADC10 on prend la moyenne de plusieurs lectures pour éliminer ce bruit.

Pour notre générateur aléatoire au contraire on veut se servir de ce bruit pour générer des entiers 16 bits qui auront on l'espère une distribution réellement aléatoire et non répétitive.

Le générateur utilisé dans le démo suivant utilise donc la sonde de température en faisant une série de lectures et en construisant une entier à partir du bit le moins significatif de chaque lecture car c'est celui qui est le plus susceptible de varier aléatoirement puisque son poids ne représente que Vref/1023. Pour un Vref de 1,5 Volt il a donc une valeur de 1,5 millivolt.

Pour obtenir un entier de 16 bits ont fait donc 16 lectures et pour chacune d'elle on ne garde que le bit le plus faible. On accumules les bits dans une variable par décalages successifs.

Le démo utilise ces nombres aléatoires pour changer à intervalle régulier la couleur d'un LED RGB qui est branché sur P2.0, P2.1 et P2.3. Le deuxième timer TA1 est utiliser en mode comparateur pour généner 3 signaux PWM sur ces broches.

Assemleur dans un projet en C

Inspiré par le document slaa338.pdf trouvé sur le site de Texas Instruments, j'ai adapté la version assembleur trouvé dans le code de TI pour l'utiliser dans ce démo. C'est une bonne occasion de voir comment on peut utiliser des routines en assembleur dans un projet en C.

Lorsqu'on ajoute un fichier dont l'extension du nom est .asm dans un projet en C il est reconnu par l'IDE comme un fichier en assembleur.

Conventions à respecter

Pour que ça fonctionne sans problème il faut respecter les conventions de registres et d'appel de sous-routines.

  • Les registres R12-R15 sont réservés pour les arguments et la valeur retournée par la routine. Dans notre exemple la fonction en assembleur adc_rand, n'a qu'un seul argument: le nombre de bits que doit avoir le nombre aléatoire généré. Lorsque la fonction est appellée à partir du programme principal cet argument se retrouve dans R12. S'il y avait un deuxième argument il serait dans R13, le troisième dans R14 et le quatrième dans R15. S'il y en a plus de 4 les suivants sont sur la pile.
  • Si votre fonction en assembleur utilise les registres R4-R10, elle doit les sauvegardés sur la pile avant de les modifiés et les restaurés avant de quitter.
  • La fonction retourne sa valeur dans les registres R12-R15, les même qui servent à recevoir les arguments. Un entier 16 bits est retourné dans R12, un 32 bits dans R12,R13 et un 64 bits dans R12-R15. R12 a le poids le plus faible.
  • Un fichier d'entête 'C' peut-être utilisé en mettant son nom dans dans une directive .cdecls
  • Votre code assembleur se retrouve dans la section .text
  • Les variables et fonctions exportées sont déclarées comme .global et les externes importées de la même façon.
  • Si des variables sont définies dans le fichier en assembleur elle doivent l'être dans une section .data pour les variables qui sont initialisées et une section .bss pour les autres.

Test de différentes options

J'ai écris une version en C de la routine adc_rand et j'ai testé les 2. Au départ ma version en 'C' utilisait une interruption et le mode LPM0 mais j'ai constaté que les nombres générés étaient moins aléatoires que pour adc_rand. J'ai donc modifié la fonction pour qu'elle n'utilise pas les interruptions et le mode LPM0 et le résultat est semblable la version en assembleur. Je pense que le fait de mettre le MCU en mode LPM0 réduit le bruit et que la lecture est donc plus stable d'où la moins bonne distribution des nombres avec cette version. Pour cette application il est bon d'avoir du bruit.

Contrôle d'un LED RGB par PWM

Le msp430G2553 a deux minuteries de 16 bits munies chacune de 3 comparateurs. Ces minuteries peuvent-être utilisées en mode capture ou comparateur. En mode capture il s'agit de compter des impulsions durant intervalle de temps fixe. En mode comparateur un registre est incrémenté et sa valeur comparée à celle des 3 registres comparateurs à chaque cycle d'incrémentation. Lorsque les valeurs sont égales, dépendant du mode de fonctionnement, il y en a 4, un état est altéré. Le résultat de la comparaison peut-être utilisé pour altérer l'état d'une broche de sortie sans intervention logiciel. Mais dans ce démo les broches qui alimentent la LED sont contrôlées en logiciel. La minuterie est programmée pour que lorsque le registre TAR atteint la valeur d'un des 3 registres CCR une interruption est déclenchée. a l'intérieur de cette interruption l'état de la broche de sortie est mise à zéro. Lorsque le registre TAR déborde une autre interruption est déclenchée qui elle sert à débuté un nouveau cycle PWM en mettant les 3 anodes de la LED à 1. La période du cycle PWM est de SMCLK/65536 soit 8000000/65536=122Hz.

Il y a 2 vecteurs d'interruption affectés à chaque minuterie. Le premier vecteur est déclenché lorsque TAR==TAxCCR0. Le deuxième vecteur se déclenche pour débordement de TAR, TAR==TAxCCR1 et TAR==TAxCCR2. Pour ce second vecteur on distingue la source de l'interruption en faisant la lecture du registre TAxIV.

Dans une chronique ultérieure je vais donner d'autres exemples d'utilisation des minuteries.

jeudi 11 juillet 2013

msp430 launchpad, partie 5

Les sujets abordés dans cette chronique sont:

  • Opération du MCU en mode burst.
  • Utilisation du USCI A en mode UART.
  • Sonde de température interne

Contrôle de la consommation électrique

Texas instruments décris la famille des MCU MSP430xxx comme étant de très faible consommation idéal pour l'utilisation dans des appareils à piles. Pour une consommation minimale il faut cependant respecter certains critères:

  1. Conserver le MCU en mode suspendu et ne l'activer qu'au besoin. TI appelle ça le burst mode.
  2. Faire fonctionner les oscillateurs et les périphériques à la plus basse fréquence possible compte tenu des contraintes de l'application.
Le démo utilise ces principes et consiste à utiliser le périphérique ADC10 pour lire la sonde de température interne au MCU et à transmettre le résultat à l'ordinateur qui est relié au launchpad par un port sériel. J'utilise un Pololu 2301a comme adapteur de niveau. L'adapteur est alimenté directement par la carte launchpad par les broches GND et VCC qui sont à adjacentes à S2 sur la carte. Organisation du programme.
  1. À la mise sous tension: initialisation horloge et périphériques.
  2. Envoie du message "OK\n" au PC hôte.
  3. entre dans la boucle du programme principal.
  4. Le MCU est suspendu en attente de réception d'une commande du PC hôte.
  5. Lorsqu'un caractère est reçu du PC hôte. l'ISR uart0_rx_isr() réactive le MCU.
  6. Le programme vérifie si le caractère reçu est 't'. Sinon remet le MCU en mode suspendu et retourne au point 4.
  7. Si reçu 't', sort de la boucle d'attente et lance la lecture de température et remet le MCU en mode suspendu.
  8. Lorsque le périphérique ADC10 a terminé sa lecture l'ISR adc10_isr() extrait la valeur qui est dans ADC10MEM dans les 2 variables b1 et b2 et réactive le MCU.
  9. L'exécution du programme principal se poursuit par la transmission de b1 et b2 au PC hôte par la fonction uart_tx(). Notez que durant la transmission le MCU est encore suspendu jusqu'à que ce la transmission du caractère soit complétée. C'est l'ISR uart_tx_isr qui réactive le MCU.
  10. Le Watchdog timer est configuré en mode minuterie avec délais de 1 seconde et le MCU est encore suspendu jusqu'à l'expiration du délais. Le mcu est réactivé par l'ISR wdt_isr().
  11. Le programme retourne au début de la boucle principale (point 3) pour attendre une nouvelle commande 't' du PC hôte.
Le programme a en fait 2 variantes:
  1. Lorsque #define LP_MODE3 est défini le MCU est conservé en mode basse consommation LPM3. À ce niveau l'activité du MCU est suspendu et le seul oscillateur en fonction est ACLK. Les périphériques qui fonctionnent dans ce mode doivent utiliser ACLK ou avoir leur propre oscillateur comme l'ADC10. La fréquence du DCO est de 1Mhz. Le générateur baudrate du UART est alimenté par ACLK et le baudrate est à 9600.
  2. Lorsque la ligne #define LP_MODE3 est supprimée (ou mise en commentaire), le MCU est suspendu en mode LPM0. À ce niveau l'activité du MCU est suspendu mais le signal SMCLCK est toujours disponible pour les périphérique. Dans cette variante le DCO est configuré à 8Mhz, le générateur baudrate est alimenté par SMCLK et la transmission UART est à 115200 baud. Cette variante consomme plus d'énergie que la première mais permet d'utiliser le UART à vitesse supérieure.

code source

Sonde de température

Les msp430xxx qui ont un convertisseur Analogue/Numérique possèdent une sonde de température intégrée. Une application peut-être conçue pour utiliser cette sonde afin de surveiller la température du chip de silicium et entreprendre une action préventive si cette température atteint un seuil critique. Action qui peut consister à simplement mettre le MCU en mode LPM4. J'ai préparé un deuxième démo (code source à la fin de cette chronique) qui montre comment ça peut-être fait.

Pour utiliser la sonde de température il s'agit simplement de sélectionner le canal d'entrée 10
ADC10CTL1 |= INCH_10;
et à utiliser une référence de voltage interne de 1,5volt
ADC10CTL0 = SREF_1+REFON+ADC10ON;
ou 2,5volt
ADC10CTL0 = SREF_1+REF2_5V+REFON+ADC10ON;

UART

Pour communiquer avec le PC hôte il faut configurer le périphérique USCI (Universal Serial Communication Interface) en mode UART. En fait il y a 2 types de USCI le type A et le type B. Le type A est le plus polyvalent, il peut-être configuré en mode LIN, SPI, UART, IrDA . Le type B ne peut-être configuré qu'en modes I2C ou SPI.

Le démo utilise donc le périphérique UCA0. La partie la plus complexe de la configuration est celle qui concerne le générateur baudrate. Heureusement le guide contient 2 tables 15-4 et 15-5 avec les valeurs nécessaires pour différents BRCLK et différents baudrates standards. Les valeurs utilisées dans ce démo sont extraites de la table 15-4.

Ce démo fait une utilisation très rudimentaire du périphérique, il n'y a aucune gestion d'erreur sur réception ni de vérification du l'état du périphérique. Il faut savoir qu'il est possible de configurer une interruption sur erreur de réception et que le SFR UCA0STAT contient des bits indicateurs d'erreurs et autre conditions.

Le périphérique en tant que tel n'a pas de lignes RTS et CTS si l'application requiert un contrôle de flux matériel celui-ci devra être configuré entièrement en logiciel en réservant 2 E/S pour ces lignes.

Contrôle de la surchauffe

La seule application utile de la sonde de température intégrée au MCU est son utilisation pour contrôler la surchauffe du MCU. Le démo suivant montre comment configurer un tel contrôle. L'application consiste simplement à faire clignoter la LED2 qui est sur la carte. S'il y a surchauffe la LED2 (verte) est éteinte la LED1 (rouge) est allumée et le MCU est placé en mode LPM4. Il devra alors être réinitialisé par le bouton RESET ou une remise sous tension. Pour ce démo j'utilise un seuil d'environ 40°C de sorte q'un séchoir à cheveux peut déclencher la protection.
SEUIL = 1023*Vt/Vref
Vref = 1,5Volt
Vt = 0,00355*T°C + 0,986
Pour 40°C on a SEUIL=770

lundi 8 juillet 2013

msp430 launchpad, partie 4

Dans cette partie j'explore une fonctionnalité particulière du Watchdog timer. Sur la majorité des micro-controleurs que je connais le WDT ne fait que réinitialiser le MCU lorsque son compteur expire. Le WDT+ des msp430 a un mode de fonctionnement supplémentaire qui permet de l'utiliser en mode minuterie qui déclenche une interruption lorsque le compte expire sans réinitialiser le MCU, d'où la signification du + après le WDT. Pour cette expérience le crystal de 32768Hz fournis avec le launchpad doit-être soudé sur la carte car l'expérience consiste à créer une horloge temps réel. Le programme utilise juste un peut plus de 1Ko (1028 octets) et pourrait-être utilisé sans modification sur un msp430g2xxx plus économique que le msp430g2553. Un msp430g2202IN20 ferais aussi bien l'affaire pour moins de la moitié du prix d'un msp430g2553.

Sujets traités:

  • Configuration des condensateurs internes pour l'oscillateur LFXT1.
  • Utilisation du WDT+ comme minuterie.
  • Surveillance de la défectuosité d'un oscillateur.

Comme programme démo j'ai créé une horloge qui affiche les heures et minutes en B.C.D. J'avais déjà beaucoup de LEDs de plantées sur la carte sans soudure suite à mes expériences antérieures, j'ai donc décidé de continuer à les utiliser comme affichage. Comme illustré sur le schéma électronique suivant l'horloge utilise 14 LED, 4 par digit sauf pour les dizaines d'heures qui n'en utilisent que deux.

Horloge à affichage B.C.D.

En gros l'oscillateur interne LFXT1 utilise le crystal 32768Hz branché sur les broches 18 et 19 du MCU. LFXT1 est utilisé comme source pour ACLK qui à son tour cadence le compteur du WDT+. Le WDT+ possède 4 diviseurs: 32768, 8192, 512, 64. Dans cette application j'utilise le diviseur 64 car il faut multiplexer les digits de l'affichage à un fréquence suffisamment élevée pour éviter le scintillement. Avec ce diviseur il y a 512 interruptions par seconde. Le programme est configuré de sorte que la fonction display_time() est appelée à chaque interruption pour afficher un seul digit. La variable q est incrémentée de sorte qu'à l'appel suivant se sera un autre digit qui sera affiché. Le multiplexeur d'affichage fonctionne donc à 512Hz.

En ce qui concerne le crystal installé sur les broches 18 et 19 normalement on devrait aussi installer 2 condensateurs entre Vss et chaque broche pour que l'oscillateur fonctionne. Mais le msp430 a des condensateurs internes. Il y a 4 valeurs de condensateurs: 1pF, 6pF, 10pF et 12,5pF. La sélection de la valeur se fait par les bits XCAPx dans le SFR BCSCTL3. la ligne
BCSCTL3 = XCAP_3;
permet de sélectionner les condensateurs de 12,5pF. Les constantes XCAP_0 à XCAP_3 sont définies dans le fichier d'entête.

La configuration du WDT+ se fait par le SFR WDTCTL et encore une fois le fichier d'entête contient une série de constantes qui simplifient la configuration du WDT+ comme minuterie. Il suffit d'affecter une de ces constantes au registre WDTCTL pour que le minuterie soit correctement configurée:

  • WDT_ADLY_1000 délais de 1seconde. 1 interruption par seconde.
  • WDT_ADLY_250 délais de 250 millisecondes. 4 interruptions par seconde.
  • WDT_ADLY_16 délais de ~16 millisecondes. 64 interruptions par seconde.
  • WDT_ADLY_1_9 délais de ~1,9 millisecondes. 512 interruptions par seconde.
Ces valeurs de délais ne sont valables que si ACLK=32768hz. Donc à intervalle régulier il y aura une interruption sur le vecteur WDT_VECTOR qui sera générée à condition que les interruptions soient activées par _enable_interrupts() et IE1 |= WDTIE; comme c'est le cas dans ce programme.

La documentation des msp430 mentionne que ça peut prendre plusieurs microsecondes avant que l'oscillateur LFTXT1 devienne stable après la réinitialisation du MCU. Il est donc conseillé de contrôler l'état de l'oscillateur en début de programme avant de continuer. Il existe un bit dans le SFR IE1 qui s'appelle OFIE Oscillator Fault Interrupt Enable. Lorsque ce bit est mis à 1, l'indicateur OFIFG passera à 1 si il y a un problème avec un des oscillateurs du MCU. Si les interruptions sont activées l'interruption sera générée sur le vecteur NMI_VECTOR. Dans ce programme OFIE est mis à 1 juste après avoir activé les interruptions. À ce moment il est douteux que l'oscillateur LFXT1 soit stable, donc l'interruption nmi_() sera déclenchée immédiatement. Cette routine d'interruption remet à zéro l'indicateur OFIFG et après un certain délais vérifie son état à nouveau. Le programme va rester dans cette ISR tant que l'oscillateur ne sera pas stable. Étant donné le rôle fondamental que joue cet oscillateur dans le programme il faut attendre qu'il soit stable avant de continuer.

La logique du programme est simple. Une fois que les interruptions et les E/S sont configurées on appelle la fonction set_time() car après une mise sous tension l'heure doit-être ajustée. Le bouton 1 sert à passer d'un digit à l'autre et le bouton 2 à ajuster la valeur du digit sélectionné. On sais qu'un digit est sélectionné parce que son affichage bascule entre la valeur du digit et tous les LED du groupe allumés. Une pression brève sur le bouton 2 incrémente la valeur du digit. Une pression brève sur le bouton 2 passe au digit suivant. Une pression d'au moins une seconde sur le bouton 1 permet de sortir du mode ajustement de l'heure. Lorsqu'en mode normal une pression d'au moins une seconde sur le bouton 1 remet le programme en mode ajustement de l'heure.

La routine de service d'interruption watchdog_timer() en plus d'incrémenter la variable tick fait la lecture des 2 boutons et ajuste les indicateurs booléens associés qui sont maintenu dans la variable btns_flags. La fonction set_time() ne lit pas l'état des boutons, elle ne vérifie que les indicateurs booléens qui sont dans btns_flags.

code source

Prochaine étape, remplacé l'affichage B.C.D. par un affichage LCD 16x2 que j'ai en main.

samedi 6 juillet 2013

msp430 launchpad, partie 3

Une simple modification au programme démo version 2 de la partie 2 de cette série. J'ai remplacé l'affichage binaire par un affichage BCD.

BCD signifie Binary Coded Decimal. Ça consiste à grouper les bits par 4 et à limiter la valeur de chaque groupe à un chiffre entre 0 et 9. Par exemple en binaire le chiffre 52 = 0011 0100 mais en BCD ce serais 0101 0010. Alors qu'en binaire un octet permet de compter de 0 à 255, en BCD on ne peut compter que de 0 à 99.

Le voltage Vdd sur le launchpad est de 3,6 volt. Cette version du programme affiche le voltage à l'entrée P1.3 avec seulement 2 chiffres BCD. Par rapport à la version 2 on perd en résolution mais comme il ne s'agit que d'un démo du principe.

code source voltmètre à affichage BCD:

Le port P1 n'est plus utilisé pour l'affichage, puisque le voltage maximal est inférieur à 4 volt on peut représenté le chiffre le plus significatif avec seulement 2 bits, P2.4-P2.5 et la fraction décimale utilise les bits P2.0-P2.3.

Comme on travaille avec des entiers et qu'on veut conserver la première décimale de la fraction on multiplie la valeur de la lecture par 36 au lieu de 3,6 ensuite on divise par 1023. Donc la variable v contient le voltage multiplié par 10. On divise donc v par 10 pour obtenir le chiffre le plus significatif qu'on conserve dans la variable d1. Pour obtenir la fraction décimale on prend le modulo de 10 de cette même variable v et on conserve le résultat dans d2. On met ensuite ces 2 digits dans la variable P2 en décalant vers la gauche de 4 bits la valeur de d1. Il ne reste plus qu'à assigner P2 à P2OUT pour afficher le résultat.

Pour quelqu'un qui a l'habitude de lire des nombres binaires en groupant les bits par 4, lire un affichage BCD est presque aussi facile que de lire un affichage 7 segments, surtout si on prend soin de laisser un espace entre chaque groupe de 4 bits.

vendredi 5 juillet 2013

msp430 launchpad, partie 2

Ces derniers jours j'ai continuer mon exploration du msp430 launchad en m'intéressant plus particulièrement au convertisseur analogue/numérique 10 bits (ADC10) qu'on retrouve sur le MSP430G2553. Associé à ce convertisseur il y a un canal DMA (Direct Memory Access) qui permet de transférer automatiquement sans intervention logicielle une séquence de conversions A/N dans la mémoire RAM. On peut par exemple programmer le périphérique ADC10 pour qu'il effectue une série de conversions sur le même canal ou sur une séquence de canaux. Le DMA permet même de transférer 2 blocs consécutifs. Le MCU peut donc traiter le contenu de l'un pendant que l'autre se rempli. J'ai écris 2 programmes de démonstration. Le premier montre la façon la plus simple d'utiliser l'ADC10 et le second utilise le DTC, LPM et une interruption. On va voir aussi comment configurer le DCO (Digitally Controlled Oscillator) pour modifier la fréquence du Master Clock (MCLK) ainsi que configurer l'Auxiliary Clock (ACLK) pour l'alimenter par l'oscillateur interne basse fréquence (VLO).

Le Basic Clock Module en bref

Commençons d'abord par présenter le BCS (Basic Clock System). Les msp430 ont 3 signaux d'horloges internes, MCLK (Main Clock), SMCLK (Sub-Main Clock) et ACLK (Auxiliary Clock). Le signal MCLK est utilisé pour cadencer le core du MCU et peut aussi être sélectionné pour alimenter un périphérique. Les signaux SMCLK et ACLK servent seulement pour les périphériques. Ces 3 signaux sont alimentés par divers oscillateurs internes ou externe. Il y a 2 à 4 oscillateurs internes le DCOCLK (Digitally Controlled Oscillator), présent sur tous les MSP430, génère un signal haute fréquence jusqu'à 16Mhz. Le LFXT1CLK utilise un Crystal externe à 32768Khz, il est absent sur les MCU à 8 broches. Le XT2LCK, présent sur les MCU avec plus de 20 broches, utilise un Crystal externe haute fréquence (400Khz à 16Mhz). Le VLOCLK (Very Low power Oscillateur), présent sur tous les MSP430 génère un signal à 12Khz. Normalement LFXT1CLK et utilisé avec un Crystal 32768Khz (horloge temps réel) et XT2CLK pour un oscillateur haute fréquence en remplacement du DCOCLK. Le MSP430G2553 n'a pas de XT2CLK donc soit on utilise un oscillateur externe (400Khz à 16Mhz) dont la sortie est branchée sur XIN (broche 19 sur les DIP20) ou le DCO si on veut fonctionner en haute fréquence.

La table suivante montre comment configurer le BCS (Basic Clock System) pour sélectionner un oscillateur pour chacun des 3 signaux d'horloge.

configuration des signaux horloge
signal/
source
MCLCKSMCLKACLK
VLOCLKBCSCTL2 |= SELM_3;
BCSCTL3 |= LFXT1S1;
BCSCLT3 &= ~LFXT1S0;
BCSCTL2 |= SELS;
BCSCTL3 |= LFXT1S1;
BCSCLT3 &= ~LFXT1S0;
BCSCTL3 |= LFXT1S1;
BCSCLT3 &= ~LFXT1S0;
LFXT1CLKBCSCTL2 |= SELM_3;
BCSCTL3= ~(LFXT1S0+LFXT1S1);
BCSCTL2 |= SELS;
BCSCTL3= ~(LFXT1S0+LFXT1S1);
BCSCTL3= ~(LFXT1S0+LFXT1S1);2
DCOCLCKBCSCTL2 &= ~SELM_3;2BCSCTL2 &= ~SELS;2N/A
XT2CLK1BCSCTL2 |= SELM_2;BCSCTL2 |= SELS;
N/A
osc. externeBCSCTL2 |= SELM_3;
BCSCTL3 |= LFXT1S0+LFXT1S1;
BCSCTL2 |= SELS;
BCSCTL3 |= LFXT1S0+LFXT1S1;
N/A
1 Certains MCU seulement.
2 état par défaut après une mise sous tension ou une réinitialisation (RESET).

La fréquence de la source qui alimente chacun des MCLK, SMCLK et ACLCK peut-être divisée par 1,2,4 ou 8.

configuration des diviseur de fréquence.
signal/
diviseur
MCLCKSMCLKACLK
1 BCSCTL2 &= ~DIVM_3; BCSCTL2 &= ~DIVS_3; BCSCTL1 &=~DIVA_3;
2 BCSCTL2 &= ~DIVM_3;
BCSCTL2 |= DIVM_1;
BCSCTL2 &= ~DIVS_3;
BCSCTL2 |=DIVS_1;
BCSCTL1 &=~DIVA_3;
BCSCTL1 |= DIVA_1;
4 BCSCTL2 &= ~DIVM_3;
BCSCTL2 |= DIVM_2;
BCSCTL2 &= ~DIVS_3;
BCSCTL2 |=DIVS_2;
BCSCTL1 &=~DIVA_3;
BCSCTL1 |= DIVA_2;
8 BCSCTL2 |= DIVM_3; BCSCTL2 |= DIVS_3; BCSCTL1 |= DIVA_3;

Au démarrage c'est le DCO qui alimente MCLK et SMCLK à une fréquence de ~1Mhz. On peut diviser cette fréquence en utilisant les diviseurs mentionnés dans la table ci-haut mais si on veut au contraire augmenter la fréquence il faut modifier la configuration du DCO. Ça se fait en programmant les SFR DCOCTL et BCSCTL1. Le plus simple est d'utiliser les constantes prédéfinies dans le fichier d'entête du MCU (msp430.h). Il y a une constante pour chacun des registres DCOCTL et BCSCTL1 et pour les fréquences suivantes: 1, 8, 12, 16 Mhz. Voir la fonction configure_clock() dans le programme démo qui suis.

Ceci n'est qu'un résumé, pour plus de détails il faut consulter le document le Family user's guide correspondant au MCU choisi. Pour le MSP430G2553 les documents pdf (en anglais) sont ici.

Voltmètre à affichage binaire

Comme le convertisseur a une résolution de 10 bits j'ai simplement utiliser 10 LEDs pour afficher le résultat de la lecture faite sur le potentiomètre RV1.

Je présente 2 versions de ce démo, la première est illustre la méthode la plus simple d'utiliser le convertisseur analogue/numérique et la deuxième utilise le gestionnaire basse consommation (LPM), une interruption et le transfert d'une séquence de lectures vers la mémoire RAM en utilisant le DTC (Data Transfert Control).

première version

Presque toutes les lignes du code source sont commentées. Les étapes sont les suivantes:

  1. Désactivation du WDT
  2. Configuration de la fréquence du DCO à 16Mhz par la fonction configure_clock()
  3. configuration en sortie des bits P1.4 à P1.7 et P2.0 à P2.5
  4. configuration et activation du convertisseur analogue/numérique (ADC10)
  5. On entre dans la boucle infinie while (1)
    1. appel de la fonction read_adc()
    2. extraction des bits de poids fort 6 à 9 de la variable voltage vers la variable P1
    3. extraction des bits de poids faible 0-5 de la variable voltage vers la variable P2
    4. affichage 4 bits forts, envoie de P1 vers P1.4-P1.7
    5. affichage 6 bits faibles, envoie de P2 vers P2.0-P2.5
    6. délais 1/3 de seconde avant de boucler
Dans la fonction read_adc() on utilise l'indicateur ADC10IFG pour savoir si la conversion est terminée. En effet même si les interruptions ne sont pas activées l'indicateur est mis à 1 à la fin de chaque conversion. Donc avant de démarrer la conversion on le met à zéro, on démarre la conversion et on attend que ADCIFG revienne à 1.

voltmètre version 2

Si vous faites le montage et exécutez la version 1 du programme vous allez constater qu'il y a beaucoup de fluctuation sur les 4 bits faible d'une lecture à l'autre. Ceci est du au fait que les voltages de référence utilisé par le convertisseur sont Vdd/Vss. Hors il y a beaucoup de bruit sur l'alimentation causé par le fonctionnement du MCU lui-même. Le MCU possède 2 références de voltage interne. L'Utilisation d'une référence interne (1,5Volt, 2,5Volt) réduirait grandement ce bruit de fond. Il y a une autre façon de réduire ce bruit de fond et c'est celle que j'ai utilisé dans cette version du démo. Le programme fait 8 lectures consécutives et retourne la moyenne. De plus le MCU est arrêté pendant la lecture. Comme c'est le fonctionnement du core qui est en grande partie responsable des fluctuations sur Vdd le fait de l'arrêter pendant la lecture va forcément réduire le bruit de fond.

Attardons-nous pour commencer aux 4 lignes suivantes:
Cette version utilise le transfert de bloc vers la RAM donc il faut activer un mode de conversion en séquence. CONSEQ_2 indique une séquence de lectures sur le même canal, INCH_3 dans ce cas ci. Le bit MSC dans ADC10CTL0 active la multi-conversion automatique. Le bit ADC10TB indique si le DTC va transférer 1 ou 2 bloc de lectures. Dans ce cas ci on met le bit à zéro ce qui signifie 1 bloc. On va utiliser l'interruption sur fin de transfert de bloc donc on active l'indicateur d'interruption global en appelant la fonction _enable_interrupts() et en mettant le bit ADC10IE à 1 dans ADC10CTL0.

Les msp430 supportent les interruptions multi-vecteurs. Pour définir une routine d'interruption on commence par indiquer au compilateur quel est le vecteur associé à l'ISR qui suis. C'est fait avec une directive #pragma vector. La directive est suivit par une fonction de la forme __interrupt void nom_isr(void). Notre ISR est très courte, on commence par remettre à zéro l'indicateur ADC10IFG et ensuite la macro LPM3_EXIT indique qu'à la sortie de l'ISR le MCU doit-être remis en mode actif. Normalement si le MCU est dans un mode LPMx lorsqu'une interruption est déclenchée il retourne dans le même mode LPMx à la sortie de l'ISR qui répond à l'interruption. Si on veut que le MCU demeure actif après la sortie de l'ISR comme dans ce cas si on doit l'indiquer par la macro LPMx_EXIT correspondant au mode LPMx initial. Dans notre cas la fonction read_adc()mais le MCU en LPM3 juste après le déclenchement de la série de lectures, donc l'ISR doit utilisez LPM3_EXIT pour réactiver le MCU.

Comme on peut le voir la fonction read_adc() est plus complexe que celle de la version 1. le SFR ADC10DTC1 indique le nombre de lecture consécutives qui seront effectuées. Huit dans ce cas ci. Les 2 lignes suivantes ne servent qu'à s'assurer que le convertisseur n'est pas déjà occupé. La boucle while (ADC10CTL1 & BUSY); attends simplement la libération du périphérique. Au début de la fonction on a déclaré une variable tableau volatile static unsigned int voltages[8]. C'est dans ce tableau que seront transférés les 8 lectures. Le DCT doit savoir où transférer les lectures, on lui indique en mettant l'adresse du tableau dans le SFR ADC10SA. Au moment où cette adresse est écrite dans ce registre la procédure DCT est enclenchée mais pas la série de conversions qui elle est déclenchée en mettant à 1 les bits ENC (enable conversion) et ADC10SC (start conversion) dans ADC10CTL0;

Maintenant on met le MCU en mode LPM3. Dans ce mode le MCU, MCLK, SMCLK, DCO et le générateur DCO sont inactif ce qui a pour effet de réduire la consommation du MCU à presque rien, moins de 1µA1 pour un Vdd de 3volt. L'ADC10 fonctionne quand même car il utilise son propre oscillateur interne. Notez qu'on peut configurer l'ADC10 pour qu'il utilise MCLK ou SMCLK au lieu de son oscillateur interne mais dans ce cas on ne pourrait pas passé en mode LPM3. C'est l'ISR qui va sortir le MCU du coma par l'exécution de LPM3_EXIT et ce n'est qu'à ce moment que les instructions qui suivent LPM3 dans la fonction read_adc() seront exécutées.

Conclusion

Le convertisseur ADC10 avec le DTC forme un système très sophistiqué dont on a fait qu'effleuré la surface. Il en va de même pour le BCS. Le user's guide pour cette famille de MCU fait plus de 600 pages. On comprendra que je ne reproduirai pas toute cette documentation ici. Comme les fabricants de circuits intégrés traduisent rarement leur documentation en français, il est quasiment essentiel de savoir lire l'anglais. Encore heureux que ce ne soit pas seulement disponible ne chinois.


NOTES:
1) Dans ce démo la consommation de courant viens majoritairement des LED donc le fait de mettre le MCU en mode LPM3 ne réduit pas la consommation significativement mais réduit le bruit sur Vdd. Avec la version 2 du programme, lorsque je mets l'entrée P1.3 à zéro volt la lecture oscille entre 1 et 2 alors qu'avec la version 1 ça oscille entre 1 et 4.

lundi 1 juillet 2013

présentation du msp430 launchpad

Texas instruments produits plusieurs types de micro-contrôleurs, 8, 16, 32 bits. Ils ont leur propres architecture pour certains d'entre eux alors que pour d'autres ils utilisent le core ARM. Les processeurs msp430 sont des processeurs 16 bits développés par TI et le launchpad est une carte d'évaluation économique qui désigne un ensemble de produits dont le msp430 launchpad. J'ai acheté cette carte d'évaluation il y a plus d'un an alors qu'elle se vendait au prix incroyablement bas de 4,67CAN$ chez Digikey.ca, aujourd'hui elle se vend plus de 11,77CAN$. Le kit contient les éléments suivants:

  • La carte d'Évaluation avec embase DIP 20 broches
  • 1 MCU MSP430G2553, avec 16Ko flash/512o RAM
  • 1 MCU MSP430G2452, avec 8Ko flash/256o RAM
  • 1 Crystal 32,768Khz non soudé sur la carte
  • 2 connecteurs 10 contacts femelle non soudé sur la carte
  • 1 câble mini USB-B
  • 1 livret de démarrage rapide

Brève description du core msp430

Le core msp430 est un core 16 bits et son architecture est de type Von Neumann, ce qui signifie que les mémoires programme et donnée sont unifiées contrairement à l'architecture Harvard ou mémoire programme et mémoire donnée sont sur des bus séparés.
Quelques exemples d'architectures Von Neumann:

  • Zilog z80
  • Intel x86
  • TI msp430
Quelques exemples d'architectures Harvard:
  • ARM
  • MIPS
  • Microchip PIC
  • Atmel AVR

Un des avantages de l'architecture Von Neumann est que le code peut aussi bien être exécuté à partir de la mémoire RAM que de la mémoire FLASH. Du code machine peut donc être créé à la volé et copié en RAM pour être exécuter.

Le core msp430 possède une banque de registres (16 registres) d'usage général sur le même principe que les AVR, ARM et MIPS mais il possède beaucoup plus de modes d'adressage (7 modes). Ces nombreux modes d'adressage permettent de diminuer le nombres d'instructions. On a donc un processeur qui fonctionne avec un code plus compacte. Contrairement aux AVR le compteur ordinal (Program Counter) et le registre d'états (Status Register) sont installés dans cette banque de registre. Le PC utilise R0 et le SR utilise R2. Le pointeur de pile (SP) utilise R1. R3 est un générateur de constantes. Il reste donc en fait 12 registres d'usage général.

Installation logicielle

Il existe plusieurs environnements de développement qui supportent les MSP430, IAR, CCS, MSPGCC. Texas instruments fourni code composer studio gratuitement avec une limitation de 16Ko de programme flash. Comme le MSP430G2553 a 16Ko de flash ce n'est pas une limitation pour ce processeur. Pour cette présentation je vais donc utiliser la dernière version de CCS disponible en ce moment. CCS est disponible aussi bien pour les plateformes Windows que Linux. Le fichier à télécharger fait plus de 1Go. TI offre aussi une multitude de documents en format PDF, spécifications, guides d'utilisation, notes applicatives. Une fois installé le logiciel s'ouvre sur la page TI resource explorer.

Cet environnement ressemble à tous les IDE du genre et sa prise en main ne présente aucune difficulté pour un programmeur habitué à ce type d'environnement. Cependant je trouve qu'il n'a pas autant de fonctionnalités que le MPLABX. Entre autre l'éditeur n'offre pas la possibilité de modifier la marge gauche (indentation) d'un bloc de texte sélectionné, fonction pourtant très utile.

Premier projet en 'C'

Pour créer un projet on va dans le menu File - New - CCS project

Il suffit de donner un nom au projet dans le champ project name, choisir le modèle de MCU (device) et le modèle de fichier (project templates and examples) et de cliquer sur le bouton Finish.

la carte doit-être branchée à l'ordinateur avec le câble mini USB-B. l'Alimentation de fait par ce câble. Pour ce premier projet j'ai utilisé les 2 LED montés sur la carte 1 rouge sur la broche P1.0, 1 vert sur la broche P1.6 ainsi que la switch S2 sur P1.3. P1 désigne le port E/S et .x désigne le bit sur le port. Il s'agit simplement de faire clignoter en alternance les LEDs ROUGE et VERT. La vitesse de clignotement est modifiée en pressant S2. La vitesse augmente jusqu'au maximum (délais nul) et ralenti à nouveau.

Discription du programme

Comme dans les autres environnement semblables, TI fournis les fichiers d'entêtes avec les symboles qui permettent d'accéder les registres spéciaux pour programmer les périphériques. C'est la directive #include <msp430g2553.h> qui permet d'intégrer ces définitions au programme. On peut aussi utiliser la directive #include <msp430.h> qui est plus générale et qui fonctionne quel que soit le MCU sélectionné dans la configuration du projet.

La première chose à faire est de désactiver le Watchdog timer pour éviter que le MCU soit continuellement réinitialisé. c'est ce que fait la ligne
WDTCTL = WDTPW + WDTHOLD;

Configuration des ports E/S

Les lignes suivantes servent à configurer les E/S utilisées par l'application. Dans ce programme on utilise seulement le port P1 en mode numérique. Il y a plusieurs SFR (Special Fonction Register) associés à chaque port E/S.

  • PxDIR configure le mode entrée(0) ou sortie(1) de chaque bit du port.
  • PxOUT détermine le niveau de la sortie sur la broche Vdd/Vss. Mais lorsque le bit est en mode entrée et que le bit correspondant dans PxREN est actif ce registre détermine si la résistance d'entrée est en pullup(1) ou pulldown(0).
  • PxIN permet de lire le niveau d'une broche.
  • PxREN permet d'activer/désactiver la résistance d'entrée sur une broche.
  • PxIE permet d'activer/désactiver l'interruption sur changement d'état de la broche.
  • PxIES détermine si l'interruption doit se produire lors d'une transition montante(0) ou descendante(1).
  • PxIFG indique s'il y a eu une condition d'interruption(1) sur cette broche. Le bit doit-être remis à zéro dans l'ISR.
  • PxSEL et PxSEL2 sélectionne quel est le périphérique associé à la broche.
Tous ces registres sont de la largeur du port, si c'est un port 8 bits ils ont 8 bits, si c'est un port 16 bits ils ont 16 bits car chaque bit de ces registres est en correspondance avec le bit du port à la même position.

Le reste du code est la logique de l'application. On a une boucle infinie à l'intérieure de laquelle l'état de P1.0 et P1.6 est inversé à intervalle régulier déterminer par la valeur de la variable dly. A chaque bouclage on fait la lecture de la switch S2 et si la valeur lue demeure à zéro plus de 200msec on modifie la valeur de dly en additionnant la variable delta à dly. Lorsque dly atteint zéro la valeur de delta est inversée de sorte que dly est incrémenté. Le délais initial est de 0,5 seconde mais en gardant le doigt sur S2 cette valeur diminue graduellement jusqu'à zéro puis ré-augmente jusqu'à 0,5 seconde.

__delay_cycles() utilisé dans la fonction delay_ms() est une macro fournie par le système qui crée une boucle qui compte le nombre de cycles CPU passé en argument. Comme après un RESET le MCLK (master clock) fonctionne à 1Mhz, 1000 cycles donne 1msec. Si on configure le DCO (Digitally Controlled Oscillator) qui alimente MCLK pour une autre fréquence il faut ajuster l'argument de __delay_cycles en conséquence. Par exemple si MCLK est à 8Mhz il faut __delay_cycles(8000) pour obtenir 1msec.

Projet en assembleur

Je voulais savoir si la prise en main de l'assembleur était simple. Les msp430 n'ont que 27 instructions machine auxquelles il faut ajouter 24 instructions synthétiques reconnues par l'assembleur. Par instructions synthétiques on entends des macros simples intégré à l'assembleur. Ces macros sont traduites par l'assembleur en une instruction machine.

Pour créer un projet en assembleur on procède exactement comme pour un projet en 'C' sauf qu'on choisi le gabarie (template) empty assembly-only project. Le projet assembleur est très semblable à celui-qui précède sauf que le délais revient à la valeur initiale (rollover) lorsqu'il atteint zéro au lieu de recroître progressivement.

Ce petit programme n'a présenté aucune difficulté particulière. Il n'y aucune variable en RAM, elles sont toutes dans les registres généraux R12,R13,R14. Les mnémoniques sont facile à retenir et il n'y en a que 51. Les instructions arithmétiques et logiques peuvent opérées aussi bien sur des octets que sur des mots 16 bits. On choisi le type d'opérandes par le suffixe .b pour byte (8bits) et .w pour word (16bits). Tous les sauts sont relatifs au compteur ordinal et le déplacement est de 10 bits signé, PC-512 à PC+511. Même les appels de sous-routines (call) sont codées par l'assembleur relativement au PC. Le code est donc totalement relocalisable. Mais il existe aussi un mode d'adressage absolu très utile pour atteindre les registres spéciaux (SFR) qui eux sont toujours à la même adresse.

Conclusion

Je suis très satisfait de cette première expérience avec les msp430. Je n'ai rencontré aucune difficulté majeure. J'ai trouvé toute la documentation dont j'avais besoin et de nombreux exemples. Prise en main facile aussi bien en 'C' qu'en assembleur. De plus le launchpad supporte le débogage matériel. On peut mettre des points d'arrêt dans le code et démarrer la session de débogage. Le code s'exécute directement sur le MCU et on peut examiner les registres, les variables, les SFR, faire du pas à pas. C'est bien plus efficace qu'un simulateur. Avec le support de débogage matériel on sauve beaucoup de temps.

Même si son prix a plus que doublé le msp430 launchpad demeure une carte d'expérimentation très intéressante considérant qu'on a n'a pas besoin d'acheter un programmeur. La carte servant de programmeur on peut enlever le MCU de la carte pour le mettre sur la carte du projet final. En plus on a le débogage matériel. Les adeptes du logiciel libre seront heureux de savoir que les outils msp430gcc supportent toutes les fonctionnalités pour développer sur cette carte et déboguer en matériel.

Les msp430 existent en différent format dont des DIP 14 et 20 broches qui peuvent-être programmés sur cette carte. Il y a aussi des msp430 à 8 broches en format 8SOIC. On peut trouvé des adapteurs pour ce type de boitier, donc on devrait pouvoir les programmer en utilisant la carte launchpad.

Le launchpad n'est pas la seule carte d'expérimentation msp430 vendu par TI. Il y a aussi le ez430-F2013 qui est entièrement contenu dans une clé USB. Le module MCU est séparé du programmeur et peut donc être débranché et utilisé indépendamment de la clé. Les modules MCU se vendent séparément. Il existe aussi une carte d'évaluation en format clé USB sur le même principe mais qui utilise un module radio fréquence EZ430-RF2500 pour les projets Wireless.

Un produit intéressant qui vaut amplement son coût d'achat.