Exercises 19.3 Programming Exercise
1.
Write an assembly language program that blinks an LED five time. The LED will be on for 1 second then off for 1 second. Use the sleep function for timing, which is described in man 3 sleep.
Hint
Solution
Start with the main function from Listing 19.1.1. Make a copy of the gpioPinSet function from Listing 19.2.8 and modify it for your gpioPinClr function.
See Listing 19.2.6 for gpioPinFSelect and Listing 19.2.8 for gpioPinSet.
@ blinkLED.s
@ Blinks LED connected between pins 1 and 11 on Raspberry Pi
@ GPIO connector once a second for five seconds.
@ 2017-09-30: Bob Plantz
@ Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified @ modern syntax
@ Constants for assembler
.equ PERIPH,0x3f000000 @ RPi 2 & 3 peripherals
@ .equ PERIPH,0x20000000 @ RPi zero & 1 peripherals
.equ GPIO_OFFSET,0x200000 @ start of GPIO device
@ The following are defined in /usr/include/asm-generic/fcntl.h:
@ Note that the values are specified in octal.
.equ O_RDWR,00000002 @ open for read/write
.equ O_DSYNC,00010000
.equ __O_SYNC,04000000
.equ O_SYNC,__O_SYNC|O_DSYNC
@ The following are defined in /usr/include/asm-generic/mman-common.h:
.equ PROT_READ,0x1 @ page can be read
.equ PROT_WRITE,0x2 @ page can be written
.equ MAP_SHARED,0x01 @ share changes
@ The following are defined by me:
.equ O_FLAGS,O_RDWR|O_SYNC @ open file flags
.equ PROT_RDWR,PROT_READ|PROT_WRITE
.equ NO_PREF,0
.equ PAGE_SIZE,4096 @ Raspbian memory page
.equ INPUT,0 @ use pin for input
.equ OUTPUT,1 @ use pin for ouput
.equ ONE_SEC,1 @ sleep one second
.equ PIN17,17 @ pin set bit
.equ FILE_DESCRP_ARG,0 @ file descriptor
.equ DEVICE_ARG,4 @ device address
.equ STACK_ARGS,8 @ includes sp 8-byte align
@ Constant program data
.section .rodata
.align 2
device:
.asciz "/dev/gpiomem"
devErr:
.asciz "Cannot open /dev/gpiomem\n"
memErr:
.asciz "Cannot map /dev/gpiomem\n"
@ The program
.text
.align 2
.global main
.type main, %function
main:
sub sp, sp, 24 @ space for saving regs
@ (keeping 8-byte sp align)
str r4, [sp, 4] @ save r4
str r5, [sp, 8] @ r5
str r6, [sp,12] @ r6
str fp, [sp, 16] @ fp
str lr, [sp, 20] @ lr
add fp, sp, 20 @ set our frame pointer
sub sp, sp, STACK_ARGS
@ Open /dev/gpiomem for read/write and syncing
ldr r0, deviceAddr @ address of /dev/gpiomem
ldr r1, openMode @ flags for accessing device
bl open
cmp r0, -1 @ check for error
bne gpiomemOK @ no error, continue
ldr r0, devErrAddr @ error, tell user
bl printf
b allDone @ and end program
gpiomemOK:
mov r4, r0 @ use r4 for file descriptor
@ Map the GPIO registers to a main memory location so we can access them
str r4, [sp, FILE_DESCRP_ARG] @ /dev/gpiomem file descriptor
ldr r0, gpio @ address of GPIO
str r0, [sp, DEVICE_ARG] @ location of GPIO
mov r0, NO_PREF @ let kernel pick memory
mov r1, PAGE_SIZE @ get 1 page of memory
mov r2, PROT_RDWR @ read/write this memory
mov r3, MAP_SHARED @ share with other processes
bl mmap
cmp r0, -1 @ check for error
bne mmapOK @ no error, continue
ldr r0, memErrAddr @ error, tell user
bl printf
b closeDev @ and close /dev/gpiomem
@ All OK, blink the LED
mmapOK:
mov r5, r0 @ use r5 for programming memory address
mov r0, r5 @ programming memory
mov r1, PIN17 @ pin to blink
mov r2, OUTPUT @ it's an output
bl gpioPinFSelect @ select function
mov r6, 5 @ blink five times
loop:
mov r0, r5 @ GPIO programming memory
mov r1, PIN17
bl gpioPinClr
mov r0, ONE_SEC @ wait a second
bl sleep
mov r0, r5
mov r1, PIN17
bl gpioPinSet
mov r0, ONE_SEC @ wait a second
bl sleep
subs r6, r6, 1 @ decrement counter
bgt loop @ loop until 0
mov r0, r5 @ memory to unmap
mov r1, PAGE_SIZE @ amount we mapped
bl munmap @ unmap it
closeDev:
mov r0, r4 @ /dev/gpiomem file descriptor
bl close @ close the file
allDone:
mov r0, 0 @ return 0;
add sp, sp, STACK_ARGS @ fix sp
ldr r4, [sp, 4] @ restore r4
ldr r5, [sp, 8] @ r5
ldr r6, [sp,12] @ r6
ldr fp, [sp, 16] @ fp
ldr lr, [sp, 20] @ lr
add sp, sp, 24 @ sp
bx lr @ return
.align 2
@ addresses of messages
deviceAddr:
.word device
openMode:
.word O_FLAGS
gpio:
.word PERIPH+GPIO_OFFSET
devErrAddr:
.word devErr
memErrAddr:
.word memErr
@ gpioPinClr.s
@ Clears a GPIO pin. Assumes that GPIO registers
@ have been mapped to programming memory.
@ Calling sequence:
@ r0 <- address of GPIO in mapped memory
@ r1 <- pin number
@ bl gpioPinClr
@ 2017-09-30: Bob Plantz
@ Define my Raspberry Pi
.cpu cortex-a53
.fpu neon-fp-armv8
.syntax unified @ modern syntax
@ Constants for assembler
.equ PIN,1 @ 1 bit for pin
.equ PINS_IN_REG,32
.equ GPCLR0,0x28 @ clear register offset
@ The program
.text
.align 2
.global gpioPinClr
.type gpioPinClr, %function
gpioPinClr:
sub sp, sp, 16 @ space for saving regs
str r4, [sp, 0] @ save r4
str r5, [sp, 4] @ r5
str fp, [sp, 8] @ fp
str lr, [sp, 12] @ lr
add fp, sp, 12 @ set our frame pointer
add r4, r0, GPCLR0 @ pointer to GPSET regs.
mov r5, r1 @ save pin number
@ Compute address of GPSET register and pin field
mov r3, PINS_IN_REG @ divisor
udiv r0, r5, r3 @ GPSET number
mul r1, r0, r3 @ compute remainder
sub r1, r5, r1 @ for relative pin position
lsl r0, r0, 2 @ 4 bytes in a register
add r0, r0, r4 @ address of GPSETn
@ Set up the GPIO pin funtion register in programming memory
ldr r2, [r0] @ get entire register
mov r3, PIN @ one pin
lsl r3, r3, r1 @ shift to pin position
orr r2, r2, r3 @ clear bit
str r2, [r0] @ update register
mov r0, 0 @ return 0;
ldr r4, [sp, 0] @ restore r4
ldr r5, [sp, 4] @ r5
ldr fp, [sp, 8] @ fp
ldr lr, [sp, 12] @ lr
add sp, sp, 16 @ restore sp
bx lr @ return
