Control ARM (Raspberry Pi) GPIO pin in assembly using functions

The aim of this tutorial is to explain Baking Pi - operating Systems development, Lesson 3 OK03 published by Alex Chadwick, University of Cambridge.
My approach is to understand the functions of lesson 3 based on the initial programs in lesson 1.

1. First function, load the GPIO address in r0


1    .Global GetGpioAddress // this function takes r0 (GPIO address as input)
2    GetGpioAddress:
3    ldr r0, =0x20200000

2. Second Function, SetGpioFunction


In this function we will set what our pin will work as. 
Every pin is represented by 3 bits, the following table shows all the possible function for an ARM GPIO pin through bits configuration:

8 (2^3) possibilities:

000    GPIO pin X is an input
001    GPIO pin X is an output
100    GPIO pin X takes alternate function 0
..
010    GPIO pin X takes alternate function 5

in our example we are interested in 001 value which is making the concerned pin works as an output.

4    .Global SetGpioFunction // this function take r0 (pin) and   r1 (function) as inputs
5    SetGpioFunction:
6    cmp r0, #53
7    cmpls r1, #7 // cmpls means that this instruction will be executed only if in the previous comparison, r0 was less or the same as #53
8    movhi pc, lr // if r0 was higher than #53 function ends. If r0 wasn't higher that #53 but r1 is higher than #7 function will also end

Lines from 6 to 8 implement the following algorithm. The goal is to verify that r0 is in the range of ARM GPIO pin number and that r1 is in the range of possible functions (described in the table above)

- Figure 1 -

About mov pc, lr

lr contains the address of the code that we have to go back to when a method finishes
pc contains the address of the next instruction to be run
mov pc, lr finishes the function and go to the next instruction 

9    push {lr} //we must do this because we will call a function (GetGpioAddress) inside this function (SetGpioFunction)=> we will need lr to store the address to come back to in our function
10   mov r2,r0 //we have to move the GPIO in number out of r0 so it doesn't get overridden when calling GetGpioAddress function
11   bl GetGpioAddress //bl calls a function by updating the lr to the next instructions address, and then branching to the function

STOP /!\ At this point, the current registers values are the following

r0=GPIO address
r1=the function code
r2=the GPIO pin number

For what block does the pin belong ?

GPIO pins are stored in blocks of 10 pin (GPIO address is organized 4 bytes=32 bits and every pin is represented by 3 bits => 32/3=10, the remainder is 2 and they are reserved bits that will not be used here. please refer to the following table for more details.

Example, the pin number 16 belongs the the second block:

- Table 1 -

12   functionLoops$
13       cmp r2, #9 // compare r2 with 9 (to decide if the pin belongs to the first set 0-9)
14       subhi r2, #10 // substrat 10 from r2 if it is higher than 9
15       addhi r0, #4 // add 4 the r0 (GpioAddree) if not
16       bhi functionLoop$

The lines from 12 to 16 will find to what block of ten pins does our pin belongs. To understand these lines let's use the following flow chart (figure 2).

- Figure 2 -

STOP /!\ At this point, the current registers values are the following

r0=0x20200004
r1=001
r2=6 (pin number 6 of the second block)

17   add r2, r2, lsl #1 //equivalent to multiplying r2 by 3 => we do so because every pin is represented by  bits (r2=18), refer to table 1.
18   lsl r1,r2 //left shift r1 (0000 0000 0000 0001) by 18 position => put the code in the appropriate location, refer to table 1.

r1 before operation in line 18


- Table 2 -

r1 after operation in line 18


- Table 3 -

18   str r1, [r0] //put the content of r1 in 0x20200004 (calculated in line 12-16)
19   pop pc //put the content of lr (pushed in stack at line 9) in pc => this will end the function (Equivalent to mov pc,lr)


3. Another function to set GPIO pin function settings (turn on/off a pin)


This function will take a GPIO pin number as its first input in r0, and a value as its second in r1. If r0=0 if turns the pin output off, if not it turns the pin output on.


1    .gloal SetGpio //make the function accessible from other files
2    SetGpio:
3    pinNum .req r0 //pinNum now means r0
4    pinVal .req r1 //pinVal now means r1

5    cmp pinNum,#53
6    movhi pc, lr //line 5 and 6, check the pinNum is valid and exit function if not
7    push {lr} //save lr value in the stack (because we will call another function)
8    mov r2,pinNum //r0 will be used in GetGpioAddress function
9    .unreq pinNum //remove pinNum alias from r0
10   pinNum .req r2 //make a new alias 
11   GetGpioAddress //call GetGpioAddress => r0=0x20200000
12   gpioAddr .req r0 //make a new alias

GPIO has two sets of 4 bytes each for turning pins on and off. 
- 0x2020 001C [pin 0-31], 0x20200020 [pin 32-53] for turning pins on.
- 0x20200028[pin 0-31],0x2020002C[pin 32-53] for turning pins off.
Please refer to the following table (table 4)


- Table 4 -

To determine which set it is in, we need to divide the pin number by 32, if the result is 0, the pin belongs to the first set and if the result is 1, the pin belongs to the second set. We then multiply by 4 since it is a set of 4 bytes.

13   pinBank .req r3 //make a new alias 
14   lsr pinBank,pinNum,#5  //divide pinNum(r2) by 32 and save the result to pinBank (r3)
15   lsl pinBank,#2 //multiply the result by 4 => two results 0 or 4
16   add gpioAddr,pinBank // two possible results: a & b (view previous table)
17   .unreq pinBank //remove alias



At this point:
gpioAddr=0x20200004 or gpioAddr=0x20200000


18   and pinNum, #31 // pinNum will get the remainder of dividing pin num by 31



Example: 
if the pinNum is 16 (<31) pinNum=16 (the 16th bit in the first set 0-31)
if the pinNum is 45(>31) pinNum=13(the 13th bit in the second set 32-53)


- Table 5 -


19   setBit .req r3 // make a new alias 
20   mov setBit, #1 // put 1 in r3
21   lsl setBit, pinNum // left shift r3 by pinNum (Exp: if the pinNum is 16, put 1 in the 16th bit)
22   .unreq pinNum // remove the alias

23   teq pinVal, #0 // teq=test equality => compare pinVal with 0
24   .unreq pinVal // remove alias
25   streq setBit, [gpioAddr, #40] // turn off if the pinVal=0
26   streq setBit, [gpioAddr, #28] // turn on if the pinVal!=0
27   .unreq setBit // remove alias
28   .unreq gpioAddr // remove alias
29   pop {pc} //return 





No comments:

Post a Comment