Code Examples from Class: Difference between revisions

From Class Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(32 intermediate revisions by the same user not shown)
Line 1: Line 1:
* Factorial
<nowiki>
; Factorial Calculation
; The answer is in R10

; Rob Frohne 2013

GLOBAL Reset_Handler
AREA Factorial, CODE, READONLY
ENTRY

Reset_Handler
movs r8, #0 ; Take the factorial of this number, n.
mov r10, #1 ; 0! and 1! are 1
beq stop ; If r8 is zero we are done
cmp r8, #1 ; If r8 is 1,
beq stop ; we are done
subs r9, r8, #1 ; n-1
loop mulne r10, r9, r8 ; n(n-1)
mov r8, r10
subs r9, r9, #1
bne loop
stop b stop
END
</nowiki>


----
*Factorial 2014
<nowiki>
_ AREA Add, CODE
ENTRY
; This program takes the factorial of n (in r2)
n EQU 0

ldr r2,=n ; Load r2 with the number to take the factorial of.
cmp r2,#0 ; Is n=0
bne check_for_1
mov r4,#1
b stop
check_for_1
cmp r2,#1
bne initialize_r4
mov r4,#1
b stop
initialize_r4
mov r4,r2
sub r3,r4,#1
loop
mul r5,r4,r3
mov r4,r5
subs r3,r3,#1
bne loop
stop b stop
END
</nowiki>
----

* 64 Bit Add
VAL1A EQU 0xffffffff
VAL1B EQU 0x0000000f
VAL2A EQU 0x00000001
VAL2B EQU 0x00000001
AREA LongAdd, CODE
ENTRY
ldr R0, = VAL1A
ldr R1, = VAL1B
ldr R2, = VAL2A
ldr R3, = VAL2B
adds R8,R0,R1
adcs R9,R1,R3
stop b stop
END

----

* Copy to RAM
* Copy to RAM
AREA Exaddress, CODE, READONLY
AREA Exaddress, CODE, READONLY
Line 5: Line 81:
LDR R9, =list
LDR R9, =list
mov R7, #4 ; number in list
mov R7, #4 ; number in list
ldr r6, =datastart
ldr r6, =datastart
loop
loop
ldr r8,[r9],#4
ldr r8,[r9],#4
Line 12: Line 87:
subs r7,r7,#1
subs r7,r7,#1
bne loop
bne loop
stop b stop
stop b stop
ALIGN
ALIGN
list DCW 0x1111, 0x2222, 0x3333, 0x4444, 0x5555
list DCW 0x1111, 0x2222, 0x3333, 0x4444, 0x5555
ALIGN
ALIGN
string DCB "This is a test.",0
string DCB "This is a test.",0 ; This string is not used in the copy to ram program
string2 DCB 'T','h','i'
string2 DCB 'T','h','i' ; This string2 is not used in the copy to ram program
ALIGN
ALIGN
AREA Thedata, DATA, NOINIT, READWRITE
AREA Thedata, DATA, NOINIT, READWRITE
datastart SPACE 20
datastart SPACE 20
END
END
* Subroutine Example1


----

