C2000 Launchpad Assembly only Software PWM Tutorial

So, i've completed an assembly project to build a software PWM program for the C2000 Launchpad.
C2000 is actually a remarkably strong platform for assembly only program with some powerful addressing modes,
many registers, and many powerful instructions. The platform to me reminisces of MSP430, which i had a wonderful
time hacking at with the FOSS tool-chain and VIM. Being forced to use CCS and controlSUITE is a minor annoyance,
(lets face it, it's the only practical way to develop for this platform) but its integration makes up for the
majority of it's shortcomings. Has anyone tried to build Cortex assembly projects with GNU tools and OpenOCD?
Thumb2 will make anyone human pull their hair out. Regardless of me being happy with the platform and environment,
without further ado, here is the code:

Main.asm

; main.asm
; By Daryl Metzler djmetzle@ncsu.edu
 
 
 
;
; MAIN
;
 
	.sect	".text"		; Program data
	.global	_main
_main:
 
 
; memory howto (for the Watchdog)
;memory:		; a simple demo of direct addressing
;		MOVW	DP, #variable		; So this is how to load data
;										;directly from memory
;		MOVL 	XAR0, @variable		; The DP points to the page data is on,
										;then MOV takes offset
 
; turn off watchdog timer
 
wd_disable:				; We need to disable the WatchDog before beginning,
					; or the DSP will reset after 512 cycles
		EALLOW			;Enable EALLOW protected register access
		MOVZ DP, #7029h>>6	;Set data page for WDCR register
	    	MOV @7029h, #0068h	;Set WDDIS bit in WDCR to disable WD
		EDIS			;Disable protected register access
 
; interrupts
 
intenable:	; Allow interrupts
		EALLOW
		CLRC VMAP	; map interrupts from boot rom to 0x0
		EINT
		EDIS
 
; PLL
 
pll_enable:		; Turn on the PLL
		EALLOW
		MOVZ DP, #7021>>6	; PLLCR
		MOV @0x7029, #0xC	; Set PLLCR[DIV] = 12
		MOVZ DP, #7011>>6	; PLLSTS
		OR @0x7011, #0x100	; Set PLLSYS[DIVSEL] = 2
		EDIS
 
; CPU Timer 1
 
timer_enable:
		EALLOW
		OR IER, #0x1000	; Enable INT13 in IER
		MOVZ DP, #0xC0C>>6	; TIMER1TCR
		OR @0xC0C, #0x4000	; Set Interrupt enable in TIMER1TCR
		MOVZ DP, #0xC0A>>6	; TIMER1TCR
		MOV @0xC0A, #0x500	; Set TIMER1PRD period register
		MOVZ DP, #0xC0B>>6	;
		MOV @0xC0B, #0x0	; TIMER1PRDH
		EDIS
 
; GPIO <blinkys>
 
gpio_enable:
		EALLOW
		MOVZ DP, #6F86h>>6	; GPA registers
		AND @6F86h, #0xFF00 	; Set GPIO1-4 for GPio
		MOVZ DP, #6FC2h>>6	; GPASET
		OR @6FC2h, #0x000F 	; Set GPIO1-4
		MOVZ DP, #0x6F8A>>6	; GPADIR
		MOV @6F8Ah, #0x000F	; Set direction
		MOVZ DP, #6FC0h>>6	; GPADAT
		AND @0x6FC0, #0xFFF0; Clear GPIO1-4 (LEDS ON)
		OR @0x6FC0, #0xFFFA 	; Set GPIO1-4
 
 
 
	; initialize sw_pwm
	MOV ACC, #0x0
	MOVL XAR0, #0x0
	MOVL XAR1, #0x40
	MOVL XAR2, #0x80
	MOVL XAR3, #0xC0
 
	MOVL XAR6, #0x0F
 
	MOVL XAR7, #0
 
 
loop:
 
	MOVW AL, AR7
	CMP AL, AR0
	B onp0, LT
	OR AR6, #0x1
onp0:
	CMP AL, AR1
	B onp1, LT
	OR AR6, #0x2
onp1:
	CMP AL, AR2
	B onp2, LT
	OR AR6, #0x4
onp2:
	CMP AL, AR3
	B onp3, LT
	OR AR6, #0x8
onp3: nop
	CMP AR7, #200h
	B swpwmwrap, GEQ
 
	B	loop, UNC
 
swpwmwrap:
	MOVL XAR7 , #0x0
	MOV AR6, #0h	; Clear GPIO1-4 (LEDS ON)
		; We're just incrementing the AR0-3 registers
		; Which pulses their respective LED
		MOV AL, AR0
		ADD AL, #1
		CMP AL, #100h
		B modulus0, LT
		MOV AL, #0
modulus0:
		MOVW AR0, AL
		MOV AL, AR1
		ADD AL, #1
		CMP AL, #100h
		B modulus1, LT
		MOV AL, #0
modulus1:
		MOVW AR1, AL
		MOV AL, AR2
		ADD AL, #1
		CMP AL, #100h
		B modulus2, LT
		MOV AL, #0
modulus2:
		MOVW AR2, AL
		MOV AL, AR3
		ADD AL, #1
		CMP AL, #100h
		B modulus3, LT
		MOV AL, #0
modulus3:
		MOV AR3, AL
 
	B loop,UNC	; loop after fall off
 
 
 
 

We see here that main is using a lot of magic and most of the work done here is getting the hardware alive and configured.
The output is that the LEDs pulse and blink using PWM. Really, there is no sophisticated output in terms of what is done with
that PWM. The duty cycles increment modulo half the duty period. The C2000 Launchpad's LED's are wired in such a way that even
at 25% duty cycle, it is difficult for the eye to pick out the "dim" LED from one at full intensity.

Interrupts.asm

	.sect ".text"
 
	.global int1irq
int1irq:
		NOP
		IRET
 
	.global int2irq
int2irq:
		NOP
		IRET
 
 
	.global timer1irq
timer1irq:
		MOV AL, AR7
		ADD AL, #0x1
		MOV AR7, AL
 
 
		PUSH DP
		MOVZ DP, #6fC0h>>6
		MOVW @6FC0h, AR6	; Light up the LEDs by AR6 value
		POP DP
		IRET
 

The interrupt routine "timer1irq" does little more than increment the PWM counter kept in AR7, and move the current LED GPIO value onto the GPADAT register.

Feel free to check out the directory to grab the code for yourself.

I hope someone finds this helpful, and that this launchpad gains a little more attention and steam with the community.

Leave a Reply

Your email address will not be published. Required fields are marked *