Skip to main content
\(\newcommand{\doubler}[1]{2#1} \newcommand{\binary}{\mathtt} \newcommand{\hex}{\mathtt} \newcommand{\octal}{\mathtt} \newcommand{\prog}{\mathtt} \newcommand{\lt}{<} \newcommand{\gt}{>} \newcommand{\amp}{&} \)

Section19.3Programming 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

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.

Solution

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