Evalbot LM3S9B92 Programming in ASM (without using Stellarisware libs or any other libs!!)

This page provide some material for programming Stellaris Evalbot using assembler language. Theses example don't use any librairies. (Version française ici)
Evalbot is a robotic evaluation kit based on the LM3S9B92 ARM Cortex M3 (datashhet here)Stellaris Cortex M3 processor, this board includes : The following examples are based on the free limited version of Keil MDK µVision 4 (fill in the form) and the Evalbot developpment kit.

In order to use theses codes, create a new project under Keil MDK, select lm3s9b92 processor. Then select "use microlib" (this avoid an error message during linking). In the debug properties, select "Use : Stellaris ICDI" (see top of this webpage if you need the driver). In the Utilities, select "Stellaris ICDI" as target driver for flash programming.

Plug your Evalbot to your PC with the USB cable, Windows then search for Evalbot drivers which are here : Stellaris_FTDI_2_08_14.zip (you need to create a TI account). Windows will ask successively to install 3 drivers on your PC, this is normal (JTAG driver, Serial over USB driver...).

List of examples :


Comments are welcome to t.grandpierre @at esiee.fr



	;; TG 09/2012 - Evalbot (Cortex M3 de Texas Instrument)
	;; make both leds blinkying no  libs!
   	
		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 

Reading switch

In order to read switches states you need :

Driving Motors by PWM

The following code initialize all the register in order to drive the 2 evalbot motors throught PWM peripheral with no libs. It is organized into two files : Main.s and Motors.s. In order to use this code, create a new project under Keil MDK, select lm3s9b92 processor. Then select "use microlib" (this avoid an error message during linking) In the debug properties, select "Use : Stellaris ICDI" (see top of this webpage if you need the driver). In the Utilities, select "Stellaris ICDI" as target driver for flash programming. Once this is done, just add the 2 following files to your project, compile & download : Evalbot should start moving as you press "ON" button.

Remark : it took me hours to make this code...working, I give it freely, but please refere it to me / just let my name :-) Sorry for code comments : some comments and labels are in French : Moteur=Motor, Droit=right, Left=gauche, Avant=Forward, Arriere=Backward

Main.s

;This code intialize the GPIO and PWM units
;in order to drive the 2 Evalbot motors
;T. Grandpierre 17 Novembre 2012
;Pages numbers refere to lm3s9b92.pdf datasheet

									
;WARNING 1 : this code use Branch & Link, so don't use nested branch!
;WARNING 2 : subprogramms doesn't save register value..

		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	   ; Right motor : forward
		BL	MOTEUR_GAUCHE_AVANT    ; Left  motor : forward
		BL	MOTEUR_DROIT_ON		   ; Right motor : on 
		BL	MOTEUR_GAUCHE_ON	   ; Left  motor : on
		BL	WAIT
		BL	WAIT
		BL	MOTEUR_DROIT_ARRIERE   ;Right motor : reverse direction
		BL	WAIT

		b	loop

WAIT	ldr r1, =0xAFFFFF 
wait1	subs r1, #1
        bne wait1
		BX	LR


        END

Motor.s

;This code intialize the GPIO and PWM units
;in order to drive the 2 Evalbot motors
;T. Grandpierre 17 Novembre 2012
;Pages numbers refere to lm3s9b92.pdf datasheet

;pin 10/PD0/PWM0 => input PWM of right motor HBridge  DRV8801RT
;pin 11/PD1/PWM1 => input Phase_R  (direction) of hbridge 
;pin 12/PD2		=> input SlowDecay common to the 2 Hbridge drivers
;pin 98/PD5		=> input Enable of 5v/12v DC/DC converter 
;pin 86/PH0/PWM2 => input PWM of 2nd motors hbridge
;pin 85/PH1/PWM3 => input Phase of 2nd motors hbridge

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)