* Subroutine Example1
; First Subroutine Example
; First Subroutine Example
; This program demonstrates using a subroutine,
; This program demonstrates using a subroutine,
; saving registers on the stack, etc.
; saving registers on the stack, etc.
; Rob Frohne, 1/3/2013
; Rob Frohne, 11/3/2013
stack_start EQU 0x40001000
stack_start EQU 0x40001000
Line 41: Line 117:
ldr sp, =stack_start ; Tell where we will place the stack.
ldr sp, =stack_start ; Tell where we will place the stack.
; (It goes down (lower addresses from here.)
; (It goes down (lower addresses from here.)
mov r1, #1 ; Store some numebers in some registers

mov r1, #1 ; Store some numebers in some registers
mov r2, #2
mov r2, #2
mov r3, #3
mov r3, #3

bl subroutine
bl subroutine
stop b stop
stop b stop
Line 53: Line 126:
; messes up the registers locally,
; messes up the registers locally,
; then restores the registers and returns.
; then restores the registers and returns.
subroutine
subroutine
stmfd sp!, {r1-r2,lr} ; save used registers and the link register (r14)
stmfd sp!, {r1-r2,lr} ; save used registers and the link register (r14)
mov r1,#0xffffffff ; mess up the registers
mov r1,#0xffffffff ; mess up the registers
mov r2, r1
mov r2, r1
ldmfd sp!, {r1,r2,pc} ; pop the stack and return
ldmfd sp!, {r1,r2,pc} ; pop the stack and return
END
END

----

* Minimum of a Signed Number
<nowiki>
; This program finds the minimum of a list of constant words in signed format
; The result is in r3 at the end of the routine.
; Rob Frohne 10/30/2013

AREA Program, CODE, READONLY

ENTRY

ldr r0,=end ; load the end address of the code & initialize it as the pointer.
ldr r2,=begin ; load the beginning address of the code.
mov r3,#0x7fffffff ; Initialize r3 as the most positive number.

loop
ldr r6,[r2],#4 ; load the next data into r6 and post increment r2 for the next data.
cmp r6,r3 ; Find out if r6 > r3. result from r6-r3 like subs r7,r6,r3 without r6 necessary.
bgt no_update ; If it is no update.
mov r3,r6 ; update if r6 is lower than r3.
no_update
cmp r0,r2 ; are we at the end yet
bne loop ; if r7 != 0 then keep looping

stop b stop
ALIGN
begin
DCD 0x8fffffff, 0x55555555, 0x44444444, 0x77777777, 0xffffffff
end
END
</nowiki>

----


* Compare Two Null Terminated Strings
<nowiki>
; Subroutine to compare two null terminated ASCII strings.
; The address where the two strings start are in r0 and r1
; The return is in r0, 1 for match and 0 for don't match.
; If they don't match, r1 gives the number of the first
; character that didn't match, starting with 0.
; r2 and r3 are not protected.
; Rob Frohne 11/6/2013
stack_start EQU 0x40001000
AREA String_Compare, CODE
ENTRY
Start
ldr sp, =stack_start ; Tell where we will place the stack.
; (It goes down (lower addresses from here.)
ldr r0, =string1
ldr r1, =string2
bl compare_strings
stop b stop

compare_strings
stmfd sp!, {r2,r4,r5,lr} ; save used registers and the link register (r14)
mov r2, #0 ; Initialize the counter for the first position.
loop
ldrb r4,[r0],#1
ldrb r5,[r1],#1
cmp r5,r4
bne do_not_match
cmp r4, #0 ; check for end of string.
beq match
add r2, #1
b loop
do_not_match
mov r0, #0 ; don't match
b finish
match
mov r0, #1
finish
mov r1,r2 ; Move the count into the result register.
ldmfd sp!, {r2,r4,r5,pc} ; pop the stack and return
string1
DCB "This is the first string.",00 ; For testing purposes.
ALIGN
string2
DCB "This is the first string.",00
ALIGN
END
</nowiki>

----

* BCD Add
<nowiki>
; Subroutine to add two BCD numbers in 32 bit form with each nibble
; representing a digit.
; locals: r8 for digit counter, single digit mask 0xf, r5 & r6 for masked addends,
; working BCD_carry, r7
; working result in r4
; the summed digits go in r3
; inputs: r0+r1
; outputs: r0 result and r1 carry
; only r4-r8 are saved.
; Rob Frohne 11/12/13
stack_start EQU 0x40001000
AREA ADD_BCD, CODE
ENTRY
Start
ldr sp, =stack_start ; Tell where we will place the stack.
; (It goes down (lower addresses from here.)
ldr r0, =0x12345678 ; The two numbers to add.
ldr r1, =0x87654321
bl bcd_add
stop b stop

bcd_add ; The subroutine
stmfd sp!, {r4-r8,lr}
mov r8, #8 ; Set the counter to move through the eight digits
mov r7, #0 ; Set the carry to zero to start with
mov r3, #0 ; This is where the resulting digits of the sum are stored.
loop
and r5, r0, #0xf ; mask for the rightmost digit.
and r6, r1, #0xf
add r4, r5, r7 ; add carry
add r4, r4, r6 ; r4=r5+r6
subs r4, r4, #10 ; Subtract 10 (base 10) to see if there is a carry.
bpl carry
mov r7, #0 ; set carry to zero
add r4, r4, #10 ; Add the 10 back in as there was no carry needed.
b roll_to_next_digit
carry
mov r7, #1 ; set the carry
roll_to_next_digit
orr r3, r3, r4 ; Add these digits into the sum.
ror r0, #4
ror r1, #4
ror r3, #4
subs r8, r8, #1 ; Decrement the digit counter
bne loop ; If it isn't zero do the next digit.
mov r0, r3 ; Set the output registers. r0 is result. r1 is carry.
mov r1, r7
ldmfd sp!, {r4-r8,pc}
END
</nowiki>
----
*Bubble Sort
<nowiki>
; This is a program to bubble sort a list.
; Rob Frohne and the CPTR 215 class

stack_start EQU 0x40001000
length EQU (end_of_list - begin)
end_ram EQU (datastart + length)
; EQU statements must be at the beginning of the program.
AREA Subroutine_Example, CODE

ENTRY
; Copy list to RAM
ldr sp, =stack_start
LDR R9, =begin
ldr R7, =end_of_list
ldr r6, =datastart
loop
ldr r8,[r9],#4
str r8,[r6],#4
cmp r9,r7
bne loop
; End copy to RAM

start_sort
ldr r5,=(end_ram - 4)
loop_sort_outer ; r2 is our counter, it goes from 1 to item_count
mov r1, #1 ; has_changed = false (0 is true)
ldr r2,=datastart ; start of data in ram goes into r2
loop_sort_inner ;
ldr r3,[r2],#4
ldr r4,[r2],#0
cmp r3,r4
blhi subroutine_swap ; swaps the last two and sets has_changed
cmp r2,r5
bne loop_sort_inner
sub r5,r5,#4 ; the last item is in order, so we don't need to check it again.
cmp r1,#1
bne loop_sort_outer
stop b stop

subroutine_swap ; swaps the contents of the addresses held in
; r2 and r2 -4 (the previous address)
; has_changed is r1 and it sets it to 0 (true)
stmfd sp!, {r0,r2-r4,lr}
mov r1,#0 ; setting the has_changed to true.
ldr r0,[r2],#-4 ; swapping data
ldr r3,[r2]
str r0,[r2],#4
str r3,[r2]
ldmfd sp!, {r0,r2-r4,pc}
ALIGN
begin
DCD 0x8fffffff, 0x55555555, 0x44444444, 0x77777777, 0xffffffff
end_of_list
AREA Thedata, DATA, NOINIT, READWRITE
datastart SPACE 20
END
</nowiki>
----
*GPIO Example
<nowiki>
;---------------------------------------------------------------------------
;
; Programmer : Larry Aamodt
;
; File name : shell_2148.s
; Class : CPTR-215
; Language : ARM assembly
; Assembler : Keil
; Target MCU : NXP LPC-2148 on Embedded Artists board
; Date Written: 11/30/09
; change history: 11/30/09 LDA Updated with hardware start-up
; 12/03/09 LDA PWM register definitions added
; Description :
;
; Inputs :
;
; Outputs :
;
; Special :
; requirements
;
;
; NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F

I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled

; Memory addresses for standard GPIO definitions
IO0PIN EQU 0xE0028000
IO0SET EQU 0xE0028004
IO0DIR EQU 0xE0028008
IO0CLR EQU 0xE002800C
; Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR EQU 0xE0014004 ; PWM_TCR Timer Control Register
PWM_PR EQU 0xE001400C ; PWM_PR Prescaler Register
PWM_MCR EQU 0xE0014014 ; PWM_MCR Match Control Register
PWM_MR0 EQU 0xE0014018 ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4 EQU 0xE0014040 ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6 EQU 0xE0014048 ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR EQU 0xE001404C ; PWM_CR Control Register
PWM_LER EQU 0xE0014050 ; PWM_LER Latch Enable Register

PINSEL0 EQU 0xE002C000 ; Pin connect block - port 0
PINSEL1 EQU 0xE002C004 ; Pin connect block - port 1

; Stack size definitions
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400

ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.

AREA RESET, CODE, READONLY
ARM


; Exception Vectors
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler

; Reset Handler

EXPORT Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR R0, =Stack_Top

; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size

; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size

; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size

; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size

; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size

; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR

MOV SP, R0
SUB SL, SP, #USR_Stack_Size


; User program code goes here

main
ldr r1, =PINSEL0
mov r0, #0 ; set P0.0 to P0.15 as GPIO
str r0,[r1]
ldr r1, =IO0DIR ; Get ready to set direction of data
ldr r0, =0xffff ; output
str r0, [r1] ; set the direction
ldr r1, =IO0PIN ; load the address of the port0
mov r0, #0
next
str r0, [r1]
add r0, r0, #1
ldr r2, =0x200
delay
subs r2,r2,#1
bne delay
b next


AREA Thedata, DATA, READWRITE
ALIGN
datastart EQU 0x40000004
SPACE 20
;FILL 20, 0xff ; Why is this stored at 0x50 instead of 0x58?
; Or maybe a better question: How do I set data at
; a specific memory location?

END
</nowiki>

----

* LCD Example
<nowiki>
;---------------------------------------------------------------------------
;
; Programmer : Larry Aamodt
;
; File name : shell_2148.s
; Class : CPTR-215
; Language : ARM assembly
; Assembler : Keil
; Target MCU : NXP LPC-2148 on Embedded Artists board
; Date Written: 11/30/09
; change history: 11/30/09 LDA Updated with hardware start-up
; 12/03/09 LDA PWM register definitions added
; Description :
;
; Inputs :
;
; Outputs :
;
; Special :
; requirements
;
;
; NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F

I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled

; Memory addresses for standard GPIO definitions
IO0PIN EQU 0xE0028000
IO0SET EQU 0xE0028004
IO0DIR EQU 0xE0028008
IO0CLR EQU 0xE002800C
; Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR EQU 0xE0014004 ; PWM_TCR Timer Control Register
PWM_PR EQU 0xE001400C ; PWM_PR Prescaler Register
PWM_MCR EQU 0xE0014014 ; PWM_MCR Match Control Register
PWM_MR0 EQU 0xE0014018 ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4 EQU 0xE0014040 ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6 EQU 0xE0014048 ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR EQU 0xE001404C ; PWM_CR Control Register
PWM_LER EQU 0xE0014050 ; PWM_LER Latch Enable Register

PINSEL0 EQU 0xE002C000 ; Pin connect block - port 0
PINSEL1 EQU 0xE002C004 ; Pin connect block - port 1

; Stack size definitions
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400

ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.

AREA RESET, CODE, READONLY
ARM


; Exception Vectors
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler

; Reset Handler

EXPORT Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR R0, =Stack_Top

; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size

; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size

; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size

; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size

; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size

; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR

MOV SP, R0
SUB SL, SP, #USR_Stack_Size


; User program code goes here

main
bl lcdReset ; set up the lcd
bl lcdClear
mov r0, #32
loop
bl lcdWRdata ; write to the LCD
add r0, r0, #1 ; increment the data
mov r9, r0
ldr r0, =1000000
bl delay
mov r0, r9
b loop

; User data area definition follows

AREA appdata, DATA, NOINIT, READWRITE


;-----------------------------------------------------------------------------
; Copyright (C) 2012 ....
;
; Programmer : Larry Aamodt
;
; File name : lcd_routines.asm
; Language : ARM assembly
; Assembler ; Keil
; Target MCU : NXP LPC2148 on Embedded Artists education board
; Date : 12/05/12 LDA lcd_routines.asm. debugged

;
; Description : LCD display routines
; Copy this code to your own program
;
; Procedures you can call are:
; lcdReset must be called first. no parameters.
; lcdClear use to clear the display & home the cursor
; no parameters are required.
; lcdWRdata use to write one character on the display
; place an ASCII char in r0 before calling.
; The cursor will advance.
; lcdCursorAt use to move the cursor. Two parameters
; r0 = row (1 or 2)
; r1 = column to move cursor to (0-15)
;
; Example use of the LCD routines:
; bl lcdReset ; do this once at the top of your program
;
; mov r0,#31h ; place the ascii code for number one in r0
; bl lcdWRdata ; display the character '1' on LCD display
;
; bl lcdClear ; clear the display
;
; mov r0,#1 ; select LCD row 1
; mov r1,#4 ; select LCD column 4
; bl lcdCursorAt ; move the cursor
;
;
; NOTE: All registers are saved by these routines, i.e. upon return
; from the subroutine all registers, except flags, have the
; same value in them as before the subroutine was called (this
; breaks with the ARM convention for subroutines but is done
; to make it easier for you).
;
;-----------------------------------------------------------------------------

AREA lcdroutines,CODE,READONLY

EXPORT lcdReset
EXPORT lcdClear
EXPORT lcdWRdata
EXPORT lcdCursorAt
EXPORT delay

; -----------------------------------------------------------------------------
; LCD Procedures follow.
;------------------------------------------------------------------------------
; ***** LCD equates ******
;IO0PIN EQU 0xE0028000
;IO0SET EQU 0xE0028004
;IO0DIR EQU 0xE0028008
;IO0CLR EQU 0xE002800C

IO1PIN EQU 0xE0028010
IO1SET EQU 0xE0028014
IO1DIR EQU 0xE0028018
IO1CLR EQU 0xE002801C

T0_TCR_ADDR EQU 0xE0004004
T0_PR_ADDR EQU 0xE000400C
T0_MCR_ADDR EQU 0xE0004014
T0_MR0_ADDR EQU 0xE0004018
; -----------------------------------------------------------------------------
; Procedure lcdReset Must be called before writing to the LCD
; No parameters required
; -----------------------------------------------------------------------------
lcdReset STMFD SP!,{LR,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9}

LDR r1,=T0_PR_ADDR ; Prescale register
MOV r2,#2 ; prescale count value
STR r2,[r1] ; Prescaler will divide by 3

LDR r1,=T0_MCR_ADDR ; Match control register
MOV r2,#0x6 ; bit 2 = one
STR r2,[r1] ; stop counter when match reached

LDR r5,=IO0CLR
LDR r6,=IO0SET
LDR r7,=IO1CLR
LDR r8,=IO1SET
LDR r0,=35000
BL delay ; delay 35ms

LDR r0,=IO0DIR
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=0x40408000 ; one's for pins to be set for output
ORR r4,r4,r3 ; bits 22 & 30 to be outputs
STR r4,[r0] ; set lcd port bits to output
LDR r3,=0x00400000 ;
STR r3,[r5] ; write 0 to LCD R/W signal
LDR r3,=0x40008000
STR r3,[r6] ; turn on backlight - write 1 to P0.30

LDR r0,=IO1DIR
LDR r4,[r0] ; read current Port 1 direction bits
LDR r3,=0x03FF0000 ; one's for lcd pins - set for output
ORR r4,r4,r3 ; bits 16 to 25 to be outputs
STR r4,[r0] ; set lcd port direction bits to output
STR r3,[r7] ; clear the LCD data & control bits

LDR r9,=0x02000000 ; bit pattern to turn on/off E

LDR r2,=0x00380000 ; function: 2 lines, 5x7 dots
STR r2,[r8] ; set lcd function bits
BL pulse_e
STR r3,[r7] ; clear lcd bits

MOV r0,#40
BL delay ; wait 40 microseconds

LDR r2,=0x000E0000 ; function: display on, cursor on
STR r2,[r8] ; set lcd function bits
BL pulse_e
STR r3,[r7] ; clear lcd bits

MOV r0,#40
BL delay ; wait 40 microseconds
LDR r2,=0x00010000 ; lcd function: clear display
STR r2,[r8] ; set lcd function bits
BL pulse_e
STR r3,[r7] ; clear lcd bits

MOV r0,#1600
BL delay ; delay for 1600 microseconds

LDR r2,=0x00030000 ; lcd function: set entry mode
STR r2,[r8] ; set lcd function bits
BL pulse_e
STR r3,[r7] ; clear lcd bits

end_lcdReset LDMFD SP!,{PC,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9}

;------------------------------------------------------------------------------
; Procedure pulse_e uses r0. Not interuptable (r14 not saved)
; assumes r9 has 0x02000000 in it
; assumes r7 has IO1CLR address in it
; assumes r8 has IO1SET address in it
;------------------------------------------------------------------------------
pulse_e MOV r0,#4
STR r9,[r8] ; assert E
ploop1 SUBS r0,r0,#1 ; delay 600ns or so
BNE ploop1
STR r9,[r7] ; de-assert E
MOV r0,#8
ploop2 SUBS r0,r0,#1 ; delay 1100ns or so
BNE ploop2
end_pulse_e BX LR
; -----------------------------------------------------------------------------
; Procedure lcdClear Clears the LCD and positions cursor to line1, left col
; no parameters required
; -----------------------------------------------------------------------------
lcdClear STMFD SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

LDR r9,=0x02000000 ; bit pattern to turn on/off E
LDR r3,=0x03FF0000 ; one's for pins used for lcd
LDR r7,=IO1CLR
LDR r8,=IO1SET

LDR r2,=0x00010000 ; lcd function: clear display
STR r2,[r8] ; set lcd function bits
BL pulse_e
STR r3,[r7] ; clear lcd bits

MOV r0,#1600
BL delay

end_lcdClear LDMFD SP!,{PC,r0,r1,r2,r3,r7,r8,r9}
; -----------------------------------------------------------------------------
; Procedure lcdCursorAt
; r0 assumed to contain the row# (1 or 2)
; r1 assumed to contain col#(0-15)
; No cursor movement if invalid row or col # is found
; -----------------------------------------------------------------------------

lcdCursorAt STMFD SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

CMP r1,#15 ; check column & row range
BHI end_lcdCursorAt
CMP r0,#0
BEQ end_lcdCursorAt
CMP r0,#2
BHI end_lcdCursorAt
ADDEQ r1,r1,#0x40 ; build the cursor address
LDR r0,=0x00800000
ORR r2,r0,r1,LSL #16 ; r2 now has the address

LDR r3,=0x03FF0000 ; one's for pins used for lcd
LDR r7,=IO1CLR
LDR r8,=IO1SET
LDR r9,=0x02000000 ; bit pattern to turn on/off E

STR r2,[r8] ; set lcd function bits
BL pulse_e
STR r3,[r7] ; clear lcd bits

MOV r0,#40
BL delay

end_lcdCursorAt LDMFD SP!,{PC,r0,r1,r2,r3,r7,r8,r9}

; -----------------------------------------------------------------------------
; Procedure lcdWRdata Writes data (an ASCII character) to the
; current cursor position on the LCD
; r0 is assumed to contain the character to send
; -----------------------------------------------------------------------------
lcdWRdata STMFD SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

LDR r9,=0x02000000 ; bit pattern to turn on/off E
LDR r3,=0x03FF0000 ; one's for pins used for lcd
LDR r7,=IO1CLR
LDR r8,=IO1SET

AND r0,r0,#0xFF ; make sure only 8 bits are non-zero
ORR r0,r0,#0x100 ; set bit that indicates writing
LSL r0,r0,#16
STR r0,[r8] ; write data
BL pulse_e
STR r3,[r7] ; clear lcd bits

end_lcdWRData LDMFD SP!,{PC,r0,r1,r2,r3,r7,r8,r9}

; -----------------------------------------------------------------------------
; Procedure delay microsecond delay using timer 0
; (12Mhz clock & VPB divide by 4 assumed)
; reg R0 must contain a delay value in uS
; Not interruptable (r14 not saved)
; assumes lcdReset has been called prior to delay
; r0, r1 not preserved
; -----------------------------------------------------------------------------

delay LDR r1,=T0_MR0_ADDR ; Match register zero
STR r0,[r1] ; load match count per R0

; ldr r0,=0x8000
; str r0,[r5] ; turn on p0.15 led

LDR r1,=T0_TCR_ADDR ; Timer 0 Control Register
MOV r0,#2 ; bit 1 = one
STR r0,[r1] ; Clear counter & prescaler
MOV r0,#0
STR r0,[r1]

MOV r0,#1 ; bit 0 = one
STR r0,[r1] ; Turn on counter

dloop LDR r0,[r1] ; Read TCR register
ANDS r0,r0,#1
BNE dloop

; ldr r0,=0x8000
; str r0,[r6] ; turn off p0.15 led

end_delay BX LR ; Return

END
</nowiki>

* PWM Example for running the servos on the robots
<nowiki>
;---------------------------------------------------------------------------
;
; Programmer : Larry Aamodt
;
; File name : shell_2148.s
; Class : CPTR-215
; Language : ARM assembly
; Assembler : Keil
; Target MCU : NXP LPC-2148 on Embedded Artists board
; Date Written: 11/30/09
; change history: 11/30/09 LDA Updated with hardware start-up
; 12/03/09 LDA PWM register definitions added
; Description :
;
; Inputs :
;
; Outputs :
;
; Special :
; requirements
;
;
; NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F

I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled

; Memory addresses for standard GPIO definitions
IO0PIN EQU 0xE0028000
IO0SET EQU 0xE0028004
IO0DIR EQU 0xE0028008
IO0CLR EQU 0xE002800C
; Memory addresses for Timer/Counter
T1TCR EQU 0xE0008004
T1CTCR EQU 0xE0008070
T1PR EQU 0xE000800C

T1MCR EQU 0xE0008014
T1EMR EQU 0xE000803C


; Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR EQU 0xE0014004 ; PWM_TCR Timer Control Register
PWM_PR EQU 0xE001400C ; PWM_PR Prescaler Register
PWM_MCR EQU 0xE0014014 ; PWM_MCR Match Control Register
PWM_MR0 EQU 0xE0014018 ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4 EQU 0xE0014040 ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6 EQU 0xE0014048 ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR EQU 0xE001404C ; PWM_CR Control Register
PWM_LER EQU 0xE0014050 ; PWM_LER Latch Enable Register

PINSEL0 EQU 0xE002C000 ; Pin connect block - port 0
PINSEL1 EQU 0xE002C004 ; Pin connect block - port 1

; Stack size definitions
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400

ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.

AREA RESET, CODE, READONLY
ARM


; Exception Vectors
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler

; Reset Handler

EXPORT Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR R0, =Stack_Top

; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size

; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size

; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size

; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size

; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size

; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR

MOV SP, R0
SUB SL, SP, #USR_Stack_Size


; User program code goes here

main
BL pwm_init
mov r0, #3
ldr r1, =1600
mov r2, r1
bl pwm_update
stop b stop


;*****************************************************************************
; Pulse width modulator initialization routine.
; pulse period = 20ms; initial pulse length = 1.5ms
; (values shown are for controlling model plane servos)
; Inputs: none
; Returned values: none
; Uses registers r0, r1
;
; Calling method: BL pwm_init
; LDA rev 1.0 12/01/10
; Rob Frohne rev 1.1 12/3/13
;*****************************************************************************
; Stepper motor connections on the Board of Education are from page 24 of the board
; Users Guide. A stepper motor operation is explained at:
; http://www.haydonkerk.com/Resources/StepperMotorTheory/tabid/192/Default.aspx
;
; PWM is explained in Chapter 16 (page 253 and following) of the reference manual.
; Note the registers in the reference manual are labeled without the _, i.e.
; PWMPR instead of PWM_PR.

pwm_init STMFD sp!,{r14} ; save link register on the stack

LDR r0,=PWM_PR
LDR r1,=2 ; prescaler = 2 divides PCLK by 3
STR r1,[r0] ; load prescaler
; PCLK rate is 1 MHZ (1 uSEC period) after division.
LDR r0,=PWM_MCR ; PWMMCR PWM Match Control Register.
; The PWMMCR is used to control if an

; interrupt is generated and if the PWMTC
; is reset when a Match occurs. See page 261 of the manual.
LDR r1,=0x02 ; bit 1 is set, others are zero
STR r1,[r0] ; load PWM_MCR: PWM_TC resets when PWM_TC=PWM_MR0

LDR r0,=PWM_MR0
LDR r1,=20000 ; count 20,000 1uSEC intervals = 20 milli secs
STR r1,[r0] ; load match register 0. sets pulse period. Page 261.

LDR r0,=PWM_MR4
LDR r1,=1500 ; initial pulse width = 1.5 milli secs
STR r1,[r0] ; load match register 4. Page 261
LDR r0,=PWM_MR6
LDR r1,=1500 ; initial pulse width
STR r1,[r0] ; load match register 6

LDR r0,=PWM_LER
LDR r1,=0x51
STR r1,[r0] ; Enable latch registers 0, 4, & 6

LDR r0,=PWM_PCR
LDR r1,=0x5000 ; bits 12 & 14 set
STR r1,[r0] ; load PWM_CR: enables PWM4 and PWM6 outputs

LDR r0,=PWM_TCR
LDR r1,=0x09 ; bits 3,1,0 set
STR r1,[r0] ; load PWM_TCR enable PWM counter & prescaler

LDR r0,=PINSEL0
LDR r1,=0xA0000 ; bits 19 & 17 set. selects output PWM 6 & 4
STR r1,[r0] ; Enable PWM6 & PWM4 outputs

end_pwm_init LDMFD sp!,{pc} ; Return from subroutine pwm_init


;*****************************************************************************
; Pulse width modulator update routine
; Inputs: r0 = command
; 1 = update PWM channel 4
; 2 = update PWM channel 6
; 3 = update both channel 4 and 6
; r1 = new pulse width (# of microseconds) for PWM channel 4
; r2 = new pulse width (# of microseconds) for PWM channel 6
; Returned values: none
; Uses registers r0, r1, r2, r3
;
; Calling method: first put values in r0, r1, r2 as appropriate
; BL pwm_update
; LDA rev 2.0 12/01/11
;*****************************************************************************

pwm_update STMFD sp!,{r14} ; r0 specifies channels to update

load_pwm4 CMP r0,#1 ; 1 = load PWM chan 4
BNE load_pwm6
LDR r3,=PWM_MR4
STR r1,[r3] ; load match register 4

load_pwm6 CMP r0,#2 ; 2 = load PWM chan 6
BNE load_both
LDR r3,=PWM_MR6
STR r2,[r3] ; load match register 6

load_both CMP r0,#3
BNE pwm_reg_update
LDR r3,=PWM_MR4
STR r1,[r3]
LDR r3,=PWM_MR6
STR r2,[r3]

pwm_reg_update CMP r0,#1 ; check for just pwm 4 update
MOVEQ r1,#0x10
CMP r0,#2 ; check for just pwm 6 update
MOVEQ r1,#0x40
CMP r0,#3 ; check for pwm 4 & 6 update
MOVEQ r1,#0x50

LDR r3,=PWM_LER
STR r1,[r3] ; enable latch registers

end_pwm_update LDMFD sp!,{pc} ; Return from subroutine pwm_update

; User data area definition follows

AREA appdata, DATA, NOINIT, READWRITE

END
</nowiki>

* Stepper Motor on the Embedded Artists Board of Education
<nowiki>

;---------------------------------------------------------------------------
;
; Programmer : Larry Aamodt
; : Modifications for propeller drive: Rob Frohne
;
; File name : shell_2148.s
; Class : CPTR-215
; Language : ARM assembly
; Assembler : Keil
; Target MCU : NXP LPC-2148 on Embedded Artists board
; Date Written: 11/30/09
; change history: 11/30/09 LDA Updated with hardware start-up
; 12/03/09 LDA PWM register definitions added
; Description :
;
; Inputs :
;
; Outputs :
;
; Special :
; requirements
;
;
; NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F

I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled

; Memory addresses for standard GPIO definitions
IO0PIN EQU 0xE0028000
IO0SET EQU 0xE0028004
IO0DIR EQU 0xE0028008
IO0CLR EQU 0xE002800C

; Timer addresses
T0_TCR_ADDR EQU 0xE0004004
T0_PR_ADDR EQU 0xE000400C
T0_MCR_ADDR EQU 0xE0004014
T0_MR0_ADDR EQU 0xE0004018

; Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR EQU 0xE0014004 ; PWM_TCR Timer Control Register
PWM_PR EQU 0xE001400C ; PWM_PR Prescaler Register
PWM_MCR EQU 0xE0014014 ; PWM_MCR Match Control Register
PWM_MR0 EQU 0xE0014018 ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4 EQU 0xE0014040 ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6 EQU 0xE0014048 ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR EQU 0xE001404C ; PWM_CR Control Register
PWM_LER EQU 0xE0014050 ; PWM_LER Latch Enable Register

PINSEL0 EQU 0xE002C000 ; Pin connect block - port 0
PINSEL1 EQU 0xE002C004 ; Pin connect block - port 1

; Stack size definitions
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400

ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.

AREA RESET, CODE, READONLY
ARM


; Exception Vectors
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler

; Reset Handler

EXPORT Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR R0, =Stack_Top

; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size

; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size

; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size

; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size

; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size

; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR

MOV SP, R0
SUB SL, SP, #USR_Stack_Size


; User program code goes here

quarter_period EQU 250000 ; uS
main
BL init_propeller
mov r1, #1 ; for handy access
mov r2, #0 ; for handy access

mov r0, r1, LSL #12 ; turn one on
mov r4, r2, LSL #21 ; two still off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

mov r0, r1, LSL #12
mov r4, r1, LSL #21
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

mov r0, r2, LSL #12 ; turn one off
mov r4, r1, LSL #21 ; two still on
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay

mov r0, r2, LSL #12 ; turn one off
mov r4, r2, LSL #21 ; turn two off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
ldr r0,=quarter_period
bl delay
b main

; -----------------------------------------------------------------------------
; Procedure init_propeller Must be called before to initialize the propeller.
; Set up the counter T0 and GPIO P0.12 and P0.21 for output.
; No parameters required
; -----------------------------------------------------------------------------
init_propeller STMFD SP!,{LR,r0,r1,r2,r3,r4}

LDR r1,=T0_PR_ADDR ; Prescale register
MOV r2,#2 ; prescale count value
STR r2,[r1] ; Prescaler will divide by 3 (1 uS period)

LDR r1,=T0_MCR_ADDR ; Match control register
MOV r2,#0x4 ; bit 2 = one: Stop on MR0: the TC and PC
; will be stopped and TCR[0] will be set to 0 if
; MR0 matches the TC
STR r2,[r1] ; stop counter when match reached

LDR r0,=IO0DIR
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=2101248 ; one's for pins to be set for output (2^12+2^21 = 2101248)
; 2134016 = 2^12+2^21+2^15 (for debugging LED on P0.15
ORR r4,r4,r3
STR r4,[r0] ; set propeller port bits to output without changing others.
LDR r0,=PINSEL0
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=50331648 ; zero's for pins to be set for output (2^24+2^25 = 50331648)
BIC r4,r4,r3 ;
STR r4,[r0] ; set propeller port P0.12 to GPIO without changing others.

LDR r0,=PINSEL1
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=3072 ; zero's for pins to be set for output (2^10+2^11 = 3072)
BIC r4,r4,r3 ;
STR r4,[r0] ; set propeller port P0.21 to GPIO without changing others.
end_init_propeller LDMFD SP!,{PC,r0,r1,r2,r3,r4}



; -----------------------------------------------------------------------------
; Procedure delay microsecond delay using timer 0
; (12Mhz clock & VPB divide by 4 assumed)
; reg R0 must contain a delay value in uS
; Not interruptable (r14 not saved)
; assumes lcdReset has been called prior to delay
; r0, r1 not preserved
; -----------------------------------------------------------------------------

delay STMFD SP!,{LR,r0,r1}
LDR r1,=T0_MR0_ADDR ; Match register zero
STR r0,[r1] ; load match count per R0

; ldr r0,=0x8000
; str r0,[r5] ; turn on p0.15 led

LDR r1,=T0_TCR_ADDR ; Timer 0 Control Register
MOV r0,#2 ; bit 1 = one
STR r0,[r1] ; Clear counter & prescaler
MOV r0,#0
STR r0,[r1]

MOV r0,#1 ; bit 0 = one
STR r0,[r1] ; Turn on counter

dloop LDR r0,[r1] ; Read TCR register
ANDS r0,r0,#1
BNE dloop

; ldr r0,=0x8000
; str r0,[r6] ; turn off p0.15 led

end_delay LDMFD SP!,{PC,r0,r1} ; Return

END
</nowiki>

* Propeller Drive Using Interrupts (not polled)
<nowiki>

;---------------------------------------------------------------------------
;
; Shell Programmer : Larry Aamodt, main: Rob Frohne
;
;
; File name : shell_2148.s
; Class : CPTR-215
; Language : ARM assembly
; Assembler : Keil
; Target MCU : NXP LPC-2148 on Embedded Artists board
; Date Written: 11/30/09
; change history: 11/30/09 LDA Updated with hardware start-up
; 12/03/09 LDA PWM register definitions added
; Description :
;
; Inputs :
;
; Outputs :
;
; Special :
; requirements
;
;
; NOTES:
;
;
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F

I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled

; Memory addresses for standard GPIO definitions
IO0PIN EQU 0xE0028000
IO0SET EQU 0xE0028004
IO0DIR EQU 0xE0028008
IO0CLR EQU 0xE002800C

; Vector Interrupt Controller Addresses
VICIntSelect EQU 0xFFFFF00C
VICVectAddr4 EQU 0xFFFFF110
VICVectCntl4 EQU 0xFFFFF210
VICIntEnable EQU 0xFFFFF010
VICVectAddr EQU 0xFFFFF030


; Timer addresses
T0PC EQU 0xE0004010 ; Timer 0 Prescale counter
T0IR EQU 0xE0004000 ; Timer 0 Interrupt Register
T0_TCR_ADDR EQU 0xE0004004
T0_PR_ADDR EQU 0xE000400C
T0_MCR_ADDR EQU 0xE0004014
T0_MR0_ADDR EQU 0xE0004018

; Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR EQU 0xE0014004 ; PWM_TCR Timer Control Register
PWM_PR EQU 0xE001400C ; PWM_PR Prescaler Register
PWM_MCR EQU 0xE0014014 ; PWM_MCR Match Control Register
PWM_MR0 EQU 0xE0014018 ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4 EQU 0xE0014040 ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6 EQU 0xE0014048 ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR EQU 0xE001404C ; PWM_CR Control Register
PWM_LER EQU 0xE0014050 ; PWM_LER Latch Enable Register

PINSEL0 EQU 0xE002C000 ; Pin connect block - port 0
PINSEL1 EQU 0xE002C004 ; Pin connect block - port 1

; Stack size definitions
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400

ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)

AREA STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.

AREA RESET, CODE, READONLY
ARM


; Exception Vectors
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

; Exception Handlers
Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler

; Reset Handler

EXPORT Reset_Handler
Reset_Handler

; Setup Stack for each mode

LDR R0, =Stack_Top

; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #UND_Stack_Size

; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size

; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size

; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size

; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size

; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR
MOV SP, R0
SUB SL, SP, #USR_Stack_Size

step_time_uS EQU 250000 ; uS between steps of the propeller motor.

main BL init_propeller ; Initialize GPIO and IRQ, and Timer 0.

LDR r0,=step1
LDR r1,=NextRoutine_Address
STR r0,[r1]
bl delay ; start the whole automated thing

do_nothing b do_nothing ; because the ISR will do it all!

; -----------------------------------------------------------------------------
; Step the motor routines. These are called by the Timer0 ISR (timer0ISR)
; -----------------------------------------------------------------------------

step1 STMFD SP!,{LR,r0-r12}
mov r1, #1 ; for handy access
mov r2, #0 ; for handy access
mov r0, r1, LSL #12 ; turn one on
mov r4, r2, LSL #21 ; two still off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step2
LDR r1,=NextRoutine_Address
STR r0,[r1]
bl delay
LDMFD SP!,{PC,r0-r12}

step2 STMFD SP!,{LR,r0-r12}
mov r1, #1 ; for handy access
mov r2, #0 ; for handy access
mov r0, r1, LSL #12
mov r4, r1, LSL #21
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step3
LDR r1,=NextRoutine_Address
STR r0,[r1]
bl delay
LDMFD SP!,{PC,r0-r12}

step3 STMFD SP!,{LR,r0-r12}
mov r1, #1 ; for handy access
mov r2, #0 ; for handy access
mov r0, r2, LSL #12 ; turn one off
mov r4, r1, LSL #21 ; two still on
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step4
LDR r1,=NextRoutine_Address
STR r0,[r1]
bl delay
LDMFD SP!,{PC,r0-r12}

step4 STMFD SP!,{LR,r0-r12}
mov r1, #1 ; for handy access
mov r2, #0 ; for handy access
mov r0, r2, LSL #12 ; turn one off
mov r4, r2, LSL #21 ; turn two off
orr r0, r4, r0 ; both on
ldr r3,=IO0PIN
str r0,[r3]
LDR r0,=step1
LDR r1,=NextRoutine_Address
STR r0,[r1]
bl delay
LDMFD SP!,{PC,r0-r12}

; -----------------------------------------------------------------------------
; Procedure init_propeller Must be called before to initialize the propeller.
; Set up GPIO P0.12 and P0.21 for output.
; Set up timer 0 and interrupts for IRQ (low priority)
; No parameters required
; -----------------------------------------------------------------------------

init_propeller STMFD SP!,{LR,r0,r1,r2,r3,r4}
LDR r1,=T0_PR_ADDR ; Prescale register
MOV r2,#2 ; prescale count value
STR r2,[r1] ; Prescaler will divide by 3 (1 uS period

; T0MCR = 0x00000003; //reset counter and generate IRQ on MR0 match

LDR r1,=T0_MCR_ADDR ; Match control register
MOV r2,#0x3 ; bit 2 = one: Stop on MR0: the TC and PC : bit 1 = 1 gives interrupt
; will be stopped and TCR[0] will be set to 0 if
; MR0 matches the TC
STR r2,[r1] ; stop counter when match reached

; VICIntSelect &= ~0x10; //Timer0 interrupt is assigned to IRQ (not FIQ)
LDR r1,=VICIntSelect
MOV r0,#~0x10
STR r0,[r1]

; VICVectAddr4 = (tU32)timer0ISR; //register ISR address
LDR r1,=VICVectAddr4
LDR r0,=timer0ISR
STR r0,[r1]

; VICVectCntl4 = 0x24; //enable vector interrupt for timer0
LDR r1,=VICVectCntl4
MOV r0,#0x24
STR r0,[r1]

; VICIntEnable = 0x10; //enable timer0 interrupt
LDR r1,=VICIntEnable
MOV r0,#0x10
STR r0,[r1]

LDR r0,=IO0DIR
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=2101248 ; one's for pins to be set for output (2^12+2^21 = 2101248)
; 2134016 = 2^12+2^21+2^15 (for debugging LED on P0.15
ORR r4,r4,r3
STR r4,[r0] ; set propeller port bits to output without changing others

LDR r0,=PINSEL0
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=50331648 ; zero's for pins to be set for output (2^24+2^25 = 50331648)
BIC r4,r4,r3 ;
STR r4,[r0] ; set propeller port P0.12 to GPIO without changing others.

LDR r0,=PINSEL1
LDR r4,[r0] ; read current Port 0 direction bits
LDR r3,=3072 ; zero's for pins to be set for output (2^10+2^11 = 3072)
BIC r4,r4,r3 ;
STR r4,[r0] ; set propeller port P0.21 to GPIO without changing others.

end_init_propeller LDMFD SP!,{PC,r0,r1,r2,r3,r4}

; -----------------------------------------------------------------------------
; Timer 0 Interrupt Service Routine timer0ISR
; Some comments are C equivalents.
; -----------------------------------------------------------------------------

timer0ISR SUB LR,LR,#4 ; Update the LR
STMFD SP!, {r0-r12,LR} ; Store registers (including LR)

; T0IR = 0xff; //reset all IRQ flags
LDR r1,=T0IR
MOV r0,#~0xff
STR r0,[r1]

; VICVectAddr = 0x00; //dummy write to VIC to signal end of interrupt
LDR r1,=VICVectAddr
MOV r0,#~0x00
STR r0,[r1]

; Here we will branch to the appropriate next step routine.
LDR r0,=NextRoutine_Address
push {r14} ; save LR for this return.
ldr r14,=end_timer0ISR ; load the LR because we are using an LDR PC below.
ldr PC,[r0] ; branch to the subroutine pointed to by NextRoutine_Address
end_timer0ISR pop {r14} ; restore the LR for this return.
LDMFD SP!, {r0-r12,PC}^ ; Return from ISR

; -----------------------------------------------------------------------------
; Procedure delay microsecond delay using timer 0
; (12Mhz clock & VPB divide by 4 assumed)
; Delay is set by step_time_uS
; -----------------------------------------------------------------------------

delay STMFD SP!,{LR,r0,r1}


; T0PC = 0x00000000; Didn't do this yet. Unnecessary? //no prescale of clock

; LDR r1,=T0PC
; MOV r0,#0x00000000
; STR r0,[r1]

; T0IR = 0x000000ff; //reset all flags before enable IRQs

LDR r1,=T0IR
MOV r0,#0x000000ff
STR r0,[r1]

; T0MR0 = delayInMs * //calculate no of timer ticks
; ((CRYSTAL_FREQUENCY * PLL_FACTOR) / (1000 * VPBDIV_FACTOR));
LDR r1,=T0_MR0_ADDR ; Match register zero
LDR r0,=step_time_uS
STR r0,[r1] ; load match count from step_time_uS

; T0TCR = 0x00000002; //disable and reset Timer0
LDR r1,=T0_TCR_ADDR ; Timer 0 Control Register
MOV r0,#2 ; bit 1 = one
; T0TCR = 0x00000001; //start Timer0
STR r0,[r1] ; Clear counter & prescaler
MOV r0,#0
STR r0,[r1]

MOV r0,#1 ; bit 0 = one
STR r0,[r1] ; Turn on counter

end_delay LDMFD SP!,{PC,r0,r1} ; Return

AREA Thedata, DATA, READWRITE
ALIGN
NextRoutine_Address SPACE 4 ; This holds the address of the next step subroutine.

END
</nowiki>

Latest revision as of 10:58, 7 November 2014

  • Factorial
; Factorial Calculation
; The answer is in R10

; Rob Frohne 2013

	GLOBAL Reset_Handler 
	AREA Factorial, CODE, READONLY
	ENTRY

Reset_Handler
		movs r8, #0 ; Take the factorial of this number, n.
		mov r10, #1	 ; 0! and 1! are 1
		beq stop   ; If r8 is zero we are done
		cmp r8, #1	 ; If r8 is 1, 
		beq stop	  ; we are done
		subs r9, r8, #1	; n-1
loop	mulne r10, r9, r8 ; n(n-1)
		mov r8, r10
		subs r9, r9, #1
		bne loop
stop 	b stop
		END



  • Factorial 2014
_	AREA Add, CODE
 	ENTRY
	; This program takes the factorial of n (in r2)
n EQU 0

		ldr r2,=n ; Load r2 with the number to take the factorial of.
		cmp r2,#0 ; Is n=0
		bne check_for_1
		mov r4,#1
		b stop
check_for_1
		cmp r2,#1
		bne initialize_r4
		mov r4,#1
		b stop
initialize_r4
		mov r4,r2
		sub r3,r4,#1
loop
		mul r5,r4,r3
		mov r4,r5
		subs r3,r3,#1
		bne loop
stop b stop
	END


  • 64 Bit Add
VAL1A EQU 0xffffffff
VAL1B EQU 0x0000000f
VAL2A EQU 0x00000001
VAL2B EQU 0x00000001
	AREA LongAdd, CODE
	ENTRY
		ldr R0, = VAL1A
		ldr R1, = VAL1B
		ldr R2, = VAL2A
		ldr R3, = VAL2B
		adds R8,R0,R1
		adcs R9,R1,R3
stop 	b stop
	END

  • Copy to RAM
   AREA Exaddress, CODE, READONLY
   EXPORT Reset_Handler
Reset_Handler
   LDR R9, =list
   mov R7, #4 ; number in list
   ldr r6, =datastart 
loop
   ldr r8,[r9],#4
   str r8,[r6],#4
   subs r7,r7,#1
   bne loop
 
stop b stop
   ALIGN
list DCW 0x1111, 0x2222, 0x3333, 0x4444, 0x5555
   ALIGN
string DCB "This is a test.",0 ; This string is not used in the copy to ram program
string2 DCB 'T','h','i' ; This string2 is not used in the copy to ram program
   ALIGN
   AREA Thedata, DATA, NOINIT, READWRITE
datastart SPACE 20
 
   END

  • Subroutine Example1
; First Subroutine Example
; This program demonstrates using a subroutine,
; saving registers on the stack, etc.

; Rob Frohne, 11/3/2013

stack_start EQU 0x40001000
   AREA Subroutine_Example, CODE

   ENTRY

Start
   ldr sp, =stack_start ; Tell where we will place the stack.
                        ; (It goes down (lower addresses from here.)
   mov r1, #1           ; Store some numebers in some registers
   mov r2, #2
   mov r3, #3
   bl subroutine
stop b stop

; This subroutine saves the registers,
; messes up the registers locally,
; then restores the registers and returns.
subroutine
   stmfd sp!, {r1-r2,lr} ; save used registers and the link register (r14)
   mov r1,#0xffffffff    ; mess up the registers
   mov r2, r1
   ldmfd sp!, {r1,r2,pc} ; pop the stack and return 
   END

  • Minimum of a Signed Number
; This program finds the minimum of a list of constant words in signed format
; The result is in r3 at the end of the routine.
; Rob Frohne 10/30/2013

	AREA Program, CODE, READONLY

	ENTRY

	ldr r0,=end	; load the end address of the code & initialize it as the pointer.
	ldr r2,=begin	; load the beginning address of the code.
	mov r3,#0x7fffffff ; Initialize r3 as the most positive number.

loop	
	ldr r6,[r2],#4 ; load the next data into r6 and post increment r2 for the next data.
	cmp r6,r3      ; Find out if r6 > r3. result from r6-r3  like subs r7,r6,r3 without r6 necessary.
	bgt no_update  ; If it is no update.
	mov r3,r6      ; update if r6 is lower than r3.
no_update
	cmp r0,r2      ;  are we at the end yet
	bne loop       ; if r7 != 0 then keep looping

stop b stop
ALIGN
begin
	DCD 0x8fffffff, 0x55555555, 0x44444444, 0x77777777, 0xffffffff
end	
	END 



  • Compare Two Null Terminated Strings
; Subroutine to compare two null terminated ASCII strings.
; The address where the two strings start are in r0 and r1
; The return is in r0, 1 for match and 0 for don't match.
; If they don't match, r1 gives the number of the first 
; character that didn't match, starting with 0.
; r2 and r3 are not protected.
; Rob Frohne 11/6/2013
stack_start EQU 0x40001000
	AREA String_Compare, CODE
    ENTRY
Start
	ldr sp, =stack_start ; Tell where we will place the stack.
                        ; (It goes down (lower addresses from here.)
	ldr r0, =string1
	ldr r1, =string2
	bl compare_strings
stop b stop

compare_strings
	stmfd sp!, {r2,r4,r5,lr} ; save used registers and the link register (r14)
	mov r2, #0 ; Initialize the counter for the first position.
loop
	ldrb r4,[r0],#1
	ldrb r5,[r1],#1
	cmp r5,r4
	bne do_not_match
	cmp r4, #0 ; check for end of string.
	beq match
	add r2, #1
	b loop
do_not_match
	mov r0, #0 ; don't match
	b finish
match
	mov r0, #1
finish
	mov r1,r2 ; Move the count into the result register.	
	ldmfd sp!, {r2,r4,r5,pc} ; pop the stack and return 
string1 
	DCB "This is the first string.",00	; For testing purposes.
	ALIGN
string2 
	DCB "This is the first string.",00
	ALIGN
        END


  • BCD Add
; Subroutine to add two BCD numbers in 32 bit form with each nibble
; representing a digit.
; locals: r8 for digit counter, single digit mask 0xf, r5 & r6 for masked addends,
; working BCD_carry, r7
; working result in r4
; the summed digits go in r3
; inputs: r0+r1
; outputs: r0 result and r1 carry
; only r4-r8 are saved.
; Rob Frohne 11/12/13
stack_start EQU 0x40001000
	AREA ADD_BCD, CODE
    ENTRY
Start
	ldr sp, =stack_start ; Tell where we will place the stack.
                        ; (It goes down (lower addresses from here.)
	ldr r0, =0x12345678	  ; The two numbers to add.
	ldr r1, =0x87654321
	bl bcd_add
stop b stop

bcd_add	; The subroutine
	stmfd sp!, {r4-r8,lr}
	mov r8, #8 ; Set the counter to move through the eight digits
	mov r7, #0 ; Set the carry to zero to start with
	mov r3, #0	; This is where the resulting digits of the sum are stored.
loop
	and r5, r0, #0xf  ; mask for the rightmost digit.
	and r6, r1, #0xf
	add r4, r5, r7 ; add carry
	add r4, r4, r6 ; r4=r5+r6
	subs r4, r4, #10 ; Subtract 10 (base 10) to see if there is a carry.
	bpl carry
	mov r7, #0 ; set carry to zero
	add r4, r4, #10 ; Add the 10 back in as there was no carry needed.
	b roll_to_next_digit
carry
	mov r7, #1 ; set the carry
roll_to_next_digit
	orr r3, r3, r4 ; Add these digits into the sum.
	ror r0, #4
	ror r1, #4
	ror r3, #4
	subs r8, r8, #1	 ; Decrement the digit counter
	bne loop	  ; If it isn't zero do the next digit.
	mov r0, r3 ; Set the output registers. r0 is result. r1 is carry.
	mov r1, r7
	ldmfd sp!, {r4-r8,pc}
	END


  • Bubble Sort
; This is a program to bubble sort a list.
; Rob Frohne and the CPTR 215 class

stack_start EQU 0x40001000
length EQU (end_of_list - begin)
end_ram EQU (datastart + length)
	; EQU statements must be at the beginning of the program.
   AREA Subroutine_Example, CODE

   ENTRY
; Copy list to RAM
   ldr sp, =stack_start
   LDR R9, =begin
   ldr R7, =end_of_list
   ldr r6, =datastart 
loop
   ldr r8,[r9],#4
   str r8,[r6],#4
   cmp r9,r7
   bne loop
; End copy to RAM

start_sort
		ldr r5,=(end_ram - 4)		
loop_sort_outer ; r2 is our counter, it goes from 1 to item_count
		mov r1, #1 ; has_changed = false (0 is true)
		ldr r2,=datastart ; start of data in ram goes into r2
loop_sort_inner ; 
		ldr r3,[r2],#4
		ldr r4,[r2],#0
		cmp r3,r4
		blhi subroutine_swap ; swaps the last two and sets has_changed
		cmp r2,r5
		bne loop_sort_inner
		sub r5,r5,#4 ; the last item is in order, so we don't need to check it again.
		cmp r1,#1
		bne loop_sort_outer					
stop 	b stop

subroutine_swap ; swaps the contents of the addresses held in
                ; r2 and r2 -4 (the previous address)
				; has_changed is r1 and it sets it to 0 (true)
		stmfd sp!, {r0,r2-r4,lr}
		mov r1,#0 ; setting the has_changed to true.
		ldr r0,[r2],#-4  ; swapping data
		ldr r3,[r2]
		str r0,[r2],#4
		str r3,[r2]		
		ldmfd sp!, {r0,r2-r4,pc}
	ALIGN
begin
	DCD 0x8fffffff, 0x55555555, 0x44444444, 0x77777777, 0xffffffff
end_of_list
   AREA Thedata, DATA, NOINIT, READWRITE
datastart SPACE 20
	END 
 

  • GPIO Example
;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
;   File name  	: shell_2148.s 
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09  
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added 
;   Description	: 
;
;   Inputs      :
;   
;   Outputs     :
;
;   Special     :  
;  requirements
;   
;
; 	NOTES: 
;
; 
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C
;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

                AREA    RESET, CODE, READONLY
                ARM


; Exception Vectors
;  Mapped to Address 0.
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;               LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

                EXPORT  Reset_Handler
Reset_Handler   

; Setup Stack for each mode

                LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR

                MOV     SP, R0
                SUB     SL, SP, #USR_Stack_Size


; User program code goes here

main
	ldr r1, =PINSEL0
	mov r0, #0		  ; set P0.0 to P0.15 as GPIO
	str r0,[r1]
	ldr r1, =IO0DIR		; Get ready to set direction of data
	ldr r0, =0xffff		; output
	str r0, [r1] 	;	set the direction
	ldr r1, =IO0PIN	; load the address of the port0
	mov r0, #0
next
	str r0, [r1]
	add r0, r0, #1
	ldr  r2, =0x200
delay
	subs r2,r2,#1
	bne delay	
	b next


	AREA Thedata, DATA, READWRITE
	ALIGN
datastart EQU 0x40000004
	SPACE 20
	 ;FILL 20, 0xff		; Why is this stored at 0x50 instead of 0x58?
	 					; Or maybe a better question:  How do I set data at
						; a specific memory location?

 		END


  • LCD Example
;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
;   File name  	: shell_2148.s 
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09  
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added 
;   Description	: 
;
;   Inputs      :
;   
;   Outputs     :
;
;   Special     :  
;  requirements
;   
;
; 	NOTES: 
;
; 
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C
;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

                AREA    RESET, CODE, READONLY
                ARM


; Exception Vectors
;  Mapped to Address 0.
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;               LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

                EXPORT  Reset_Handler
Reset_Handler   

; Setup Stack for each mode

                LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR

                MOV     SP, R0
                SUB     SL, SP, #USR_Stack_Size


; User program code goes here

main
	bl lcdReset ; set up the lcd
	bl lcdClear
	mov r0, #32
loop
	bl lcdWRdata ; write to the LCD
	add r0, r0, #1 ; increment the data
	mov r9, r0
	ldr r0, =1000000
	bl delay
	mov r0, r9
	b loop

; User data area definition follows

                AREA  appdata, DATA, NOINIT, READWRITE

               

;-----------------------------------------------------------------------------
;	Copyright (C) 2012 ....
;
;   Programmer  : Larry Aamodt
;
; 	File name  	: lcd_routines.asm 
;   Language    : ARM assembly
;   Assembler   ; Keil
; 	Target MCU	: NXP LPC2148 on Embedded Artists education board
;   Date        : 12/05/12   LDA   lcd_routines.asm.  debugged

;    
; 	Description	:	LCD display routines 
;					Copy this code to your own program
;
;                  Procedures you can call are:
;                    lcdReset       must be called first.  no parameters.
;                    lcdClear       use to clear the display & home the cursor
;                                     no parameters are required.
;                    lcdWRdata      use to write one character on the display
;                                     place an ASCII char in r0 before calling.
;                                     The cursor will advance.
;                    lcdCursorAt    use to move the cursor.  Two parameters
;                                     r0 = row (1 or 2)
;                                     r1 = column to move cursor to (0-15)
;
;                  Example use of the LCD routines:
;                    bl    lcdReset   ; do this once at the top of your program
;
;                    mov   r0,#31h    ; place the ascii code for number one in r0
;					 bl    lcdWRdata  ; display the character '1' on LCD display
;
;					 bl    lcdClear   ; clear the display 
;
;                    mov   r0,#1      ; select LCD row 1
;                    mov   r1,#4      ; select LCD column 4
;                    bl    lcdCursorAt ;  move the cursor
;                  
;
;          NOTE:   All registers are saved by these routines, i.e. upon return
;                  from the subroutine all registers, except flags, have the
;                  same value in them as before the subroutine was called (this
;                  breaks with the ARM convention for subroutines but is done 
;                  to make it easier for you).
;
;-----------------------------------------------------------------------------

                    AREA  lcdroutines,CODE,READONLY

					EXPORT	lcdReset
					EXPORT	lcdClear
					EXPORT	lcdWRdata
					EXPORT	lcdCursorAt
					EXPORT	delay

; -----------------------------------------------------------------------------
;  LCD Procedures follow. 
;------------------------------------------------------------------------------
; ***** LCD equates	******
;IO0PIN          EQU     0xE0028000
;IO0SET          EQU     0xE0028004
;IO0DIR          EQU     0xE0028008
;IO0CLR          EQU     0xE002800C

IO1PIN          EQU     0xE0028010
IO1SET          EQU     0xE0028014
IO1DIR          EQU     0xE0028018
IO1CLR          EQU     0xE002801C

T0_TCR_ADDR		EQU		0xE0004004
T0_PR_ADDR		EQU		0xE000400C
T0_MCR_ADDR		EQU		0xE0004014
T0_MR0_ADDR		EQU		0xE0004018
; -----------------------------------------------------------------------------
;   Procedure lcdReset        Must be called before writing to the LCD
;                             No parameters required
; -----------------------------------------------------------------------------
lcdReset			STMFD	SP!,{LR,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9}

					LDR		r1,=T0_PR_ADDR			; Prescale register 
					MOV		r2,#2 					; prescale count value
					STR		r2,[r1]					; Prescaler will divide by 3

					LDR		r1,=T0_MCR_ADDR			; Match control register
					MOV		r2,#0x6					; bit 2 = one
					STR		r2,[r1]					; stop counter when match reached

					LDR		r5,=IO0CLR
					LDR		r6,=IO0SET
					LDR		r7,=IO1CLR
					LDR		r8,=IO1SET
					
					LDR		r0,=35000
					BL		delay			; delay 35ms

					LDR		r0,=IO0DIR
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=0x40408000  ; one's for pins to be set for output
					ORR		r4,r4,r3		;    bits 22 & 30 to be outputs
					STR		r4,[r0]			; set lcd port bits to output
					LDR		r3,=0x00400000	; 
					STR		r3,[r5]			; write 0 to LCD R/W signal
					LDR		r3,=0x40008000
					STR		r3,[r6]			; turn on backlight - write 1 to P0.30

					LDR		r0,=IO1DIR
					LDR		r4,[r0]			; read current Port 1 direction bits
					LDR		r3,=0x03FF0000  ; one's for lcd pins - set for output
					ORR		r4,r4,r3		;    bits 16 to 25 to be outputs
					STR		r4,[r0]			; set lcd port direction bits to output
					STR		r3,[r7]			; clear the LCD data & control bits					

					LDR		r9,=0x02000000	; bit pattern to turn on/off E

					LDR		r2,=0x00380000	; function: 2 lines, 5x7 dots
					STR		r2,[r8]			; set lcd function bits
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

					MOV		r0,#40
					BL		delay			;  wait 40 microseconds

					LDR		r2,=0x000E0000	; function:  display on, cursor on
					STR		r2,[r8]			; set lcd function bits
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

					MOV		r0,#40
					BL		delay			;  wait 40 microseconds
					
					LDR		r2,=0x00010000	; lcd function:  clear display
					STR		r2,[r8]			; set lcd function bits
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

					MOV		r0,#1600
					BL		delay			; delay for 1600 microseconds

					LDR		r2,=0x00030000	; lcd function:  set entry mode
					STR		r2,[r8]			; set lcd function bits
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

end_lcdReset		LDMFD	SP!,{PC,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9}

;------------------------------------------------------------------------------
;   Procedure pulse_e		uses r0. Not interuptable (r14 not saved)
;							assumes r9 has 0x02000000 in it
;                           assumes r7 has IO1CLR address in it
;							assumes r8 has IO1SET address in it
;------------------------------------------------------------------------------
pulse_e 			MOV		r0,#4
					STR		r9,[r8]			; assert E
ploop1				SUBS	r0,r0,#1		; delay	600ns or so
					BNE		ploop1
					STR		r9,[r7]			; de-assert E
					MOV		r0,#8
ploop2				SUBS	r0,r0,#1		; delay 1100ns or so
					BNE		ploop2
end_pulse_e			BX		LR
					            
; -----------------------------------------------------------------------------
;   Procedure lcdClear    Clears the LCD and positions cursor to line1, left col
;               					no parameters required
; -----------------------------------------------------------------------------
lcdClear			STMFD	SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

					LDR		r9,=0x02000000	; bit pattern to turn on/off E
					LDR		r3,=0x03FF0000  ; one's for pins used for lcd
					LDR		r7,=IO1CLR
					LDR		r8,=IO1SET

					LDR		r2,=0x00010000	; lcd function:  clear display
					STR		r2,[r8]			; set lcd function bits
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

					MOV		r0,#1600
					BL		delay

end_lcdClear		LDMFD	SP!,{PC,r0,r1,r2,r3,r7,r8,r9}
; -----------------------------------------------------------------------------
;   Procedure lcdCursorAt
;                          r0 assumed to contain the row# (1 or 2)
;                          r1 assumed to contain col#(0-15)
;                          No cursor movement if invalid row or col # is found
; -----------------------------------------------------------------------------

lcdCursorAt			STMFD	SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

					CMP		r1,#15			; check column & row range
					BHI		end_lcdCursorAt
					CMP		r0,#0
					BEQ		end_lcdCursorAt
					CMP		r0,#2
					BHI		end_lcdCursorAt
					ADDEQ	r1,r1,#0x40		; build the cursor address
					LDR		r0,=0x00800000
					ORR		r2,r0,r1,LSL #16 ; r2 now has the address

					LDR		r3,=0x03FF0000  ; one's for pins used for lcd
					LDR		r7,=IO1CLR
					LDR		r8,=IO1SET
					LDR		r9,=0x02000000	; bit pattern to turn on/off E

					STR		r2,[r8]			; set lcd function bits
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

					MOV		r0,#40
					BL		delay

end_lcdCursorAt		LDMFD	SP!,{PC,r0,r1,r2,r3,r7,r8,r9}

; -----------------------------------------------------------------------------
;   Procedure lcdWRdata         Writes data (an ASCII character) to the
;                               current cursor position on the LCD
;                               r0 is assumed to contain the character to send
; -----------------------------------------------------------------------------
lcdWRdata	   		STMFD	SP!,{LR,r0,r1,r2,r3,r7,r8,r9}

					LDR		r9,=0x02000000	; bit pattern to turn on/off E
					LDR		r3,=0x03FF0000  ; one's for pins used for lcd
					LDR		r7,=IO1CLR
					LDR		r8,=IO1SET

					AND		r0,r0,#0xFF		; make sure only 8 bits are non-zero
					ORR		r0,r0,#0x100	; set bit that indicates writing
					LSL		r0,r0,#16
					STR		r0,[r8]			; write data
					BL		pulse_e
					STR		r3,[r7]			; clear lcd bits

end_lcdWRData		LDMFD	SP!,{PC,r0,r1,r2,r3,r7,r8,r9}

; -----------------------------------------------------------------------------
;   Procedure  delay          microsecond delay using timer 0
;                               (12Mhz clock & VPB divide by 4 assumed)
;                             reg R0 must contain a delay value in uS
;                             Not interruptable (r14 not saved)
;							  assumes lcdReset has been called prior to delay
;                             r0, r1 not preserved
; -----------------------------------------------------------------------------

delay				LDR		r1,=T0_MR0_ADDR			; Match register zero
					STR		r0,[r1]					; load match count per R0

;					ldr		r0,=0x8000
;					str		r0,[r5]					; turn on p0.15 led

					LDR		r1,=T0_TCR_ADDR			; Timer 0 Control Register
					MOV		r0,#2					; bit 1 = one
					STR		r0,[r1]					; Clear counter & prescaler
					MOV		r0,#0
					STR		r0,[r1]

					MOV		r0,#1					; bit 0 = one
					STR		r0,[r1]					; Turn on counter

dloop				LDR     r0,[r1]					; Read TCR register
					ANDS	r0,r0,#1
					BNE		dloop

;					ldr		r0,=0x8000
;					str		r0,[r6]					; turn off p0.15 led

end_delay			BX		LR						; Return

					END

  • PWM Example for running the servos on the robots
;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;
;   File name  	: shell_2148.s 
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09  
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added 
;   Description	: 
;
;   Inputs      :
;   
;   Outputs     :
;
;   Special     :  
;  requirements
;   
;
; 	NOTES: 
;
; 
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C
;  Memory addresses for Timer/Counter 
T1TCR			EQU	 	0xE0008004
T1CTCR			EQU	  	0xE0008070
T1PR			EQU	 	0xE000800C 

T1MCR			EQU	   	0xE0008014
T1EMR 			EQU	  	0xE000803C 


;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

                AREA    RESET, CODE, READONLY
                ARM


; Exception Vectors
;  Mapped to Address 0.
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;               LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

                EXPORT  Reset_Handler
Reset_Handler   

; Setup Stack for each mode

                LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR

                MOV     SP, R0
                SUB     SL, SP, #USR_Stack_Size


; User program code goes here

main
				BL   pwm_init
				mov r0, #3
				ldr r1, =1600
				mov r2, r1
				bl pwm_update
stop			b stop


;*****************************************************************************
;    Pulse width modulator initialization routine.
;          pulse period = 20ms;  initial pulse length = 1.5ms
;          (values shown are for controlling model plane servos)
;    Inputs: none
;    Returned values: none
;    Uses registers r0, r1
;
;    Calling method:   BL   pwm_init
;                                                   LDA   rev 1.0  12/01/10
;													Rob Frohne rev 1.1 12/3/13
;*****************************************************************************
; Stepper motor connections on the Board of Education are from page 24 of the board 
; Users Guide.  A stepper motor operation is explained at:
; http://www.haydonkerk.com/Resources/StepperMotorTheory/tabid/192/Default.aspx
;
; PWM is explained in Chapter 16 (page 253 and following) of the reference manual.
; Note the registers in the reference manual are labeled without the _, i.e. 
; 	PWMPR instead of PWM_PR.

pwm_init		STMFD	sp!,{r14}		; save link register on the stack

		        LDR     r0,=PWM_PR
				LDR     r1,=2			; prescaler = 2 divides PCLK by 3
                STR     r1,[r0]         ; load prescaler
                                        ; PCLK rate is 1 MHZ (1 uSEC period) after division.
                LDR     r0,=PWM_MCR		; PWMMCR PWM Match Control Register. 
										; The PWMMCR is used to control if an 

										; interrupt is generated and if the PWMTC 
										; is reset when a Match occurs.  See page 261 of the manual.
				LDR     r1,=0x02        ; bit 1 is set, others are zero
				STR     r1,[r0]         ; load PWM_MCR: PWM_TC resets when PWM_TC=PWM_MR0

                LDR     r0,=PWM_MR0
				LDR     r1,=20000       ; count 20,000 1uSEC intervals = 20 milli secs
				STR     r1,[r0]         ; load match register 0. sets pulse period.  Page 261.

				LDR     r0,=PWM_MR4		
                LDR     r1,=1500		; initial pulse width = 1.5 milli secs
				STR     r1,[r0]         ; load match register 4.  Page 261
				LDR     r0,=PWM_MR6		
				LDR     r1,=1500		; initial pulse width
				STR     r1,[r0]			; load match register 6

	            LDR     r0,=PWM_LER
				LDR     r1,=0x51
				STR     r1,[r0]         ; Enable latch registers 0, 4, & 6

                LDR     r0,=PWM_PCR
				LDR     r1,=0x5000      ; bits 12 & 14 set
				STR     r1,[r0]         ; load PWM_CR: enables PWM4 and PWM6 outputs

                LDR     r0,=PWM_TCR
				LDR     r1,=0x09        ; bits 3,1,0 set
				STR     r1,[r0]         ; load PWM_TCR enable PWM counter & prescaler

                LDR     r0,=PINSEL0
                LDR     r1,=0xA0000     ; bits 19 & 17 set.  selects output PWM 6 & 4
				STR     r1,[r0]		    ; Enable PWM6 & PWM4 outputs

end_pwm_init	LDMFD	sp!,{pc}		; Return from subroutine  pwm_init


;*****************************************************************************
;   Pulse width modulator update routine
;	Inputs:  r0 = command
;                   1 = update PWM channel 4
;                   2 = update PWM channel 6
;                   3 = update both channel 4 and 6
;            r1 = new pulse width (# of microseconds) for PWM channel 4
;            r2 = new pulse width (# of microseconds) for PWM channel 6
;   Returned values: none
;   Uses registers r0, r1, r2, r3
;
;   Calling method:  first put values in r0, r1, r2 as appropriate
;                    BL pwm_update
;                                                   LDA    rev 2.0  12/01/11
;*****************************************************************************

pwm_update		STMFD	sp!,{r14}		; r0 specifies channels to update 

load_pwm4		CMP		r0,#1			; 1 = load PWM chan 4
				BNE		load_pwm6
				LDR     r3,=PWM_MR4
				STR     r1,[r3]         ; load match register 4

load_pwm6		CMP		r0,#2			; 2 = load PWM chan 6
				BNE		load_both
				LDR     r3,=PWM_MR6
				STR     r2,[r3]			; load match register 6

load_both		CMP		r0,#3
				BNE		pwm_reg_update
				LDR		r3,=PWM_MR4
				STR		r1,[r3]
				LDR		r3,=PWM_MR6
				STR		r2,[r3]

pwm_reg_update	CMP		r0,#1			; check for just pwm 4 update
				MOVEQ	r1,#0x10
				CMP		r0,#2			; check for just pwm 6 update
				MOVEQ	r1,#0x40
				CMP		r0,#3			; check for pwm 4 & 6 update
				MOVEQ	r1,#0x50

				LDR		r3,=PWM_LER
				STR		r1,[r3]			; enable latch registers

end_pwm_update	LDMFD	sp!,{pc}		; Return from subroutine  pwm_update

; User data area definition follows

                AREA  appdata, DATA, NOINIT, READWRITE

                END

  • Stepper Motor on the Embedded Artists Board of Education

;---------------------------------------------------------------------------
;
;   Programmer  : Larry Aamodt
;				: Modifications for propeller drive: Rob Frohne
;
;   File name  	: shell_2148.s 
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09  
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added 
;   Description	: 
;
;   Inputs      :
;   
;   Outputs     :
;
;   Special     :  
;  requirements
;   
;
; 	NOTES: 
;
; 
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C

;  Timer addresses
T0_TCR_ADDR		EQU		0xE0004004
T0_PR_ADDR		EQU		0xE000400C
T0_MCR_ADDR		EQU		0xE0004014
T0_MR0_ADDR		EQU		0xE0004018

;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

                AREA    RESET, CODE, READONLY
                ARM


; Exception Vectors
;  Mapped to Address 0.
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;               LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

                EXPORT  Reset_Handler
Reset_Handler   

; Setup Stack for each mode

                LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR

                MOV     SP, R0
                SUB     SL, SP, #USR_Stack_Size


; User program code goes here

quarter_period EQU	250000 ; uS
main  				    
					BL init_propeller
					mov r1, #1	 ; for handy access
					mov r2, #0	  ; for handy access

					mov r0, r1, LSL #12		; turn one on
					mov r4, r2, LSL #21		; two still off
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3] 
					ldr r0,=quarter_period
					bl delay

					mov r0, r1, LSL #12
					mov r4, r1, LSL #21
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3] 
					ldr r0,=quarter_period
					bl delay

					mov r0, r2, LSL #12		; turn one off
					mov r4, r1, LSL #21		; two still on
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3] 
					ldr r0,=quarter_period
					bl delay

   					mov r0, r2, LSL #12		; turn one off
					mov r4, r2, LSL #21		; turn two off
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3] 
					ldr r0,=quarter_period
					bl delay
				
					b main 

; -----------------------------------------------------------------------------
;   Procedure init_propeller  Must be called before to initialize the propeller.
;							  Set up the counter T0 and GPIO P0.12 and P0.21 for output.
;                             No parameters required
; -----------------------------------------------------------------------------
init_propeller		STMFD	SP!,{LR,r0,r1,r2,r3,r4}

					LDR		r1,=T0_PR_ADDR			; Prescale register 
					MOV		r2,#2 					; prescale count value
					STR		r2,[r1]					; Prescaler will divide by 3  (1 uS period)

					LDR		r1,=T0_MCR_ADDR			; Match control register
					MOV		r2,#0x4					; bit 2 = one: Stop on MR0: the TC and PC 
													; will be stopped and TCR[0] will be set to 0 if 
													; MR0 matches the TC
					STR		r2,[r1]					; stop counter when match reached

					LDR		r0,=IO0DIR
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=2101248  	; one's for pins to be set for output (2^12+2^21 = 2101248)
											; 2134016 = 2^12+2^21+2^15 (for debugging LED on P0.15
					ORR		r4,r4,r3		
					STR		r4,[r0]			; set propeller port bits to output without changing others.
					
					LDR		r0,=PINSEL0
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=50331648  	; zero's for pins to be set for output (2^24+2^25 = 50331648)
					BIC		r4,r4,r3		;  
					STR		r4,[r0]			; set propeller port P0.12 to GPIO without changing others.

										
					LDR		r0,=PINSEL1
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=3072  		; zero's for pins to be set for output (2^10+2^11 = 3072)
					BIC		r4,r4,r3		;  
					STR		r4,[r0]			; set propeller port P0.21 to GPIO without changing others.
				
end_init_propeller	LDMFD	SP!,{PC,r0,r1,r2,r3,r4}



; -----------------------------------------------------------------------------
;   Procedure  delay          microsecond delay using timer 0
;                               (12Mhz clock & VPB divide by 4 assumed)
;                             reg R0 must contain a delay value in uS
;                             Not interruptable (r14 not saved)
;							  assumes lcdReset has been called prior to delay
;                             r0, r1 not preserved
; -----------------------------------------------------------------------------

delay				STMFD	SP!,{LR,r0,r1}
					LDR		r1,=T0_MR0_ADDR			; Match register zero
					STR		r0,[r1]					; load match count per R0

;					ldr		r0,=0x8000
;					str		r0,[r5]					; turn on p0.15 led

					LDR		r1,=T0_TCR_ADDR			; Timer 0 Control Register
					MOV		r0,#2					; bit 1 = one
					STR		r0,[r1]					; Clear counter & prescaler 
					MOV		r0,#0
					STR		r0,[r1]

					MOV		r0,#1					; bit 0 = one
					STR		r0,[r1]					; Turn on counter

dloop				LDR     r0,[r1]					; Read TCR register
					ANDS	r0,r0,#1
					BNE		dloop

;					ldr		r0,=0x8000
;					str		r0,[r6]					; turn off p0.15 led

end_delay			LDMFD	SP!,{PC,r0,r1}			; Return

					END

  • Propeller Drive Using Interrupts (not polled)

;---------------------------------------------------------------------------
;
;   Shell Programmer  : Larry Aamodt, main: Rob Frohne
;				
;
;   File name  	: shell_2148.s 
;   Class       : CPTR-215
;   Language    : ARM assembly
;   Assembler   : Keil
;   Target MCU	: NXP LPC-2148 on Embedded Artists board
;   Date Written: 11/30/09  
;    change history:  11/30/09  LDA  Updated with hardware start-up
;    		      12/03/09  LDA  PWM register definitions added 
;   Description	: 
;
;   Inputs      :
;   
;   Outputs     :
;
;   Special     :  
;  requirements
;   
;
; 	NOTES: 
;
; 
;
;---------------------------------------------------------------------------

; Put application program definitions (i.e. equates) here:


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled

;  Memory addresses for standard GPIO definitions
IO0PIN		    EQU     0xE0028000
IO0SET		    EQU     0xE0028004
IO0DIR		    EQU     0xE0028008
IO0CLR		    EQU     0xE002800C

; Vector Interrupt Controller Addresses
VICIntSelect		EQU 0xFFFFF00C
VICVectAddr4		EQU	0xFFFFF110
VICVectCntl4		EQU	0xFFFFF210
VICIntEnable		EQU	0xFFFFF010
VICVectAddr			EQU 0xFFFFF030


;  Timer addresses
T0PC				EQU	0xE0004010  ; Timer 0 Prescale counter
T0IR				EQU	0xE0004000	; Timer 0 Interrupt Register
T0_TCR_ADDR		EQU		0xE0004004
T0_PR_ADDR		EQU		0xE000400C
T0_MCR_ADDR		EQU		0xE0004014
T0_MR0_ADDR		EQU		0xE0004018

;  Memory addresses for Pulse Width Modulation (PWM)
PWM_TCR         EQU     0xE0014004      ; PWM_TCR Timer Control Register
PWM_PR          EQU     0xE001400C      ; PWM_PR Prescaler Register
PWM_MCR         EQU     0xE0014014      ; PWM_MCR Match Control Register
PWM_MR0         EQU     0xE0014018      ; PWM_MR0 Match Register 0 (sets pulse period)
PWM_MR4         EQU     0xE0014040      ; PWM_MR4 Match Register 4 (sets pulse length)
PWM_MR6         EQU     0xE0014048      ; PWM_MR6 Match Register 6 (sets pulse length)
PWM_PCR         EQU     0xE001404C      ; PWM_CR Control Register
PWM_LER         EQU     0xE0014050      ; PWM_LER Latch Enable Register

PINSEL0         EQU     0xE002C000      ; Pin connect block - port 0
PINSEL1         EQU     0xE002C004      ; Pin connect block - port 1

;  Stack size definitions
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400

ISR_Stack_Size  EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem       SPACE   USR_Stack_Size
__initial_sp    SPACE   ISR_Stack_Size
Stack_Top


; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.

                AREA    RESET, CODE, READONLY
                ARM


; Exception Vectors
;  Mapped to Address 0.
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.

Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
;                LDR     PC, IRQ_Addr
                LDR     PC, [PC, #-0x0FF0]     ; Vector from VicVectAddr
                LDR     PC, FIQ_Addr

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

; Exception Handlers
Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler

; Reset Handler

                EXPORT  Reset_Handler
Reset_Handler   

; Setup Stack for each mode

                LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR
                MOV     SP, R0
                SUB     SL, SP, #USR_Stack_Size

step_time_uS EQU	250000 ; uS between steps of the propeller motor.

main 				BL init_propeller	 ; Initialize GPIO and IRQ, and Timer 0.

					LDR r0,=step1
					LDR r1,=NextRoutine_Address
					STR r0,[r1]
					bl delay ; start the whole automated thing

do_nothing			b do_nothing	; because the ISR will do it all!

; -----------------------------------------------------------------------------
;   Step the motor routines.  These are called by the Timer0 ISR (timer0ISR)
; -----------------------------------------------------------------------------
 

step1				STMFD	SP!,{LR,r0-r12}
					mov r1, #1	 ; for handy access
					mov r2, #0	  ; for handy access
					mov r0, r1, LSL #12		; turn one on
					mov r4, r2, LSL #21		; two still off
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3]
					LDR r0,=step2
					LDR r1,=NextRoutine_Address
					STR r0,[r1] 
					bl delay
					LDMFD	SP!,{PC,r0-r12}

step2				STMFD	SP!,{LR,r0-r12}
					mov r1, #1	 ; for handy access
					mov r2, #0	  ; for handy access
					mov r0, r1, LSL #12
					mov r4, r1, LSL #21
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3]
					LDR r0,=step3
					LDR r1,=NextRoutine_Address
					STR r0,[r1] 
					bl delay
					LDMFD	SP!,{PC,r0-r12}

step3				STMFD	SP!,{LR,r0-r12}
					mov r1, #1	 ; for handy access
					mov r2, #0	  ; for handy access
					mov r0, r2, LSL #12		; turn one off
					mov r4, r1, LSL #21		; two still on
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3]
					LDR r0,=step4
					LDR r1,=NextRoutine_Address
					STR r0,[r1] 
					bl delay
					LDMFD	SP!,{PC,r0-r12}

step4				STMFD	SP!,{LR,r0-r12}
					mov r1, #1	 ; for handy access
					mov r2, #0	  ; for handy access
					mov r0, r2, LSL #12		; turn one off
					mov r4, r2, LSL #21		; turn two off
					orr r0, r4, r0 ; both on
					ldr r3,=IO0PIN
					str r0,[r3]
					LDR r0,=step1
					LDR r1,=NextRoutine_Address
					STR r0,[r1] 
					bl delay				
					LDMFD	SP!,{PC,r0-r12} 

; -----------------------------------------------------------------------------
;   Procedure init_propeller  Must be called before to initialize the propeller.
;							  Set up GPIO P0.12 and P0.21 for output.
;							  Set up timer 0 and interrupts for IRQ (low priority)
;                             No parameters required
; -----------------------------------------------------------------------------

init_propeller		STMFD	SP!,{LR,r0,r1,r2,r3,r4}
										LDR		r1,=T0_PR_ADDR			; Prescale register 
					MOV		r2,#2 					; prescale count value
					STR		r2,[r1]					; Prescaler will divide by 3  (1 uS period

;  T0MCR = 0x00000003;                           	//reset counter and generate IRQ on MR0 match

					LDR		r1,=T0_MCR_ADDR			; Match control register
					MOV		r2,#0x3					; bit 2 = one: Stop on MR0: the TC and PC : bit 1 = 1 gives interrupt
													; will be stopped and TCR[0] will be set to 0 if 
													; MR0 matches the TC
					STR		r2,[r1]					; stop counter when match reached

;	VICIntSelect &= ~0x10;           //Timer0 interrupt is assigned to IRQ (not FIQ)
					LDR		r1,=VICIntSelect
					MOV 	r0,#~0x10
					STR		r0,[r1]

;	VICVectAddr4  = (tU32)timer0ISR; //register ISR address
					LDR		r1,=VICVectAddr4
					LDR 	r0,=timer0ISR
					STR		r0,[r1]

;	VICVectCntl4  = 0x24;            //enable vector interrupt for timer0
					LDR		r1,=VICVectCntl4
					MOV 	r0,#0x24
					STR		r0,[r1]

;	VICIntEnable  = 0x10;            //enable timer0 interrupt
					LDR		r1,=VICIntEnable
					MOV 	r0,#0x10
					STR		r0,[r1]		

					LDR		r0,=IO0DIR
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=2101248  	; one's for pins to be set for output (2^12+2^21 = 2101248)
											; 2134016 = 2^12+2^21+2^15 (for debugging LED on P0.15
					ORR		r4,r4,r3		
					STR		r4,[r0]			; set propeller port bits to output without changing others					

					LDR		r0,=PINSEL0
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=50331648  	; zero's for pins to be set for output (2^24+2^25 = 50331648)
					BIC		r4,r4,r3		;  
					STR		r4,[r0]			; set propeller port P0.12 to GPIO without changing others.										

					LDR		r0,=PINSEL1
				   	LDR		r4,[r0]			; read current Port 0 direction bits
					LDR		r3,=3072  		; zero's for pins to be set for output (2^10+2^11 = 3072)
					BIC		r4,r4,r3		;  
					STR		r4,[r0]			; set propeller port P0.21 to GPIO without changing others.				

end_init_propeller	LDMFD	SP!,{PC,r0,r1,r2,r3,r4}

; -----------------------------------------------------------------------------
;  			Timer 0 Interrupt Service Routine  timer0ISR
; 			Some comments are C equivalents.
; -----------------------------------------------------------------------------

timer0ISR		 	SUB LR,LR,#4	 ; Update the LR
					STMFD SP!, {r0-r12,LR}	  ; Store registers (including LR)

;  T0IR        = 0xff;        //reset all IRQ flags
					LDR		r1,=T0IR
					MOV 	r0,#~0xff
					STR		r0,[r1]

;  VICVectAddr = 0x00;        //dummy write to VIC to signal end of interrupt
					LDR		r1,=VICVectAddr
					MOV 	r0,#~0x00
					STR		r0,[r1]

; Here we will branch to the appropriate next step routine.
					LDR r0,=NextRoutine_Address
					push {r14}  ; save LR for this return.
					ldr r14,=end_timer0ISR  ; load the LR because we are using an LDR PC below.
					ldr PC,[r0] ; branch to the subroutine pointed to by NextRoutine_Address
end_timer0ISR		pop {r14}	; restore the LR for this return.
					LDMFD SP!, {r0-r12,PC}^  			; Return from ISR

; -----------------------------------------------------------------------------
;   Procedure  delay          microsecond delay using timer 0
;                               (12Mhz clock & VPB divide by 4 assumed)
;                  				Delay is set by step_time_uS
; -----------------------------------------------------------------------------

delay				STMFD	SP!,{LR,r0,r1}


;  T0PC  = 0x00000000;    Didn't do this yet.  Unnecessary?                       //no prescale of clock

				;	LDR		r1,=T0PC
				;	MOV 	r0,#0x00000000
				;	STR		r0,[r1]

;  T0IR  = 0x000000ff;                           //reset all flags before enable IRQs

					LDR		r1,=T0IR
					MOV 	r0,#0x000000ff
					STR		r0,[r1]

;  T0MR0 = delayInMs *                           //calculate no of timer ticks
;         ((CRYSTAL_FREQUENCY * PLL_FACTOR) / (1000 * VPBDIV_FACTOR));
					LDR		r1,=T0_MR0_ADDR			; Match register zero
					LDR		r0,=step_time_uS
					STR		r0,[r1]					; load match count from step_time_uS

;  T0TCR = 0x00000002;                           //disable and reset Timer0
					LDR		r1,=T0_TCR_ADDR			; Timer 0 Control Register
					MOV		r0,#2					; bit 1 = one
;  T0TCR = 0x00000001;                           //start Timer0
					STR		r0,[r1]					; Clear counter & prescaler 
					MOV		r0,#0
					STR		r0,[r1]

					MOV		r0,#1					; bit 0 = one
					STR		r0,[r1]					; Turn on counter

end_delay			LDMFD	SP!,{PC,r0,r1}			; Return

                	AREA Thedata, DATA, READWRITE
					ALIGN
NextRoutine_Address SPACE 4	 ; This holds the address of the next step subroutine.

					END