Home |  GPIO |  PWM |  ADC |  LCD |  Threads |  Sonar |  Clock |  I2C |  Expert |  KANREN |  Candy


A Scheme Interpreter for ARM Microcontrollers: LPC2131 Program Examples

SourceForge.net Logo

Most of the LPC2000 program examples work also on the LPC2131 with modest modifications. The required modifications are to substitute Level 2 Library and Addendum functions (which are not included in the LPC2131 implementation) with equivalents written in terms of Core, Level 0 and Level 1 functions. For most programs, this consists of replacing (begin expr1 expr2 ...) with, either, separate statements or ((lambda () expr1 expr2 ...)) and replacing (let ...) with an internal (define ...) and a call to the defined function. The modified programs are listed below, without textual introduction.


 

GPIO:



; Armpit Scheme GPIO Example
; tested on Tiny2131

; define GPIO ports and offsets
(define gpio0 #xE0028000) ; IO0 port (PIN = #x00, SET = #x04, DIR = #x08, CLR = #x0C)
(define gpio1 #xE0028010) ; IO1 port (PIN = #x00, SET = #x04, DIR = #x08, CLR = #x0C)
(define pin-status #x00)  ; IOxPIN register offset
(define pin-set    #x04)  ; IOxSET register offset
(define pin-clear  #x0C)  ; IOxCLR register offset

; function to check the status of a pin on a gpio port (subs: zero?, not)
(define (is-set? port pin)
  (if (= 0 (logand (read port pin-status) (ash 1 pin)))
      #f #t))

; function to set a pin on a gpio port
(define (set-pin port pin)
  (write (ash 1 pin) port pin-set))

; function to clear a pin on a gpio port
(define (clear-pin port pin)
  (write (ash 1 pin) port pin-clear))

; function to toggle a pin on a gpio port
(define (toggle port pin)
  (if (is-set? port pin)
      (clear-pin port pin)
      (set-pin port pin)))

; toggle MCU board's LED(s)
(toggle gpio1 21) ; Tiny213x red led

(toggle gpio1 22) ; Tiny213x yellow led

(toggle gpio1 23) ; Tiny213x green led


 

PWM:



; Armpit Scheme PWM Example
; tested on Tiny2131

; define useful ports
(define pinsel #xE002C000) ; PINSEL (0 = #x00, 1 = #x04, 2 = #x14)
(define pwm    #xE0014000) ; PWM (TCR = #x04, PR = #x0C, MCR = #x14, MR0 = #x18, MR2 = #x20, PCR = #x4C, LER = #x50)

; configure PWM block
(write 145  pwm #x0C)  ; set prescale count register to 145 = 411 kHz via PWMPR
(write 1023 pwm #x18)  ; set PWM rate register to 1023 = 401 Hz (period = 2.5 ms) via PWMMR0
(write 2    pwm #x14)  ; set timer to reset at end of each cycle via PWMMCR

; configure PWM2 (P0.7)
(write (logior #x8000 (logand #xFFF3FFF (read pinsel #x00))) pinsel #x00) ; make P0.7 a PWM pin (i.e. PWM2) via PINSEL0
(write 0      pwm #x20)  ; set PWM2 duty cycle to 0 via PWMMR2
(write #x0400 pwm #x4c)  ; enable PWM2 single-edge output (bit 10, i.e. 2 + 8) via PWMPCR
(write #x04   pwm #x50)  ; Latch desired PWM2 rate for update (bit 2) via PWMLER

; function to set the PWM duty cycle to a given value (0 to 1023) on a given pwm channel
(define (set-pwm channel value)
  (write value pwm (+ #x18 (ash channel 2))) ; set duty in Match register PWMMRx
  (write (ash 1 channel) pwm #x50))          ; set channel bit in Latch PWMLER

; set duty-cycle of PWM2 (pin P0.7) to 500
(set-pwm 2 500)


 

ADC:



; Armpit Scheme ADC Example
; tested on Tiny2131

; define useful ports
(define pinsel #xE002C000) ; PINSEL (0 = #x00, 1 = #x04, 2 = #x14)
(define adc    #xE0034000) ; ADC0

; configure adc0.0 (P0.27)
(write (logior #x400000 (logand #xFF3FFFF (read pinsel #x04))) pinsel #x04) ; make P0.27 an ADC pin (i.e. ADC0.0) via PINSEL1

; function to read a value from an input channel in adc0
(define (read-adc channel)
  (write (logior #x01201000 (ash 1 channel)) adc #x00) ; start 10-bit conversion on channel via AD0CR
  (define (wait status)
    (if (= 0 (logand status (ash 1 channel)))      ; check if conversion is done
	(wait (read adc #x30))                     ; if not, loop with new status read from AD0STAT
	(logand #x03FF (ash (read adc #x04) -6)))) ; if so,  read and return value from AD0DR
  (wait 0))

; read a value from adc0 channel 0 (P0.27)
(read-adc 0)


 

Serial LCD on uart1:



; Armpit Scheme Example of Serial Communication to an LCD, via uart1
; tested on Tiny2131

; define useful ports
(define pinsel #xE002C000) ; PINSEL port (0 = #x00, 1 = #x04, 2 = #x14)
(define uart1  #xE0010000) ; UART1  port (RBR = THR = DLL = #x00, DLM = IER = #x04, LCR = #x0C, LSR = #x14)

; enable and configure UART1 for 9600,8,N,1 communication
(write #x01 uart1 #x08)   ; enable UART1 via U1FCR
(write #x80 uart1 #x0C)   ; enable Divisor Latch (DLAB = 1) via U1LCR
(write #x87 uart1 #x00)   ; set low  divisor for 9600 bauds via U1DLL
(write #x01 uart1 #x04)   ; set high divisor for 9600 bauds via U1DLM
(write #x03 uart1 #x0C)   ; set 8 data bits, no parity, 1 stop bit via U1LCR

; Connect UART Tx pin
(write (logior #x010000 (logand #xFFFCFFFF (read pinsel #x00))) pinsel #x00)

; clear the LCD screen
(write-char (integer->char 254) uart1)
(write-char (integer->char 1)   uart1)

; write-out a string
(write "hello: " uart1)

; move to line 2 of LCD
(write-char (integer->char 254) uart1)
(write-char (integer->char 192) uart1)

; write-out a number
(write (/ 100 -3) uart1)


 

MultiTasking:



; Armpit MultiTasking Example
; Tested on Tiny2131

; define useful ports
(define timer0 #xE0004000) ; T0 (IR = #x00, TCR = #x04, TC = #x08, PR = #x0C, MCR = #x14, MR0 = #x18)
(define gpio1  #xE0028010) ; IO1 (PIN = #x00, SET = #x04, DIR = #x08, CLR = #x0C)

; configure timer0
(write 59 timer0 #x0C) ; set timer0 period to 1 us (for 60MHz clk) via T0PR
(write 5  timer0 #x14) ; set timer0 to generate interrupt and stop on MR0 match via T0MCR

; Initialize the process queue
(define *q* '())

; Set timer 0 callback and set interrupts to every 10ms (10000 microsec)
(write
 (lambda ()
   (call/cc
    (lambda (rsme)
      (write 2 timer0 #x04)
      (write 1 timer0 #x04)
      (if (eq? *q* '())
	  (rsme #t)
	  ((car *q*) (set! *q* (append (cdr *q*) (cons rsme '()))))))))
 timer0 #x010000)

; Start timer 0 to produce interupts every 10ms (10000 microsec) for task switching
(write     2 timer0 #x04)
(write 10000 timer0 #x18)
(write     1 timer0 #x04)

; read the timer count
(read timer0 #x08)

; function to spawn a new thunk (for infinite loops only: does not dequeue tasks)
(define (spwn thnk)
  (write 0  timer0 #x04)
  (set! *q* (cons thnk *q*))
  (write 1  timer0 #x04))

; function to toggle a gpio pin every "tcks" tick
(define (tglr port pin tcks)
  (define (go n)
    (if (> n 0)
	(go (- n 1))
	((lambda ()
	   (write pin port
		  (if (= 0 (logand (read port #x00) pin)) #x04 #x0C))
	   (go tcks)))))
  (lambda () (go tcks)))

; spawn togglers on MCU board's LEDs (can spawn 2 of those before heap runs out)
(spwn (tglr gpio1 #x00800000 50))   ; Tiny2131 green led

(spwn (tglr gpio1 #x00400000 100))  ; Tiny2131 yellow led

(spwn (tglr gpio1 #x00200000 200))  ; Tiny2131 red led



 

Ultrasonic Ranger:



; Armpit Scheme Ultrasonic Ranger Example
; tested on Tiny2131

; define useful ports
(define timer1 #XE0008000) ; T1 (IR = #x00, TCR = #x04, TC = #x08, PR = #x0C, MCR = #x14, MR0 = #x18)
(define pinsel #xE002C000) ; PINSEL (0 = #x00, 1 = #x04, 2 = #x14)

; Configure match and capture pins (P0.17 <- match 1.2, P0.19 <- capture 1.2)
(write (logior #xCC (logand #xFFFFFF33 (read pinsel #x04))) pinsel #x04) ; set P0.19, P0.17 to CAP1.2, MAT1.2 (bits 7:6 & 3:2)

; configure timer1
(write     59 timer1 #x0C)  ; set Prescale Register for 1 micro-secs timing (at 60MHZ) via T1PR
(write   #x00 timer1 #x70)  ; set timer1 to timer mode via T1CTCR
(write   #x04 timer1 #x14)  ; set timer1 to stop timer on MR0 and do nothing on MR2 via T1MCR
(write  50000 timer1 #x18)  ; set timer1 to stop after 50 milli-secs (if no echo) via T1MR0
(write    100 timer1 #x20)  ; set 100 micro-secs sonar control pulse on MAT1.2 via T1MR2
(write   #x80 timer1 #x28)  ; set CAP1.2 to capture timer value on falling edge of P0.19 via T1CCR

; function to get sonar reading as round-trip echo time in micro-seconds
(define (read-sonar)
  (write   #x02 timer1 #x04)  ; reset and stop TIMER1 via T1TCR
  (write #x0104 timer1 #x3C)  ; set timer1 to make P0.17 high first, then low on MR2 match via T1EMR
  (write   #x01 timer1 #x04)  ; start TIMER1 via T1TCR
  (define (wait status)
    (if (= 0 (logand #x01 status))  ; is timer stopped?
	(read timer1 #x34)          ; if so,  read echo time in micro-seconds + 100 from T1CR2
	(wait (read timer1 #x04)))) ; if not, loop with status update from T1CR
  (wait 1))

; function to get sonar reading and return it as a distance in centimeters
(define (sonar-cm)
  (/ (- (read-sonar) 100) 2 29.033))

; read the sonar
(sonar-cm)



 

Real-Time Clock:



; Armpit Real-Time Clock Example (see also NXP/Philips AN10382)
; tested on Tiny2131

; define useful port and offsets
(define rtc #xE0024000) ; RTC (ILR = #x00, CCR = #x08, PREINT = #x80, PREFRAC = #x84)
(define year   #x3C)
(define month  #x38)
(define dom    #x2C)
(define hour   #x28)
(define minute #x24)
(define second #x20)

; configure the RTC
(write 2    rtc #x08) ;
(write 3    rtc #x00) ; clear any pending clock interrupt via ILR
(write 1830 rtc #x80) ; 
(write 1792 rtc #x84) ; 
(write 1    rtc #x08) ; 

; define the cadr, caddr and null? functions
(define (cadr l) (car (cdr l)))
(define (caddr l) (cadr (cdr l)))
(define (null? l) (eq? l '()))

; function to read/set the date
(define date
  (lambda opt-date-list
    (if (null? opt-date-list)
	(cons (read rtc month) (cons (read rtc dom) (cons (read rtc year) '())))
	((lambda ()
	  (write (car   opt-date-list) rtc month)
	  (write (cadr  opt-date-list) rtc dom)
	  (write (caddr opt-date-list) rtc year))))))

; function to read/set the time
(define time
  (lambda opt-time-list
    (if (null? opt-time-list)
	(cons (read rtc hour) (cons (read rtc minute) (cons (read rtc second) '())))
	((lambda ()
	  (write (car   opt-time-list) rtc hour)
	  (write (cadr  opt-time-list) rtc minute)
	  (write (caddr opt-time-list) rtc second))))))

; reading the date
(date)

; setting the date to July 16, 2006
(date 7 16 2006)

; reading the time
(time)

; setting the time to 4:10:00am
(time 4 10 0)


 

I2C Communication:



; Armpit Scheme I2C Communication Example 1: Code for Echo by the Slave Device (flashed as scheme startup program)
; Tested on Tiny2131

(flash)

(define i2c    #xE001C000)

(define pinsel #xE002C000)

(write (logior #x50 (logand #xFFFFFF0F (read pinsel 0))) pinsel 0)

(write 47   i2c #x10)

(write 103  i2c #x14)

(write #x28 i2c #x18)

(write #x44 i2c #x00)

(write 40 i2c #x0C)

(define (echo)
  (write (read i2c) i2c)
  (echo))

(echo)

ctrl-d



; Armpit Scheme I2C Communication Example 1: Code for the Master Device
; Tested on Tiny2131

; define useful ports
(define i2c    #xE001C000) ; I2C0    port (CONSET = #x00, STAT = #x04, SCLH = #x10, SCLL = #x14, CONCLR = #x18)
(define pinsel #xE002C000) ; PINSEL0 port (0 = #x00, 1 = #x04, 2 = #x14)

; configure MCU pins for I2C0 operation
(write (logior #x50 (logand #xFFFFFF0F (read pinsel 0))) pinsel 0) ; configure P0.2 and P0.3 as I2C via PINSEL0

; initialize the I2C0 peripheral
(write 47   i2c #x10) ; set I2C high clock period to 0.783 us (60 MHz pclck, 400kb/s) via I2C0SCLH
(write 103  i2c #x14) ; set I2C low period to 1.716 us (60 MHz pclck, 400kb/s) via I2C0SCLL
(write #x28 i2c #x18) ; clear STA and SI via I2C0CONCLR
(write #x44 i2c #x00) ; enable I2C on port 0, both master and slave (set I2EN, AA) via I2C0CONSET

; see if anything is available on the input port
(eof-object? (read i2c '#(20)))

; send an integer to the Slave device
(write 123 i2c '#(20))
; read the data back from the Slave device
(read i2c '#(20))

; send #t to the Slave device
(write #t i2c '#(20))
; read the data back from the Slave device
(read i2c '#(20))

; send a string to the Slave device
(write "hello" i2c '#(20))
; read the data back from the Slave device
(read i2c '#(20))


Note: for the remote evaluation example (I2C example 2, below) the LPC2131 can only be used as a Slave device since it does not have the Armpit Scheme Level 2 Addendum function pack. The result of the evaluation, to be sent back to the Master device, must be an immediate (integer, float, character, #t/#f, '() ...) or a string (no pointers -- vectors of constants are o.k.). The Master device needs to be an MCU with Level 2 functionality so that it can pack the object to be sent to and evaluated by the LPC2131 (i.e. any MCU except the LPC2131). The object sent to the 2131 cannot include closures that involve Level 2 functions because the LPC2131 cannot evaluate them.


; Armpit Scheme I2C Communication Example 2: Code for Evaluation by the Slave Device (flashed as scheme startup program)
; Tested on Tiny2131

(flash)

(define i2c    #xE001C000)

(define pinsel #xE002C000)

(write (logior #x50 (logand #xFFFFFF0F (read pinsel 0))) pinsel 0)

(write 47   i2c #x10)

(write 103  i2c #x14)

(write #x28 i2c #x18)

(write #x44 i2c #x00)

(write 40 i2c #x0C)

(define (rep)
  (write (eval (unpack (read i2c))) i2c)
  (rep))

(rep)

ctrl-d



; Armpit Scheme I2C Communication Example 2: Code for the Master Device (to communicate with LPC2131)
; Tested on Tiny2106

; define useful ports
(define i2c    #xE001C000) ; I2C0    port (CONSET = #x00, STAT = #x04, SCLH = #x10, SCLL = #x14, CONCLR = #x18)
(define pinsel #xE002C000) ; PINSEL0 port (0 = #x00, 1 = #x04, 2 = #x14)

; configure MCU pins for I2C0 operation
(write (logior #x50 (logand #xFFFFFF0F (read pinsel 0))) pinsel 0) ; configure P0.2 and P0.3 as I2C via PINSEL0

; initialize the I2C0 peripheral
(begin
  (write 47   i2c #x10) ; set I2C high clock period to 0.783 us (60 MHz pclck, 400kb/s) via I2C0SCLH
  (write 103  i2c #x14) ; set I2C low period to 1.716 us (60 MHz pclck, 400kb/s) via I2C0SCLL
  (write #x28 i2c #x18) ; clear STA and SI via I2C0CONCLR
  (write #x44 i2c #x00)) ; enable I2C on port 0, both master and slave (set I2EN, AA) via I2C0CONSET

; remotely sum a list of numbers
(write (pack '(+ 3 5 7 9 11 13 15)) i2c '#(20))

; get the result back from the Slave device
(read i2c '#(20))

; closure to get mcu-id
(define (mcu-id) (ash (read i2c #x0C) -1))

; read remote mcu-id
(write (pack (list mcu-id)) i2c '#(20))

; get the result back from the Slave device
(read i2c '#(20))


 

Expert System:


The Expert System example is too memory intensive for the LPC2131.



 

miniKANREN:


MiniKANREN is too memory intensive for the LPC2131.



 

Brain-Candy:



; Armpit Example of Applicative Order Y-combinator, for anonymous recursion
; tested on Tiny2131

; the Y-combinator:
(define Y
 (lambda (g)
  ((lambda (f) (f f))
   (lambda (f) (g (lambda (x) ((f f) x)))))))

; its application to calculating 7! :
((Y (lambda (f) (lambda (n) (if (< n 2) 1 (* n (f (- n 1)))))))
 7)


Last updated February 9, 2007

bioe-hubert-at-sourceforge.net