m0a0t0
Posts: 2
Joined: Mon Dec 03, 2012 5:15 pm

Accessing the GPIO through C

Mon Dec 03, 2012 5:31 pm

Hi everyone. I've been following the "Baking pi" tutorial (http://www.cl.cam.ac.uk/freshers/raspbe ... orials/os/) and decided to try and write the GPIO functions in C. So far I have this (in gpio.c):

Code: Select all

#define GPIO_ADDR 0x20200000

unsigned int get_gpio_addr() {
    return GPIO_ADDR;
}

int set_gpio_func(unsigned int pin, unsigned int func) {
    if (pin >= 54 || func >= 7) {
        return 0;
    }
//    pin += 1;
    int block_addr = GPIO_ADDR + 4 * (pin / 10); // one block per ten pins
    int pin_n = pin % 10;
    pin_n *= 3; // 3 bits per pin
    func = func << pin_n;
    unsigned char* block_addr_p = (unsigned char*) block_addr;
//    *block_addr_p = func;
    asm("str %1, [%0]" : "=r"(block_addr) : "r"(func));
    return 1;
}

int set_gpio(int pin, int val) {
    if (pin >= 54) {
        return 0;
    }
    int block = pin / 32; 
    block *= 4; // block is 4 bytes long
    block += GPIO_ADDR;
    int set_bit = 1 << (pin % 32);
    unsigned char* addr = (unsigned char*) (block + (val == 0 ? 40 : 28));
    *addr = set_bit;
    //int n = val == 0 ? 40 : 28;
    //asm("str %0, [%1, #40]" : "=r"(set_bit) : "r"(block));
    return 1;
}

int cmain() {
    set_gpio_func(16, 1);
    set_gpio(16, 0);
    return 0;
}
I'm compiling this with the command:

Code: Select all

arm-linux-gnueabi-gcc -Wno-unknown-pragmas -c source/*.c -mcpu=arm1176jzf-s
Finally I'm gluing this together with the following assembly code (main.s):

Code: Select all

.section .init
.globl _start
_start:
b main
.section .text
main:
mov sp,#0x8000
ldr r0,=0x20200000

/*mov r1,#1
lsl r1,#18
str r1,[r0,#4]
*/
bl cmain
/*
mov r1,#1
lsl r1,#16
str r1,[r0,#40]
*/
loop$:
b loop$

However it does not work. I have tried just calling one of the functions and pasting the assembly for the other function from the tutorial before/after the bl cmain, which still doesn't work. Does anyone know why?

BrianW
Posts: 83
Joined: Sun Jul 29, 2012 9:03 pm

Re: Accessing the GPIO through C

Wed Dec 05, 2012 1:30 am

Hi,

There are a couple of reasons why it won't work

Code: Select all

    asm("str %1, [%0]" : "=r"(block_addr) : "r"(func));
This specifies that block_addr is an output of this instruction, not an input to it. In other words, gcc expects that instruction to put something into whatever register it picks as %1, which it will then store in block_addr.

The first colon after the instruction specifies the output(s) of the instruction. The second specifies all the inputs. It should be this:

Code: Select all

        asm("str %1, [%0]" : : "r"(block_addr), "r"(func));
Secondly, reads/writes to the GPIO (and most of the other peripherals) should be words, not bytes.

Code: Select all

        unsigned char* addr = (unsigned char*) (block + (val == 0 ? 40 : 28));
should be

Code: Select all

        unsigned int* addr = (unsigned int*) (block + (val == 0 ? 40 : 28));
These two change were enough for it to work for me.

User avatar
Arjan
Posts: 261
Joined: Sat Sep 08, 2012 1:59 pm

Re: Accessing the GPIO through C

Wed Dec 05, 2012 1:05 pm

Hi,

I would use

Code: Select all

 uint32_t * addr = (uint32_t *) (block + (val == 0 ? 40 : 28));
And

Code: Select all

sizeof(uint32_t)
to allow pointer addition.

The above gives you the proper 32-bit type definition.
http://www.raspberrypi-dmx.org/
Open Source DMX/RDM/MIDI/OSC/Art-Net/sACN solutions

m0a0t0
Posts: 2
Joined: Mon Dec 03, 2012 5:15 pm

Re: Accessing the GPIO through C

Wed Dec 05, 2012 3:45 pm

Thank you both very much.

Return to “Bare metal, Assembly language”