;;; -*- Mode:Asm Mode:outline-minor-mode outline-regexp:";;;+" comment-start: "; " -*- ;;; led4.asm ;;; Frank Sergeant frank@pygmy.utoh.org ;;; Test program for the Olimex LPC P-2378 board. ;;; Flash the STAT LED connected to P1.19 ;; This is much like led3.asm except we set up the PLL ;; (by making sure it is off), select the clock source, ;; set various clock dividers, etc. ;; ;; Do not set up stacks (therefore no subroutines or interrupts). ;; Set the GPIOM bit, so we can use the Fast GPIO. .include "equates-lpc23xx.s" .include "olimex-lpc2378-equates.s" ;;; Timing related equates ;; input frequency to the PLL ;.equ PLLCLKIN, 4000000 ; if IRC is clock source ;.equ PLLCLKIN, 32768 ; if RTC is clock source .equ PLLCLKIN, 12000000 ; if Main oscillator (12 MHz for Olimex P2378 board) is clock source ;; note, we do *not* set the PLL in this program, so this ;; must always be 1. .equ PLL_MULTIPLIER, 1 ;; output frequency from the PLL ;; (some NXP diagrams also call this HCLK, right?) ;; when PLL is bypassed, PLLCLK == PLLCLKIN .equ PLLCLK, (PLLCLKIN * PLL_MULTIPLIER) ;; The CPU Clock Divider divides PLLCLK to give CCLK (ARM core clock). ;; The value in CCLKCFG determines the divisor. ;; CCLK must be at least 18 MHz if USB is used. ;; CPUDIVISOR below is one greater than the "CCLKSEL" value written ;; to CCLKCFG, i.e. to divide by 1, write 0 to CCLKCFG. .equ CPUDIVISOR, 1 .equ CCLK, (PLLCLK / CPUDIVISOR) ;; PCLKSEL0 and PCLKSEL1 control the individual PCLK divisors for ;; the various peripherals. Bits 3:2 of PCLKSEL0 control Timer0 .equ PCLK_TIMER0_DIVISOR, 4 ;; Then the timer prescaler divides the timer's PCLK before ;; it gets to the timer register. .equ TIMER0_PRESCALE_DIVISOR, 1 ;; frequency is given in number of clocks per second .equ TIMER0FREQ, ((PLLCLK / PCLK_TIMER0_DIVISOR) / TIMER0_PRESCALE_DIVISOR) ;; now that we know (or have guessed) the timer frequency we can ;; calculate the value for the timer's match register and/or ;; the timer's prescale register in order to kill the requested ;; amount of time. .equ SECONDS, 1 ; let's have the LED on for 10 seconds ; then off for 10 seconds .equ LEDDELAY, (SECONDS * TIMER0FREQ) ;;; Misc Equates ;; (none yet) ;;; Code .code 32 .section .text .global vectors .org 0 .global _start ;;; Vectors ; each interrupt vector runs an endless loop except for reset vectors: b _start b . b . b . b . b . b . b . ;;; Cold .section .text _start: ;;;; Clock ;; disconnect and disable the PLL ; clear the 2 ls bits of PLLCON to prepare to disable and ; disconnect the PLL. bit1=PLLC, bit0=PLLE. However, ; the manual, p. 44, shows disconnecting with one feed sequence ; then disabling with another feed sequence, so that's what ; we will do here. ldr r1, = PLLCON ldr r0, [r1] bic r0, r0, #2 ; clear bit 1 (PLLC) to disconnect PLL str r0, [r1] ;; PLL feed sequence ldr r1, = PLLFEED mov r0, #0xaa strb r0, [r1] mov r0, #0x55 strb r0, [r1] ldr r1, = PLLCON ldr r0, [r1] bic r0, r0, #1 ; clear bit 0 (PLLE) to disable PLL str r0, [r1] ;; PLL feed sequence ldr r1, = PLLFEED mov r0, #0xaa strb r0, [r1] mov r0, #0x55 strb r0, [r1] ; at this point, the PLL should be off and disconnected and ; the selected clock source should providing the clock to the ; CPU, bypassing the PLL. This clock source should be the IRC ; (4 MHz). However, it looks like the selected clock, even when ; the PLL is bypassed, still goes through the CPU clock divider ; (CCLKCFG). ; Set the CPU clock divider so it divides by CPUDIVISOR. ; (Set the value of the CCLKCFG reg to 1 less than the ; desired divider.) ldr r1, = CCLKCFG mov r0, # (CPUDIVISOR - 1) strb r0, [r1] ; Turn on the main clock, wait for it to stabilize, then ; switch from the IRC clock to the main clock. ldr r1, = SCS ldr r0, [r1] bic r0, r0, #0x10 ; clear bit 4 to select 1-20MHz OSRANGE str r0, [r1] orr r0, r0, #0x20 ; set bit 5 OSCEN to start main oscillator str r0, [r1] ; wait for main oscillator to stabilize by reading bit 6 (OSCSTAT) ; until it goes to 1. oscstab: ldr r0, [r1] tst r0, #0x40 ; Wait for OSCSTAT beq oscstab ; to go true. chgclk: ; for LPC2378, now that the main oscillator is stable, ; switch from the IRC clock to the main clock ldr r1, = CLKSRCSEL mov r0, #1 strb r0, [r1] ;;;; Ports ;; Set the GPIOM bit so we can use the Fast GPIO. ;; (The bootloader might have set the GPIOM bit but set it ;; explicitly to be sure.) ;; select fast GPIO mode for ports 0 and 1 ldr r6, = SCS ldr r0, [r6] orr r0, r0, #1 ; set bit zero (GPIOM) to force fast GPIO mode str r0, [r6] ;; Clear all the mask bits so that no pins are masked ldr r6, = FIO0MASK mov r0, #0 str r0, [r6] ; FIO0MASK str r0, [r6, #0x20] ; FIO1MASK str r0, [r6, #0x40] ; FIO2MASK str r0, [r6, #0x60] ; FIO3MASK str r0, [r6, #0x80] ; FIO4MASK ;; Note, P1.19 is in position 19 (dec) or 0x13 (hex) ;; in decimal ;; 3322 2222 2222 1111 1111 1100 0000 0000 ;; 1098 7654 3210 9876 5432 1098 7654 3210 ;; xxxx xxxx xxxx Yxxx xxxx xxxx xxxx xxxx ;; in hex ;; 1111 1111 1111 1111 0000 0000 0000 0000 ;; fedc ba98 7654 3210 fedc ba98 7654 3210 ;; xxxx xxxx xxxx Yxxx xxxx xxxx xxxx xxxx ;; So an orr or bic(and) mask is 0x00080000 ldr r6, = PINSEL3 ;; set P1.19 as a GPIO pin ;; P1.19 is controlled by bits 7:6 of PINSEL3 ldr r0, [r6] bic r0, r0, # 0xc0 ; clear bits 7:6 to force GPIO mode str r0, [r6] ;; set LED output pin (i.e. P1.19) as an output ldr r6, = FIO1DIR ; for PORT1 mov r0, # STAT_LED_MASK ; all inputs except for pin 19 str r0, [r6] ;;;; Timer ;; Use timer0 to delay between LED blinks ;; With a prescale value of 0, the counter increments on ;; every rising edge of PCLK. ldr r6, = T0CTCR ; Put timer0 into timer (not counter) mode, mov r0, #0 ; almost certainly not needed as strb r0, [r6] ; bootloader probably did not disturb it. ldr r6, = T0TCR ; start timer0 mov r0, #1 strb r0, [r6] ldr r6, = T0PR ; set timer0 prescaler mov r0, #0 ; start with no prescaling str r0, [r6] ;; We do not poll the timer for a zero because we might miss it. ;; Instead, we poll for the interrupt flag that indicates the ;; match register count has been reached. ;; We set a match register, but do not activate the ;; interrupt in the VIC. Thus, the bit still be set in ;; the timer interrupt register but no actual interrupt ;; will be generated. ldr r6, = T0MCR ; Reset the counter when it mov r0, #3 ; matches the match register value str r0, [r6] ; and also generate an interrupt flag. ; That is, set the interrupt bit ; for it in the timer interrupt register ; but this will not actually cause an interrupt ; because we have not turned it on in ; the VIC. ldr r6, = T0MR0 ; set the counter match value ldr r0, = LEDDELAY str r0, [r6] ldr r6, = FIO1PIN 1: eor r0, r0, # STAT_LED_MASK ; flip the bit controlling the LED str r0, [r6] ; set or clear P1.19, turning LED on or off ldr r5, = T0IR ; bit 0 goes high when timer count matches ; the value in match register 0 2: ldr r1, [r5] ; read timer interrupt register ands r1, r1, #1 ; set the zero flag if bit 0 is not high yet beq 2b ; loop until bit goes high str r1, [r5] ; We just read a 1 bit which indicated the counter ; has reached the match register value. No we write ; that 1 back to the interrupt register to clear the bit. b 1b ; continue forever