TP microcontrˆ oleur 16 bits : MSP430
J.-M Friedt, 6 octobre 2008 Objectif de ce TP :
– pr´esentation GNU/Linux et commandes unix
– faire clignoter des diodes, exemples en C et assembleur – communication RS232, communication de donn´ees en ASCII – lecture avec un baud rate et communication avec un autre – communication SPI, acc`es `a la carte SD
1 GNU/Linux
L’arborescence de GNU/Linux et comment s’y retrouver : dans l’ordre de priorit´e : – /est la racine de l’arborescence. Le s´eparateur entre r´epertoire sera toujours le/.
– /homecontient les r´epertoires des utilisateurs. Unix, et Linux en particulier, impose un confinement strict des fichiers de chaque utilisateur dans son r´epertoire. Seul l’administrateur (root) peut acc´eder aux fichiers du syst`eme en ´ecriture : personne ne travaille jamais comme administrateur.
– /usrcontient les programmes locaux `a un ordinateur : applications, entˆetes de compilation (/usr/include), sources du syst`eme (/usr/src)
– /libcontient les librairies du syst`eme d’exploitation
– /proccontient des pseudo fichiers fournissant des informations sur la configuration et l’´etat du mat´eriel et du syst`eme d’exploitation. Par exemple,cat /proc/cpuinfo.
– /etc contient les fichiers de configuration du syst`eme ou la configuration par d´efaut des logiciels utilis´es par les utilisateurs en l’absence de fichier de configuration locale (par exemplecat /etc/profile).
– /bin contient les outils du syst`eme accessibles aux utilisateurs et /sbin contient les outils du syst`emes qu’a prioriseul l’administrateur a `a utiliser.
Les commandes de base :
– ls: list, afficher le contenu d’un r´epertoire
– mv: move, d´eplacer ou renommer un fichier.mv source dest
– cd: change directory, se d´eplacer dans l’arborescence. Pour remonter dans l’aroborescence : cd ..
– rm: remove, effacer un fichier
– cp: copy, copier un fichier.cp source destAfin de copier un r´epertoire et son contenu, cp -r repertoire destination.
– cat: afficher le contenu d’un fichier.cat fichier
– man, manuel, est la commnde la plus importante sous Unix, qui donne la liste des options et le mode d’emploi de chaque commande.
Afin de remonter dans l’historique, utiliser SHIFT+PgUp. Toute commande est lanc´ee en tˆache de fond lorsqu’elle est termin´ee par&.
L’interface graphique est une surcouche qui ralentit le syst`eme d’exploitation et occupe des ressources, mais peut parfois faciliter la vie en proposant de travailler dans plusieurs fenˆetres simultan´ement. Elle se lance au moyen destartx.
Parmi les outils mis `a disposition, un ´editeur de texte (scite), un clone opensource de matlab nomm´eoctave, un outil pour tracer des courbes (gnuplot), affichage des fichiers au format PDF par xpdf, un gestionnaire de fichiers (pcmanfm).
La liste des modules noyaux (´equivalent des drivers, charg´es de r´ealiser l’interface logicielle entre le noyau Linux et l’espace utilisateur) s’obtient par/sbin/lsmod.
2 Premier pas avec gcc
Dans un ´editeur de texte (scite), tapper le programme suivant :
#include <stdio.h>
#include <stdlib.h>
int main() {printf("hello world %d\n",getpid());}
Nous n’utiliserons pas d’environnement int´egr´e de d´eveloppement (IDE) : un ´editeur de texte sert `a entrer son programme, et une fenˆetre de commande (xtermou Termial) servira `a la compilation.
Une fois le programme tapp´e, sauver dans un fichierexemple1.c et compiler dans un terminal en entrant la ligne de commande : gcc -o exemple1 exemple1.c (compiler le programme C exemple1.c r´esultant en l’ex´ecutableexemple1).
Exercice :ex´ecuter le programme par./exemple1.
3 Premier pas sur MSP430
L’exemple pr´ec´edent ´etait une compilation de programme : l’architecture sur laquelle le programme sera ex´ecut´e est la mˆeme que celle sur laquelle le progamme est compil´e (ici, Intel x86).
Nous allons d´esormais compiler des programmes `a destination du microcontˆoleur : il s’agit decross-compilation.
Vservo
"JTAG"
USB DVCC1P6.3/A32P6.4/A43P6.5/A54P6.6/A65P6.7/A76VREF+7XIN8XOUT/TCLK9VEREF+10VEREF-11P1.0/TACLK12P1.1/TA013P1.2/TA114P1.3/TA215P1.4/SMCLK16
P1.5/TA0 17
P1.6/TA1 18
P1.7/TA2 19
P2.0/ACLK 20
P2.1/TAINCLK 21
P2.2/CAOUT/TA0 22
P2.3/CA0/TA1 23
P2.4/CA1/TA2 24
P2.5/ROSC 25
P2.6/ADC12CLK 26
P2.7/TA0 27
P3.0/STE0 28
P3.1/SIMO0 29
P3.2/SOMI0 30
P3.3/UCLK 31
P3.4/UTXD0 32
P3.5/URXD033 P3.6/UTXD134 P3.7/URXD135 P4.0/TB036 P4.1/TB137 P4.2/TB238 P4.3/TB339 P4.4/TB440 P4.5/TB541 P4.6/TB642 P4.7/TBCLK43 P5.0/STE144 P5.1/SIMO145 P5.2/SOMI146 P5.3/UCLK147 P5.4/MCLK48 P5.5/SMCLKP5.6/ALK 4950 P5.7/TBOUTH!RST/NMIXT2OUTTDO/TDIP6.2/A2P6.0/A0P6.1/A1XT2INAVSSDVSSAVCCTMSTCKTDI 5152535455565758596061626364
12 34 56 78
JP4
R8
C9
C1
C10
C13 C14 R14
C35
C36
C11C12
A1B2 C13
IC3AA8B9 C6
IC3C
7 14VDD IC3PVSS
LED5LED6
D3
3V3OUT 17
USBDM 16
USBDP 15
_RESET 19
27 OSCI
28 OSCO
GND17 GND218 GND321 TXD 1 RXD 5 _RTS 3 _CTS 11 _DTR 2 _DSR 9 _DCD 10
_RI 6
CBUS0 23 CBUS1 22 CBUS2 13 CBUS3 14 CBUS4 12
VCCIO4
AGND25
26 TEST
VCC20
IC5 CS 1
DI 2
GND 3
VCC 4 SCK 5 GND1 6 DO 7 IRQ 8
WP WP
CD CD
SW SW
SHIELD 14
D+
D- VBUS GND
R1
21
Q1
Q3
R4
R5
1 2
JP1 1 2
JP2
GATE1 2 GATE2 7
GND3
4 IN1 5 IN2
VCC6 IC2
Q2 D
S GQ4
D S G
SHDN/
5
2 LBI
LBO/ 3 4 REF
FB1
LX 7 OUT 8
GND6
IC4
C2
L1R2
1 2 3 JP3
1 2 3 JP5
1 2
JP7
1 2
JP6 1
2 3 4 5
MPS_RST
VDD
VDD VDD
GND
GND
GND TDI TCLKTCK TDOTMS
ONOFF1
ONOFF2
VSERIE VSERIE
3.3
SHOOT2 SHOOT1
CSVCC_SDSCK MOSI
MISO
4.0MHz xtal 3k
VDD
100n 1n
GND
GND
GND
GND
1n
GND
VDD
10k
VDD
100nf
>22uF
GND
GND
4066D
4066D
VDD GND 4k7
2k
2k LTC1157
GND 2N3819
2N3819
MAX1674
GND GND GND
Le MSP4301poss`ede 16 registres dont 4 sont occup´es pour des fonctions sp´ecifiques (notamment R1=stack pointer: il reste donc 12 registres disponibles pour notre programme, R4-R15).
XOR 0 0 1
1
1 0
1 0
#include <signal.h>
#include <io.h>
#define SEC R13
.global main main:
RESET: MOV.W #0x280,R1
MOV.w #WDTPW+WDTHOLD,&WDTCTL ; Stop Watchdog Timer MOV #0x0,&P1OUT
MOV #0x0,&P1SEL ; p.9-4: P1_1 is TA, NOT GPIO MOV #0xFF,&P1DIR
MOV #0,&P1IES ; jmf
MOV #0,&P1IE ; jmf
Mainloop:
xor.b #0xff,&P1OUT call #delai jmp Mainloop delai:
mov.w #0x4fff,R7 attend: dec R7
jnz attend ret
1focus.ti.com/lit/ds/symlink/msp430f149.pdfethttp://focus.ti.com/lit/ug/slau049f/slau049f.pdf
Compilation du programme :
msp430-gcc -Os -mmcu=msp430x149 -D_GNU_ASSEMBLER_ -o sortie.elf entree.S pour g´en´erer le listing correspondant :
msp430-objdump -dSt led.elf > led.lst
et la liste d’instructions au format hexad´ecimal Intel `a partir du fichier ELF : msp430-objcopy -O ihex led.elf led.hex
Rappel :gcc -Sarrˆete la compilation `a la g´en´eration de l’assembleur (fichier .s),gcc -oarrˆete la compilation
`
a la g´en´eration des objets (avant le linker).
Chaque port d’entr´ee sortie n´ecessite de d´efinir :
– la fonction de chaque broche avec le registre PxSEL : 0 pour du GPIO, 1 pour la fonction sp´ecifique – la direction du port dans PxDIR : 0 pour une entr´ee, 1 pour une sortie
– le niveau de la broche, 1 pour un niveau haut (3,3 V) et 0 pour le niveau bas (masse)
Exercices
1. o`u se trouve la pile dans cet exemple ?
2. combien de donn´ees pouvons nous placer au maximum sur la pile ?
3. proposer une alternative `a l’op´erateur XOR pour atteindre le mˆeme r´esultat.
Les ports du MSP430 sont capables d’absorber ou fournir la vingtaine de milliamp`eres n´ecessaires `a alimenter une LED.
Cet exemple exploite la fonction XOR pour p´eriodiquement changer l’´etat du port et donc allumer ou
´
eteindre la diode qui y est connect´ee.
Le mˆeme r´esultat s’obtient en C par :
#include <msp430x14x.h>
#include <io.h>
main (void) {volatile int i;
P1OUT=0x00;
P1SEL=0x00;
P1DIR=0xFF;
P1IES=0;
P1IE=0;
while (1) {
P1OUT^=0xFF;
for (i=0x3FFF;i>0;i--) {}
} }
Compilation du programme :
msp430-gcc -Os -mmcu=msp430x149 -o led led.c Exercices :
– visualiser le code assembleur g´en´er´e
– comment est initialis´e le pointeur de pile ? commenter ce choix.
– comparer l’efficacit´e du code C par rapport au code assembleur, `a fonctionnalit´es ´egales, propos´e plus haut.
4 Communiquer par RS232
Initialisation d’un port s´erie : pour le port 0,
SetupUART0: mov.b #SWRST,&UCTL0 ; cf p.13-4
bis.b #CHAR,&UCTL0 ; 8-bit characters: 13-22 mov.b #SSEL0,&UTCTL0 ; UCLK = ACLK @ 32768 Hz mov.b #0x03,&UBR00 ;
mov.b #0x00,&UBR10 ; cf p.13-16: 9600 bauds +/- 15%
mov.b #0x4A,&UMCTL0 ; bic.b #SWRST,&UCTL0
bis.b #UTXE0+URXE0,&ME1 ; Enable USART0 TXD/RXD ret
ou pour le port 1 :
SetupUART1: mov.b #SWRST,&UCTL1 ; cf p.13-4 bis.b #CHAR,&UCTL1
mov.b #SSEL0,&UTCTL1 mov.b #0x03,&UBR01 mov.b #0x00,&UBR11 mov.b #0x4A,&UMCTL1 bic.b #SWRST,&UCTL1 bis.b #UTXE1+URXE1,&ME2 ret
Une fois le port initialis´e, la communication de donn´ees se fait en ´emission par :
rs_tx: ;bit.b #UTXIFG0,&IFG1 ; p.13-29: UTXIFG0 set if U0TXBUF empty
;jz rs_tx
;mov.b tmp,&TXBUF0 ; bit.b #UTXIFG1,&IFG2
jz rs_tx
mov.b tmp,&TXBUF1 ; ret
ou en r´eception :
rs_rx:
; bit.b #URXIFG0,&IFG1 ; p.13-29: UTXIFG0 set if U0TXBUF empty bit.b #URXIFG1,&IFG2 ; p.13-29: UTXIFG0 set if U0TXBUF empty
jz rsrx ; pas de char recu
fin_rsrx:
; mov.b &RXBUF0,tmp mov.b &RXBUF1,tmp ret
Cˆot´e PC, la communication par port s´erie se fait au moyen de minicom : CTRL A-O pour d´efinir le port (/dev/ttyUSB0) et le contrˆole de flux (ni mat´eriel, ni logiciel).CTRL A-Ppour d´efinir la vitesse (ici 9600 bauds, N81).
Exercices :
1. ´emettre en continu la valeur binaire correspondant au caract`ere ’A’, 0x41 ou 65 en d´ecimal.
2. envoyer une valeur hexad´ecimale (par exemple 0x55 et 0xAA) en ASCII 3. envoyer une valeur en d´ecimal (par exemple 250) en ASCII
4. lire une caract`ere et le retranscrire
5. lire dans un baud rate (4800 N81) sur un port et ´ecrire dans un autre (9600 N81) sur l’autre port 6. dans le programme en C faisant clignoter les diodes, ajouter une fonction pour visualiser le contenu de
la pile et son ´evolution au cours de l’ex´ecution du programme. Il s’agit d’une fonctionnalit´e de base des debuggers.
5 Lire une valeur analogique
Pour lancer une conversion analogique-num´erique :
SetupADC12: ; book C. Nagy p.90 Vtemp=0.00355(T_C)+0.986
mov.w #SHT0_6+SHT1_6+REFON+ADC12ON,&ADC12CTL0 ; V_REF=1.5 V mov.w #SHP,&ADC12CTL1
mov.b #INCH_10+SREF_1,&ADC12MCTL0 ; p.17-11: single conversion bis.w #ENC+ADC12SC,&ADC12CTL0 ; MUST be .w jmfriedt adc: bit.b #1,&ADC12CTL1 ; wait while ADC busy==1
jnz adc
mov.w &ADC12MEM0,tmp ; conversion result ret
Le canal 10 (INCH 10) est un cas particulier : il s’agit d’une sonde interne de temp´erature.
Exercice :
lire la temp´erature et la tranmettre sous forme hexad´ecimale (caract`eres ASCII) sur le port s´erie.
La conversion s’obtient par (User Manual p.17-16) parVT emp= 0,00355×T empdegC+0,986. Nous observons exp´erimentalement un d´ecallage entre la temp´erature et la valeur du microcontrˆoleur qu’il semble n´ecessaire de calibrer pour chaque nouveau composant.
En C :
unsigned int IntDegC;
ADC12CTL0=SHT0_6+SHT1_6+REFON+ADC12ON; // V_REF=1.5 V ADC12CTL1=SHP;
ADC12MCTL0=INCH_10+SREF_1; // p.17-11: single conversion for (j=0;j<16;j++) {
ADC12CTL0 |= ENC+ADC12SC;
do {} while ((ADC12CTL1&1)==1);
IntDegC += (ADC12MEM0);
}
IntDegC/=16;
6 Interruption timer
Un gestionnaire d’interruption se d´efinit par
interrupt(TIMERA0_VECTOR) ;register interrupt vector
.global CCR0INT ;place a label afterwards so
CCR0INT:
RETI ;
Ici l’interruption correspondant au timer A est activ´ee chaque fois que le timer atteint une valeur pr´ed´efinie :
setupTA: MOV #TASSEL0+TACLR, &TACTL ; ACLK for Timer_A.
BIS #CCIE,&CCTL0 ; Enable CCR0 interrupt.
MOV #0x7FFF,&CCR0 ; load CCR0 with 32,767.
BIS #MC0,&TACTL ; start TA in "up to CCR0" mode
Dans cet exemple, le timer A est initialis´e `a 0x7FFF=32767. Le timer ´etant cadenc´e par un quartz horloger
`
a 215 Hz, l’interruption est activ´ee chaque seconde.
Une utilisation tr`es utile du timer est la sortie p´eriodique du mode veille dans lequel on place le micro- contrˆoleur afin d’´economiser l’´energie.
Exercices :
1. exploiter une interruption timer pour faire clignoter une diode `a fr´equence pr´ecis´ement d´etermin´ee depuis la fr´equence du r´esonateur `a quartz
2. ´ecrire une fonction d’horloge temps-r´eelle capable d’afficher le temps en secondes et minutes 3. utiliser une repr´esentation en BCD au lieu de binaire.
Afin d’effectuer une somme en BCD :
Clock: SETC ; Set Carry bit.
DADC.b SEC ; Increment seconds decimally CMP.b #0x60,SEC ; One minute elapsed?
JLO Clockend ; No, return
CLR.b SEC ; Yes, clear seconds
DADC.b MIN ; Increment minutes decimally CMP.b #0x60,MIN ; Sixty minutes elapsed?
JLO Clockend ; No, return
CLR.b MIN ; yes, clear minutes
DADC.b HR ; Increment Hours decimally CMP.b #0x24,HR ; 24 hours elapsed?
JLO Clockend ; No, return
CLR.b HR ; yes, clear hours
Clockend: RET ;
7 Exemples en C
La programmation en C sur microcontrˆoleur se r´esume en grande partie `a une syntaxe plus compacte mais une connaissance d´etaill´ee du fonctionnement du mat´eriel et de la fonction de chaque bit de chaque registre reste n´ecessaire.
#include "hardware.h"
#include <signal.h>
void delay(unsigned int d) { int i; for (i = 0; i<d; i++) { nop(); nop(); } } int test=0;
int main(void) {
int sec=0,min=0,hr=0;
WDTCTL = WDTCTL_INIT; //Init watchdog timer P1OUT = P1OUT_INIT; //Init output data of port1
P1SEL = P1SEL_INIT; //Select port or module -function on port1 P1DIR = P1DIR_INIT; //Init port direction register of port1 P1IES = P1IES_INIT; //init port interrupts
P1IE = P1IE_INIT;
/*
while (1) {
P1OUT ^= 0xff; delay(0x4fff);
P1OUT ^= 0xff; delay(0x4fff);
}
*/
TACTL = TASSEL0+TACLR;
//CCTL0 |= CCIE; // 0x10 asm("BIS #0x10,&0x0162");
CCR0 = 0x7FFF;
//TACTL |= MC0; // 0x10 asm("BIS #0x10,&0x0160");
eint();
while (1)
{// asm("bis #208, r2\n");
sec++;
if (sec>59) {min++;sec=0;}
if (min>59) {hr++;min=0;}
if (hr>23) hr=0;
P1OUT = 0xff;
do {asm("nop");} while ((TACTL&0x01)==0x00);
P1OUT = 0x00; delay(0x4fff);
test=0;
} }
interrupt(TIMERA0_VECTOR) isr() {test=1;}
Exercices :
1. faire clignoter une diode avec un d´elai (programme en C)
2. faire clignoter une diode avec une interruption timer (programme en C) 3. communiquer sur le port RS232 (programme en C)
8 Acc` es carte m´ emoire
Les microcontrˆoleurs 8 et 16 bits ne fournissent pas une puissance de calcul importante mais sont capable d’effectuer un certain nombre d’op´erations simples telles qu’asservissements et acquisitions de donn´ees. Dans tous les cas, par soucis de contrˆolea posterioriou de dupliquer en local les donn´ees transmises en temps r´eel, les applications de ces microcontrˆoleurs sont ´etendues par la possibilit´e de stocker ces informations sur un support peu coˆuteux et consommant peu de courant en mode veille (Fig. 2). Nous nous proposons d’´etudier le stockage d’informations sur cartes m´emoires de type MutliMediaCard (MMC) ou Secure Digital (SD).
Afin de gagner du temps, nous ne d´etaillerons pas le protocole d’initialisation et de communication d’une carte SD : nous utiliserons une librairie mise `a disposition par son auteur pour conserver et relire des donn´ees sur support de stockage de masse non volatile2. L’utilisation d’une librairie opensource ne prive cependant pas d’en lire le contenu en vue d’en comprendre le fonctionnement.
Les cartes SD fonctionnent en deux mode : le mode natif, rapide et supportant l’encryption des donn´ees, et le mode SPI, plus lent mais facilement accessible depuis n’importe quel microcontrˆoleur. Nous utiliserons ce second mode, dans lequel seules 4 broches sont utiles (fig. 1).
2http://www.true-random.com/homepage/projects/msp430 mmc/
1 = CS 2= MOSI 3=GND 5=CK 6=GND 7=MISO 4=Vcc=3,3 V
1 2 3 4 5 6 7 8 9
8,9 : NC (SD)
Fig.1 – Assignation des broches d’une carte SD. Les broches 8 et 9 n’existent pas dans les cartes MMC, qui fonctionnent sinon de la mˆeme fa¸con.
Apr`es initialisation parif (initMMC() == MMC SUCCESS), deux commandes de base nous servent `a ´ecrire (mmcWriteBlock(adresse)) et lire (mmcReadBlock(adresse, taille)) des donn´ees sur la carte. Il s’agit d’´ecritures non-format´ees : la carte n’est pas lisible depuis un PC qui s’attend `a trouver un syst`eme de fi- chier. Toute transaction sur une carte SD se fait par blocs de 512 octets (tableaummc buffer). Les informations ne sont pas stock´ees en zone non-volatile tant que l’acquittement d’´ecriture n’est pas ´emis : lors des acquisitions lentes, une partie de la derni`ere s´equence d’acquisitions pourra ˆetre perdue lors de la mise hors tension du circuit.
Afin de compiler notre programmeprogramme.c avec les librairies d’acc`es `a la carte SD : msp430-gcc -mmcu=msp430x149 -o sd.elf test_jmf.c led.o mmc.o
Pour programmer et voir imm´ediatement le r´esultat :
msp430-jtag -e sd.elf && stty -F /dev/ttyUSB0 9600 && cat < /dev/ttyUSB0
Exercices :
– valider quelques fonctions de debuggage dont nous devons ˆetre certain du bon fonctionnement : allu- mage/extinction des diodes, transmission de donn´ees en RS232
– ´ecrire dans un bloc de la carte SD une s´erie de valeurs, r´einitialiser le tableau de caract`eres et lire les valeurs stock´ees. V´erifier qu’il s’agit des valeurs ´ecrites.
– ´ecrire et relire sur plusieurs blocs m´emoire. Attention : les adresses doiventtoujoursˆetre multiples de 512.
– ´ecrire des valeurs issues de la conversion analogique-num´erique ou de la lecture d’un port s´erie. Que se passe-t-il lors du passage d’un bloc de la carte SD au suivant ?
Pour aller plus loin : impl´ementer un syst`eme de fichier qui permette la relecture des informations stock´ees sur la carte depuis un PC (FAT16).
47.225 47.23 47.235 47.24 47.245 47.25
6.014 6.016 6.018 6.02 6.022 6.024 6.026 6.028 6.03 6.032 6.034 6.036
latitude (deg.)
longitude (deg.)
"080920_msp430.asc" u 1:2
6.014 6.016 6.018 6.02 6.022 6.024 6.026 6.028 6.03 6.032 6.034 6.036
47.225 47.23 47.235 47.24 47.245 47.25 250
300 350 altitude (m) 400
"080920_msp430.asc" u 1:2:3
longitude (deg.)
latitude (deg.) altitude (m)
Fig. 2 – Exemple d’acquisition de trames GPS avec une fr´equence de 1 Hz, et restitution du trac´e en 2D (gauche) et 3D (droite) sousgnuplot.
Afficher des donn´ ees
Deux outils sont mis `a disposition pour afficher des courbes : gnuplotetoctave3. gnuplotaffiche des courbes lus dans des fichiers :
plot "fichier"
on peut pr´eciser les colonnes `a afficher, l’axe des abscisses et ordonn´ees... : plot "fichier" using 2:3 with lines
pour utiliser la seconde colonne comme abscisse et la troisi`eme colonne comme ordonn´ee, les points ´etant reli´es par des lignes.help plotpour toutes les options.
octaveest une version opensource de Matlab et en reprend toutes les syntaxes. Seule fonctionnalit´e nouvelle qui nous sera utile ici : octavepeut lire des donn´ees au format hexad´ecimaal :
f=fopen("fichier","r");d=fscanf("%x");fclose(d);
La variabledcontient alors les informations qui ´etaient stock´ees dans le fichierfichierau format hexad´ecimal.
Si le fichier contenaitNlignes, les donn´ees sont lues par colonne et les informations d’une colonne donn´ee d’ob- tiennent par d(1 :N :length(d)).
3Pour une description plus exaustive des fonctionnalit´es degnuplotetoctave, on pourra consulterhttp://jmfriedt.free.fr/
lm octave.pdf.