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.