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


	;; 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 :

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)