Programmation de l'Evalbot LM3S9B92 (Texas Instruments) en assembleur (sans utiliser les bibliothèques Stellarisware!)
Aucun de ces exemples n'utilisent de bibliothèques. (English version here)
L'Evalbot
est un kit d'évaluation de Texas Instrument basé sur le processeur Stellaris LM3S9B92
(datashhet ici)
, processeur de la famille ARM Cortex M3, c'est
une plateforme orientée robotique puisque le kit inclus :
A l'ESIEE nous utilisons ce kit pour les cours de microprocesseurs dès la première année : chaque bînome reçoit un kit qu'il peut emmener
chez lui pour refaire les TPs et réaliser des projets (et oui il faut un gros stocke : 300 kits!).
( Exemple de sujet de TP )
Les exemples suivant sont basés sur l'environnement de dev.
Keil MDK µVision 4, il suffit de remplir les champs de cette page
pour obtenir le lien de téléchargement d'une version limitée à la génération de 32Ko de binaire. Cet environement contient un editeur
de code, un cross compilateur, un simulateur, un debuggeur...
Pour utiliser les codes ci-dessous, créez un nouveau projet sous Keil MDK, selectionnez lm3s9b92, acceptez d'utiliser le fichier startup.s
proposé. Puis dans les propriétés du projet, cochez "use microlib" (bien qu'on ne l'utilisera pas, c'est pour éviter des erreurs d'éditions de liens).
Dans le menu propriété, onglet "debug properties", selectionnez "Use : Stellaris ICDI" (voir ci-dessous
si vous avez besoin de drivers). Dans l'onglet "Utilities", selectionnez "Stellaris ICDI"
comme "target driver for flash programming."
Connectez l'USB de votre Evalbot (connecteur de gauche "ICDI"), puis allumer le (bouton on), Windows recherche alors le drivers qui
doit être téléchargé ici :
Stellaris_FTDI_2_08_14.zip
(vous aurez besoin de créer un compte chez Texas Instrument), ou directement ici si vous êtes à l'ESIEE : https://extra.esiee.fr/~grandpit/Stellaris_FTDI_2_08_14.zip
Windows vous demandera successivement d'installer 3 drivers, c'est normal (JTAG driver, Serial over USB driver...).
Liste des exemples :
Commentaires bienvenus à t.grandpierre @at esiee.fr
Blinking leds
;; TG 09/2012 - Evalbot (Cortex M3 de Texas Instrument)
;; fait clignoter les leds connectées au port GPIO_F
AREA |.text|, CODE, READONLY
SYSCTL_PERIPH EQU 0x400FE108 ;SYSCTL_RCGC2_R : pour enable clock (p291 datasheet de lm3s9b92.pdf)
GPIO_PORTF_BASE EQU 0x40025000 ; voir chap.8 "GPIO": page 416 de lm3s9B92.pdf
GPIO_O_DIR EQU 0x00000400 ; GPIO Direction (p417 de lm3s9B92.pdf)
GPIO_O_DR2R EQU 0x00000500 ; GPIO 2-mA Drive Select (p428 de lm3s9B92.pdf)
GPIO_O_DEN EQU 0x0000051C ; GPIO Digital Enable (p437 de lm3s9B92.pdf)
; Pour être "propre" il faudrait aussi s'assurer que les
;autres registres de configuration sont a 0 (ce qui est le
;cas a l'init.)
PORT4 EQU 0x10 ;led sur port 4
PORT5 EQU 0x20 ;led sur port 5
DUREE EQU 0x002FFFFF
ENTRY
EXPORT __main
__main
ldr r6, = SYSCTL_PERIPH
mov r0, #0x00000020 ;;Enable clock sur GPIOF où sont branchés les leds
str r0, [r6]
nop ; tres tres important....(beaucoup temps perdu, cf petite note p413!)
nop ; "There must be a delay of 3 system clocks before any GPIO reg. access
nop ; pas necessaire en simu ou en debbug step by step...@#@! :-(
ldr r6, = GPIO_PORTF_BASE+GPIO_O_DIR ;2 Pins du portF en sortie
ldr r0, = PORT4 + PORT5
str r0, [r6]
ldr r6, = GPIO_PORTF_BASE+GPIO_O_DEN ;;Enable Digital Function (p316 )
ldr r0, = PORT4 + PORT5
str r0, [r6]
ldr r6, = GPIO_PORTF_BASE+GPIO_O_DR2R ;; Choix de l'intensité de sortie (2mA)
ldr r0, = PORT4 + PORT5
str r0, [r6]
mov r2, #0x000 ;pour eteindre tout
;Allume les 2 leds
mov r3, #(PORT4 + PORT5) ;Allume portF broche 4et 5 : 00110000
ldr r6, = GPIO_PORTF_BASE+ ((PORT4+PORT5)<<2) ; @data Register = @base + (mask<<2)
;Pour allumer seulement la led broche 4 sans toucher au reste (led 5)
;mov r3, #PORT4 ;Allume portF broche 4 : 00010000
;ldr r6, = GPIO_PORTF_BASE+ (PORT4<<2) ; @data Register = @base + (mask<<2)
;on peut aussi allumer la led 4 comme ca => ca eteint la led 5 si allumée
;mov r3, #PORT5 ;Allume portF broche 4et 5 : 00110000
;ldr r6, = GPIO_PORTF_BASE+ ((PORT4+PORT5)<<2) ; @data Register = @base + (mask<<2)
loop
str r2, [r6] ;Eteint tout car r2 = 0x00
ldr r1, = DUREE ;pour la duree de la boucle d'attente1 (wait1)
wait1 subs r1, #1
bne wait1
str r3, [r6] ;Allume en fonction du contenu de r3
ldr r1, = DUREE ;pour la duree de la boucle d'attente2 (wait2)
wait2 subs r1, #1
bne wait2
b loop
END
Lecteure des inters
Pour lire les interrupteurs, il faut :
- activer l'horloge du périphérique GPIO correspondant (RCGC2 reg.)
- configurer la broche en entrée : c'est déjà le cas au reset/startup
- activer la résistance de pull-up (il n'y a pas de résistance de pull up sur la carte)
- activer les broches utilisées (GPIODEN)
- That's all folk! Il sufffit maintenant de lire à l'adresse : (GPIODATA base address + PinMASK<<2)
Driving Motors by PWM
Le code suivant initialise tous les registres nécessaire au pilotage des 2 moteurs de l'Evalbot, sans passer par
les fonctions C stockées dans la ROM. Cette approche permet de faire des TPs en assembleur, sans "bizarreries" que les
étudiants ne pourraient pas comprendre (binding vers du C etc). Le but est donc uniquement pédagogique.
Le code est organisé dans 2 fichiers Main.s and Motors.s.
Pour utiliser ce code, il faut créer un projet sous Keil MDK comme vu plus haut.
Une fois que c'est fait, ajoutez les 2 fichiers à votre projet, compile & download : L'Evalbot devrait commencer à bouger
dès l'appuie sur le bouton "ON".
Remarque : Ça m'a pris des heures de faire ce code (configuration des registres de PWM) que je donne aujourd'hui, aussi citez moi, merci!
Main.s
;Pilotage 2 Moteurs Evalbot par PWM tout en ASM
;TG 18 Novembre 2012 à partir de la lib stellaris
;Pas de gestion des fautes retourné par pont en H.
;Les pages se réfèrent au datasheet lm3s9b92.pdf
;ATTENTION, c'est du Branch & Link pour le moment...
;donc DANGER si appels imbriques
AREA |.text|, CODE, READONLY
ENTRY
EXPORT __main
IMPORT MOTEUR_INIT
IMPORT MOTEUR_DROIT_ON
IMPORT MOTEUR_DROIT_OFF
IMPORT MOTEUR_DROIT_AVANT
IMPORT MOTEUR_DROIT_ARRIERE
IMPORT MOTEUR_DROIT_INVERSE
IMPORT MOTEUR_GAUCHE_ON
IMPORT MOTEUR_GAUCHE_OFF
IMPORT MOTEUR_GAUCHE_AVANT
IMPORT MOTEUR_GAUCHE_ARRIERE
IMPORT MOTEUR_GAUCHE_INVERSE
__main BL MOTEUR_INIT ; configure les pwms + GPIO
loop BL MOTEUR_DROIT_AVANT ;Avance droit devant
BL MOTEUR_GAUCHE_AVANT
BL MOTEUR_DROIT_ON
BL MOTEUR_GAUCHE_ON
BL WAIT
BL WAIT
BL MOTEUR_DROIT_ARRIERE ;Tourne sur lui meme
BL WAIT
b loop
WAIT ldr r1, =0xAFFFFF
wait1 subs r1, #1
bne wait1
BX LR
END
Motor.s
;Pilotage Moteur Evalbot par PWM tout en ASM
;TG 17 Novembre 2012
;a partir de la lib stellaris, ouf!
;Controle des 2 moteurs, pas gestion
;des fautes retourné par pont en H.
;Les pages se réfèrent au datasheet lm3s9b92.pdf
;Cablage :
;pin 10/PD0/PWM0 => input PWM du pont en H DRV8801RT
;pin 11/PD1/PWM1 => input Phase_R du pont en H DRV8801RT
;pin 12/PD2 => input SlowDecay commune aux 2 ponts en H
;pin 98/PD5 => input Enable 12v du conv DC/DC
;pin 86/PH0/PWM2 => input PWM du 2nd pont en H
;pin 85/PH1/PWM3 => input Phase du 2nd pont en H
GPIO_0 EQU 0x1
GPIO_1 EQU 0x2
GPIO_2 EQU 0x4
GPIO_5 EQU 0x20
SYSCTL_RCGC0 EQU 0x400FE100
SYSCTL_RCGC2 EQU 0x400FE108
PORTD_BASE EQU 0x40007000
GPIODATA_D EQU PORTD_BASE
GPIODIR_D EQU PORTD_BASE+0x00000400
GPIODR2R_D EQU PORTD_BASE+0x00000500
GPIODEN_D EQU PORTD_BASE+0x0000051C
GPIOPCTL_D EQU PORTD_BASE+0x0000052C
GPIOAFSEL_D EQU PORTD_BASE+0x00000420
PORTH_BASE EQU 0x40027000
GPIODATA_H EQU PORTH_BASE
GPIODIR_H EQU PORTH_BASE+0x00000400
GPIODR2R_H EQU PORTH_BASE+0x00000500
GPIODEN_H EQU PORTH_BASE+0x0000051C
GPIOPCTL_H EQU PORTH_BASE+0x0000052C
GPIOAFSEL_H EQU PORTH_BASE+0x00000420
PWM_BASE EQU 0x040028000 ;BASE des Block PWM p.1138
PWMENABLE EQU PWM_BASE+0x008
;Block PWM0 pour sorties PWM0 et PWM1 (moteur 1)
PWM0CTL EQU PWM_BASE+0x040 ;p1167
PWM0LOAD EQU PWM_BASE+0x050
PWM0CMPA EQU PWM_BASE+0x058
PWM0CMPB EQU PWM_BASE+0x05C
PWM0GENA EQU PWM_BASE+0x060
PWM0GENB EQU PWM_BASE+0x064
;Block PWM1 pour sorties PWM1 et PWM2 (moteur 2)
PWM1CTL EQU PWM_BASE+0x080
PWM1LOAD EQU PWM_BASE+0x090
PWM1CMPA EQU PWM_BASE+0x098
PWM1CMPB EQU PWM_BASE+0x09C
PWM1GENA EQU PWM_BASE+0x0A0
PWM1GENB EQU PWM_BASE+0x0A4
VITESSE EQU 0x1A2 ;Voir config PWM plus bas
;(0x1C2=>10% ;pour 50% => plus petit, à calculer, exemple 0x192 = rapide!)
AREA |.text|, CODE, READONLY
ENTRY
EXPORT MOTEUR_INIT
EXPORT MOTEUR_DROIT_ON
EXPORT MOTEUR_DROIT_OFF
EXPORT MOTEUR_DROIT_AVANT
EXPORT MOTEUR_DROIT_ARRIERE
EXPORT MOTEUR_DROIT_INVERSE
EXPORT MOTEUR_GAUCHE_ON
EXPORT MOTEUR_GAUCHE_OFF
EXPORT MOTEUR_GAUCHE_AVANT
EXPORT MOTEUR_GAUCHE_ARRIERE
EXPORT MOTEUR_GAUCHE_INVERSE
MOTEUR_INIT
ldr r6, = SYSCTL_RCGC0
ldr r0, [R6]
ORR r0, r0, #0x00100000 ;;bit 20 = PWM recoit clock: ON (p271)
str r0, [r6]
;ROM_SysCtlPWMClockSet(SYSCTL_PWMDIV_1);PWM clock is processor clock /1
;Je ne fais rien car par defaut = OK!!
;*(int *) (0x400FE060)= *(int *)(0x400FE060)...;
;RCGC2 : Enable port D GPIO(p291 ) car Moteur Droit sur port D
ldr r6, = SYSCTL_RCGC2
ldr r0, [R6]
ORR r0, r0, #0x08 ;; Enable port D GPIO
str r0, [r6]
;MOT2 : RCGC2 : Enable port H GPIO (2eme moteurs)
ldr r6, = SYSCTL_RCGC2
ldr r0, [R6]
ORR r0, r0, #0x80 ;; Enable port H GPIO
str r0, [r6]
nop
nop
nop
;;Pin muxing pour PWM, port D, reg. GPIOPCTL(p444), 4bits de PCM0=0001<=>PWM (voir p1261)
;;il faut mettre 1 pour avoir PD0=PWM0 et PD1=PWM1
ldr r6, = GPIOPCTL_D
;ldr r0, [R6] ;; *(int *)(0x40007000+0x0000052C)=1;
;ORR r0, r0, #0x01 ;; Port D, pin 1 = PWM
mov r0, #0x01
str r0, [r6]
;;MOT2 : Pin muxing pour PWM, port H, reg. GPIOPCTL(p444), 4bits de PCM0=0001<=>PWM (voir p1261)
;;il faut mettre mux = 2 pour avoir PH0=PWM2 et PH1=PWM3
ldr r6, = GPIOPCTL_H
mov r0, #0x02
str r0, [r6]
;;Alternate Function Select (p 426), PD0 utilise alernate fonction (PWM au dessus)
;;donc PD0 = 1
ldr r6, = GPIOAFSEL_D
ldr r0, [R6] ;*(int *)(0x40007000+0x00000420)= *(int *)(0x40007000+0x00000420) | 0x00000001;
ORR r0, r0, #0x01 ;
str r0, [r6]
;;MOT2 : Alternate Function Select (p 426), PH0 utilise PWM donc Alternate funct
;;donc PH0 = 1
ldr r6, = GPIOAFSEL_H
ldr r0, [R6] ;*(int *)(0x40007000+0x00000420)= *(int *)(0x40007000+0x00000420) | 0x00000001;
ORR r0, r0, #0x01 ;
str r0, [r6]
;;-----------PWM0 pour moteur 1 connecté à PD0
;;PWM0 produit PWM0 et PWM1 output
;;Config Modes PWM0 + mode GenA + mode GenB
ldr r6, = PWM0CTL
mov r0, #2 ;Mode up-down-up-down, pas synchro
str r0, [r6]
ldr r6, =PWM0GENA ;en decomptage, qd comparateurA = compteur => sortie pwmA=0
;en comptage croissant, qd comparateurA = compteur => sortie pwmA=1
mov r0, #0x0B0 ;0B0=10110000 => ACTCMPBD=00 (B down:rien), ACTCMPBU=00(B up rien)
str r0, [r6] ;ACTCMPAD=10 (A down:pwmA low), ACTCMPAU=11 (A up:pwmA high) , ACTLOAD=00,ACTZERO=00
ldr r6, =PWM0GENB;en comptage croissant, qd comparateurB = compteur => sortie pwmA=1
mov r0, #0x0B00 ;en decomptage, qd comparateurB = compteur => sortie pwmB=0
str r0, [r6]
;Config Compteur, comparateur A et comparateur B
;;#define PWM_PERIOD (ROM_SysCtlClockGet() / 16000),
;;en mesure : SysCtlClockGet=0F42400h, /16=0x3E8,
;;on divise par 2 car moteur 6v sur alim 12v
ldr r6, =PWM0LOAD ;PWM0LOAD=periode/2 =0x1F4
mov r0, #0x1F4
str r0,[r6]
ldr r6, =PWM0CMPA ;Valeur rapport cyclique : pour 10% => 1C2h si clock = 0F42400
mov r0, #VITESSE
str r0, [r6]
ldr r6, =PWM0CMPB ;PWM0CMPB recoit meme valeur. (rapport cyclique depend de CMPA)
mov r0, #0x1F4
str r0, [r6]
;Control PWM : active PWM Generator 0 (p1167): Enable+up/down + Enable counter debug mod
ldr r6, =PWM0CTL
ldr r0, [r6]
ORR r0, r0, #0x07
str r0, [r6]
;;-----------PWM2 pour moteur 2 connecté à PH0
;;PWM1block produit PWM2 et PWM3 output
;;Config Modes PWM2 + mode GenA + mode GenB
ldr r6, = PWM1CTL
mov r0, #2 ;Mode up-down-up-down, pas synchro
str r0, [r6] ;*(int *)(0x40028000+0x040)=2;
ldr r6, =PWM1GENA ;en decomptage, qd comparateurA = compteur => sortie pwmA=0
;en comptage croissant, qd comparateurA = compteur => sortie pwmA=1
mov r0, #0x0B0 ;0B0=10110000 => ACTCMPBD=00 (B down:rien), ACTCMPBU=00(B up rien)
str r0, [r6] ;ACTCMPAD=10 (A down:pwmA low), ACTCMPAU=11 (A up:pwmA high) , ACTLOAD=00,ACTZERO=00
;*(int *)(0x40028000+0x060)=0x0B0; //
ldr r6, =PWM1GENB ;*(int *)(0x40028000+0x064)=0x0B00;
mov r0, #0x0B00 ;en decomptage, qd comparateurB = compteur => sortie pwmB=0
str r0, [r6] ;en comptage croissant, qd comparateurB = compteur => sortie pwmA=1
;Config Compteur, comparateur A et comparateur B
;;#define PWM_PERIOD (ROM_SysCtlClockGet() / 16000),
;;en mesure : SysCtlClockGet=0F42400h, /16=0x3E8,
;;on divise par 2 car moteur 6v sur alim 12v
;*(int *)(0x40028000+0x050)=0x1F4; //PWM0LOAD=periode/2 =0x1F4
ldr r6, =PWM1LOAD
mov r0, #0x1F4
str r0,[r6]
ldr r6, =PWM1CMPA ;Valeur rapport cyclique : pour 10% => 1C2h si clock = 0F42400
mov r0, #VITESSE
str r0, [r6] ;*(int *)(0x40028000+0x058)=0x01C2;
ldr r6, =PWM1CMPB ;PWM0CMPB recoit meme valeur. (CMPA depend du rapport cyclique)
mov r0, #0x1F4 ; *(int *)(0x40028000+0x05C)=0x1F4;
str r0, [r6]
;Control PWM : active PWM Generator 0 (p1167): Enable+up/down + Enable counter debug mod
ldr r6, =PWM1CTL
ldr r0, [r6] ;*(int *) (0x40028000+0x40)= *(int *)(0x40028000+0x40) | 0x07;
ORR r0, r0, #0x07
str r0, [r6]
;;-----Fin config des PWMs
;PORT D OUTPUT pin0 (pwm)=pin1(direction)=pin2(slow decay)=pin5(12v enable)
ldr r6, =GPIODIR_D
ldr r0, [r6]
ORR r0, #(GPIO_0+GPIO_1+GPIO_2+GPIO_5)
str r0,[r6]
;Port D, 2mA les meme
ldr r6, =GPIODR2R_D ;
ldr r0, [r6]
ORR r0, #(GPIO_0+GPIO_1+GPIO_2+GPIO_5)
str r0,[r6]
;Port D, Digital Enable
ldr r6, =GPIODEN_D ;
ldr r0, [r6]
ORR r0, #(GPIO_0+GPIO_1+GPIO_2+GPIO_5)
str r0,[r6]
;Port D : mise à 1 de slow Decay et 12V et mise à 0 pour dir et pwm
ldr r6, =(GPIODATA_D+((GPIO_0+GPIO_1+GPIO_2+GPIO_5)<<2))
mov r0, #(GPIO_2+GPIO_5) ; #0x24
str r0,[r6]
;MOT2, PH1 pour sens moteur ouput
ldr r6, =GPIODIR_H
mov r0, #0x03 ;
str r0,[r6]
;Port H, 2mA les meme
ldr r6, =GPIODR2R_H
mov r0, #0x03
str r0,[r6]
;Port H, Digital Enable
ldr r6, =GPIODEN_H
mov r0, #0x03
str r0,[r6]
;Port H : mise à 1 pour dir
ldr r6, =(GPIODATA_H +(GPIO_1<<2))
mov r0, #0x02
str r0,[r6]
BX LR ; FIN du sous programme d'init.
;Enable PWM0 (bit 0) et PWM2 (bit 2) p1145
;Attention ici c'est les sorties PWM0 et PWM2
;qu'on controle, pas les blocks PWM0 et PWM1!!!
MOTEUR_DROIT_ON
;Enable sortie PWM0 (bit 0), p1145
ldr r6, =PWMENABLE
ldr r0, [r6]
orr r0, #0x01 ;bit 0 à 1
str r0, [r6]
BX LR
MOTEUR_DROIT_OFF
ldr r6, =PWMENABLE
ldr r0, [r6]
and r0, #0x0E ;bit 0 à 0
str r0, [r6]
BX LR
MOTEUR_GAUCHE_ON
ldr r6, =PWMENABLE
ldr r0, [r6]
orr r0, #0x04 ;bit 2 à 1
str r0, [r6]
BX LR
MOTEUR_GAUCHE_OFF
ldr r6, =PWMENABLE
ldr r0, [r6]
and r0, #0x0B ;bit 2 à 0
str r0, [r6]
BX LR
MOTEUR_DROIT_ARRIERE
;Inverse Direction (GPIO_D1)
ldr r6, =(GPIODATA_D+(GPIO_1<<2))
mov r0, #0
str r0,[r6]
BX LR
MOTEUR_DROIT_AVANT
;Inverse Direction (GPIO_D1)
ldr r6, =(GPIODATA_D+(GPIO_1<<2))
mov r0, #2
str r0,[r6]
BX LR
MOTEUR_GAUCHE_ARRIERE
;Inverse Direction (GPIO_D1)
ldr r6, =(GPIODATA_H+(GPIO_1<<2))
mov r0, #2 ; contraire du moteur Droit
str r0,[r6]
BX LR
MOTEUR_GAUCHE_AVANT
;Inverse Direction (GPIO_D1)
ldr r6, =(GPIODATA_H+(GPIO_1<<2))
mov r0, #0
str r0,[r6]
BX LR
MOTEUR_DROIT_INVERSE
;Inverse Direction (GPIO_D1)
ldr r6, =(GPIODATA_D+(GPIO_1<<2))
ldr r1, [r6]
EOR r0, r1, #GPIO_1
str r0,[r6]
BX LR
MOTEUR_GAUCHE_INVERSE
;Inverse Direction (GPIO_D1)
ldr r6, =(GPIODATA_H+(GPIO_1<<2))
ldr r1, [r6]
EOR r0, r1, #GPIO_1
str r0,[r6]
BX LR
END
Oled Display in full asm (no Stellarisware, direct I2C)