Code Examples from Class: Difference between revisions
		
		
		
		Jump to navigation
		Jump to search
		
| No edit summary | No edit summary | ||
| (30 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 17: | Line 92: | ||
|   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 | ||
| Line 24: | Line 99: | ||
|      END |      END | ||
| ---- | |||
| * Subroutine Example1   | * Subroutine Example1   | ||
| Line 30: | Line 107: | ||
|   ; saving registers on the stack, etc. |   ; saving registers on the stack, etc. | ||
|   ; Rob Frohne,  |   ; Rob Frohne, 11/3/2013 | ||
|   stack_start EQU 0x40001000 |   stack_start EQU 0x40001000 | ||
| Line 40: | 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 | ||
| Line 51: | Line 128: | ||
|   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 11: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