Chris_W
Posts: 7
Joined: Thu Mar 27, 2014 12:40 pm

GPIO pin always low?

Thu Apr 03, 2014 12:31 am

I'm learning bare metal programming on the Raspberry Pi and try to read the pin level of a GPIO pin. These are my assembly functions:

Code: Select all

@-----------------------------------------
@ Configure GPIO16 as output
@-----------------------------------------

.globl RPI_LED_INIT
RPI_LED_INIT:
        ldr r0,=0x20200004
        mov r1,#1
        lsl r1,#18
        str r1,[r0]
        bx lr

@-----------------------------------------
@ Turn LED off
@-----------------------------------------

.globl RPI_LED_OFF
RPI_LED_OFF:
        ldr r0,=0x2020001c
        mov r1,#1
        lsl r1,#16
        str r1,[r0]
        bx lr

@-----------------------------------------
@ Turn LED on
@-----------------------------------------

.globl RPI_LED_ON
RPI_LED_ON:
        ldr r0,=0x20200028
        mov r1,#1
        lsl r1,#16
        str r1,[r0]
        bx lr

@-----------------------------------------
@ Read level of GPIO16
@-----------------------------------------

.globl RPI_PIN_LEVEL
RPI_PIN_LEVEL:
        ldr r0,=0x20200034
        lsr r0,#16
        and r0,r0,#1
        bx lr
The assembly functions are called by a C program:

Code: Select all

extern void RPI_LED_INIT(void);
extern void RPI_LED_OFF(void);
extern void RPI_LED_ON(void);
extern unsigned int RPI_PIN_LEVEL(void);
extern void RPI_LED_BLINK(void);

void notmain(void) {

	RPI_LED_INIT();
	RPI_LED_OFF();

	if (RPI_PIN_LEVEL() == 1)
		RPI_LED_BLINK();
}
The function RPI_LED_BLINK() is from Baking Pi and makes the LED blink infinitely. As there is no screen output, this is the only way I can see if my code works

The two functions RPI_LED_OFF() and RPI_LED_ON() works as expected by turning the LED off and on. The problem is that according to the manual, GPIO pin 16 should be high when setting bit 16 to 1 at address 0x2020001c, like I do in the RPI_LED_OFF() function. However, when reading the pin level after calling RPI_LED_OFF() it's always low.

Why is the pin level not high?

colinh
Posts: 95
Joined: Tue Dec 03, 2013 11:59 pm
Location: Munich

Re: GPIO pin always low?

Thu Apr 03, 2014 3:06 am

Code: Select all

.globl RPI_PIN_LEVEL
RPI_PIN_LEVEL:
        ldr r0,=0x20200034
        lsr r0,#16
        and r0,r0,#1
        bx lr
This doesn't look good.

You've loaded r0 with a number, divided it by 2^16 and ANDed the result with 1.

If you look really carefully at the LED on/off routines, you'll see that they use r1 as well as r0. And they use a STR instruction. They don't do this just for fun...

You'd need to use a LDR instruction instead.

Chris_W
Posts: 7
Joined: Thu Mar 27, 2014 12:40 pm

Re: GPIO pin always low?

Thu Apr 03, 2014 5:31 am

I have modified the function a little as I could not find the assembler reference used when writing the function, and with the new reference more arguments had to be used. However, the GPIO pin is still always high as before.

Code: Select all

.globl RPI_PIN_LEVEL
RPI_PIN_LEVEL:
        ldr r0,=0x20200034	  @ Load address of GPIO Pin Level 0 (GPLEV0) into r0
        lsr r1,r0,#16          @ Shift bits at address in r0, 16 places to the right and save result in r1
        and r0,r1,#1           @ Perform bitwise AND with bits in r1 and 1, then save result r0
        bx lr                  @ Return
This is how I visualize the function in my head if GPIO pin 16 is high:

Code: Select all

Bits in register GPLEV0:
 
00000000000000010000000000000000

Shift the bits 16 places to the right:

00000000000000000000000000000001

Perform bitwise AND:

Operand1: 00000000000000000000000000000001
Operand2: 00000000000000000000000000000001

Result:   00000000000000000000000000000001

The result is then passed to the caller in r0.
I'm not sure where to place the LDR instruction?

User avatar
rpdom
Posts: 18170
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: GPIO pin always low?

Thu Apr 03, 2014 7:33 am

As colinh said, you need another LDR instruction in there.

What you are doing is

Code: Select all

.globl RPI_PIN_LEVEL
RPI_PIN_LEVEL:
        ldr r0,=0x20200034     @ Load address of GPIO Pin Level 0 (GPLEV0) into r0
        lsr r1,r0,#16          @ Shift ***address*** in r0, 16 places to the right and save result in r1
        and r0,r1,#1           @ Perform bitwise AND with bits in r1 and 1, then save result r0
        bx lr                  @ Return
What you probably need is more like

Code: Select all

.globl RPI_PIN_LEVEL
RPI_PIN_LEVEL:
        ldr r0,=0x20200034     @ Load address of GPIO Pin Level 0 (GPLEV0) into r0
        ldr r0,[r0]            @ Load contents of address pointed to by r0 into r0
        lsr r0,r0,#16          @ Shift contents of  r0, 16 places to the right and save result in r0
        and r0,r0,#1           @ Perform bitwise AND with bits in r0 and 1, then save result r0
        bx lr                  @ Return
You don't actually need two registers to read the level, but you would if you wanted to preserve the address to write back to.

Chris_W
Posts: 7
Joined: Thu Mar 27, 2014 12:40 pm

Re: GPIO pin always low?

Mon Apr 07, 2014 8:44 am

Now I understand. I was not aware about the significance of the brackets when using the LDR instruction. It's like dereferencing a pointer in C.

After modifying the code as instructed, the RPI_PIN_LEVEL() function works as expected.

Thanks.

Return to “Bare metal, Assembly language”