Beagleboard GPIO Input (driverless)

The following example C program configures the beagleboard expansion header pins as GPIO inputs. the pins are configured all pull up. It then reads from these pins and output “1″ if pin 3 ONLY is shorted to ground.

The code bypasses the kernel completely by mapping the memory containing the GPIO configuration registers directly. It does this by mmaping /dev/mem, to access system memory. You need to use O_SYNC when you do this to tell mmap the memory is uncacheable. It’ll work /a bit/ without but you’ll get weird artifacts.

Once you have access to memory you need to configure the pins to be used as GPIO. Each pin on the OMAP3530 can have up to 6 functions, by default the expansion header pins are configured for use as MMC and I2C ports. The pinconf code reconfigures these as GPIO, an input bit also needs to be set here. Next there another GPIO configuration register, which also needs setting to 1 for input. After this you can read GPIO data from the GPIO input register for bank 5. You can’t configure all the pins on bank 5 as input on the beagleboard, a couple of them are used for the main MMC port.

Bypassing the kernel isn’t the best way to access GPIO from userland. It’s possibly useful though if you’re planning to migrate the code in to a driver.

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

int main() {

  int fd = open("/dev/mem", O_RDWR | O_SYNC);

  if (fd < 0) {
    printf("Could not open memory\n");
    return 0;
  }

  // Pad configuration
  volatile ulong *pinconf;
  pinconf = (ulong*) mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x48000000);
  if (pinconf == MAP_FAILED) {
    printf("Pinconf Mapping failed\n");
    close(fd);
    return 0;
  }

  // Configure Expansion header pins as input.
  pinconf[0x2158/4] = 0x011C011C;
  pinconf[0x215C/4] = 0x011C011C;
  pinconf[0x2160/4] = 0x011C011C;
  pinconf[0x2164/4] = 0x011C011C;
  pinconf[0x2168/4] = 0x011C011C;
  pinconf[0x216C/4] = 0x011C011C;
  pinconf[0x2170/4] = 0x011C011C;
  pinconf[0x2188/4] = 0x011C011C;
  close(fd);

  fd = open("/dev/mem", O_RDWR | O_SYNC);
  
  // GPIO Configuration: configure are input
  volatile ulong *gpio;
  gpio = (ulong*) mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x49050000);
  if (gpio == MAP_FAILED) {
     printf("Gpio Mapping failed\n");
     close(fd);
     return 0;
  }

  // Configure all GPIO pins on bank 5 as input.
  gpio[0x6034/4] = 0xFFFFFFFF;

  // Prints 1 when pin 3 of the Expansion header is Grounded.
  int c=0;
  for(;;) {
    if(gpio[0x6038/4] == 201390076) printf("1\n");
    // printf("gpio5: %d \n",gpio[0x6038/4]);
  }
}

3 thoughts on “Beagleboard GPIO Input (driverless)

  1. There comments are copied over from my blogger blog (sorry for the confusion, I’ve got fed up with blogger). Some of them are quite useful so I’ve copied them here:

    BBFP said…
    Hi!
    I’m trying to set up an SPI communication with a peripheral on BeagleBoard and I’m trying to figure out how to do it by using what you suggest in your comment.
    Your code is very useful also to newbie like me!
    There is something I don’t understand, especially regarding the line:

    gpio[0x6034/4] = 0xFFFFFFFF;

    and

    if(gpio[0x6038/4] == 201390076) printf(“1\n”);

    How did you get the 6034 and 6038 values?

    Thank you very much in advance

    F

    March 18, 2009 9:21 AM
    BBFP said…
    Ok, I solved!
    Just take a look at:
    http://focus.ti.com/lit/ug/sprufd5a/sprufd5a.pdf

    F

    March 18, 2009 9:41 AM
    Jon Elson said…
    Hi,

    Thanks VERY much for posting this code example. I have a Beagle Board that was set up with a Debian install on an SD card. I tried your example code, and it locks up the system. I understand that it takes over bank 5 I/O, which maybe interferes with using the SD card slot. Can you comment on this?

    I am working on the first experiments aimed at building an EPP (IEEE-1284) parallel port emulator for the Beagle, to support some existing devices that communicate over a PC’s par port.
    I need extremely fast I/O, able to transfer a byle in less than a us. Eventually, this driver will be run as a real-time kernel module, but I want to have a user-mode version for diagnostics, too.

    I will need to use about 13 signals on the expansion header.
    I have already noticed that there is no continuous, aligned byte available on the expansion header, but I think I can get around that with a bit shift.

    Thanks,

    Jon

    September 2, 2009 9:38 AM
    new said…
    I don’t know why that code should take the system out. It could be due to a difference in board revisions (I was using B2 IIRC). Perhaps they’ve wired things differently on newer revisions.

    IIRC I was also using the handhelds mojo distribution, though this shouldn’t make any difference.

    September 3, 2009 9:52 AM
    v4vendetta said…
    Hi,

    I have question that I did you land up with this values?

    pinconf[0x2158/4] = 0x011C011C;
    pinconf[0x215C/4] = 0x011C011C;
    pinconf[0x2160/4] = 0x011C011C;
    pinconf[0x2164/4] = 0x011C011C;
    pinconf[0x2168/4] = 0x011C011C;
    pinconf[0x216C/4] = 0x011C011C;
    pinconf[0x2170/4] = 0x011C011C;
    pinconf[0x2188/4] = 0x011C011C;

    Can you please explain???

    September 11, 2009 12:23 PM
    v4vendetta said…
    Hi,

    When i tried to compile this ..it is giving me this errors??

    build/core/copy_headers.mk:15: warning: overriding commands for target `out/target/product/generic/obj/include/libpv/getactualaacconfig.h’
    build/core/copy_headers.mk:15: warning: ignoring old commands for target `out/target/product/generic/obj/include/libpv/getactualaacconfig.h’
    make: execvp: /bin/bash: Argument list too long
    target thumb C: gpiodriver < = external/gpiodriver//gpiodriver.c
    external/gpiodriver//gpiodriver.c: In function 'main':
    external/gpiodriver//gpiodriver.c:19: error: expected '=', ',', ';', 'asm' or '__attribute__' before '*' token
    external/gpiodriver//gpiodriver.c:19: error: 'pinconf' undeclared (first use in this function)
    external/gpiodriver//gpiodriver.c:19: error: (Each undeclared identifier is reported only once
    external/gpiodriver//gpiodriver.c:19: error: for each function it appears in.)
    external/gpiodriver//gpiodriver.c:20: error: 'ulong' undeclared (first use in this function)
    external/gpiodriver//gpiodriver.c:20: error: expected expression before ')' token
    external/gpiodriver//gpiodriver.c:41: error: expected '=', ',', ';', 'asm' or '__attribute__' before '*' token
    external/gpiodriver//gpiodriver.c:41: error: 'gpio' undeclared (first use in this function)
    external/gpiodriver//gpiodriver.c:42: error: expected expression before ')' token
    make: *** [out/target/product/generic/obj/EXECUTABLES/gpiodriver_intermediates//gpiodriver.o] Error 1

    September 11, 2009 1:03 PM
    a2 said...
    Hey v4vendetta!

    Just put main(){ ... } around it and it will compile.

    But that didn't keep the mmap() from failing on me :(

    December 18, 2009 11:13 PM
    Chao said...
    I have the same problem as Jon. The access to /dev/mem RDWR will lock up the system.

    Is there a work around?

    Jon, how did you work it out?

    Thanks

    February 20, 2010 9:48 PM
    Tallak Tveide said...
    On my beagleboard running the program caused some errors regarding USB. My board still runs fine from the serial console. If your communication is via USB this could explain the lockup.

    [ 2331.830169] hub 1-0:1.0: port 2 disabled by hub (EMI?), re-enabling...
    [ 2331.846527] usb 1-2: USB disconnect, address 2
    [ 2331.857147] usb 1-2.1: USB disconnect, address 3

    March 13, 2010 1:00 AM
    Milo_Nurv said...
    Hi! How did you get those values?

    June 15, 2010 5:05 PM
    new said...
    from the TI datasheet.

    June 16, 2010 1:52 AM
    Anonymous said...
    Hi, I tried your code but I receive an error opening /dev/mem

    Do you have any suggestion on how to overcame this problem?

    I'm using last Angstrom image

    SilverWolf78

    August 9, 2010 10:21 AM
    new said...
    erm chmod 777 /dev/mem ? or run the code as root? I've not used this code for quite a while.

    August 9, 2010 3:19 PM
    cp said...
    This was an excellent post, thanks so much. I adapted it for my own uses (digital output) and posted tidbits of my code. In case that's useful for anyone else, it can be found here:

    http://x4350.blogspot.com/2011/01/digital-io-on-beagleboard-using-gpio.html

    January 11, 2011 9:19 PM
    drwho said…
    “You need to use O_SYNC when you do this to tell mmap the memory is uncacheable. It’ll work /a bit/ without but you’ll get weird artifacts.”

    Wow, right in the nail! That’s the problem I was having in my “userland” home-made driver.

    Thank you very much for sharing this information!

    -scop.

    September 27, 2011 11:57 AM

  2. Someone asked how it worked on stack overflow here was my answer:

    I’m the original blog/code author, here’s what I meant:

    To answer your main point, pinconf[0x2168/4] refers to the address 0×48002168. The pinconf array starts at the address 0×48000000. It’s been defined as a ulong [1], which on ARM processors is 4 bytes. I know I want to access the address pinconf+0×2168. To convert the address 0×2168 into an index in pinconf I need to divide by 4.

    —-

    Walking through the code from the start:

    volatile ulong *pinconf;

    pinconf is defined as a ulong (32bit int) pointer. It’s defined as volatile, this means that something outside of our code may change it’s value. It tells the compiler that every time we use that value we need to read it from memory, this stops the compiler doing clever optimisations that may screw things up.

    pinconf = (ulong*) mmap(NULL, 0×10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0×48000000);

    This sets pinconf to point to the address 0×48000000. Normally you could do something like:

    pinconf = (ulong*) 0×48000000;

    To make pinconf point to an address, but this won’t work. 0×48000000 is a protected address, it’s only accessible by the kernel. The mmap magic gives you a way to access the address from userspace.

    pinconf[0x2168/4] = 0x001C001C;

    We’ve covered this already, but this is writing a value to the address: 0×48000000+0×2168. The value 0×48002168 comes from the OMAP3 datasheets, and is used to do memory mapped IO with the GPIO system. We divide by 4 to convert the address 0×2168 to an index in pinconf.

    [1] I should probably have used a uint32_t to be honest.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>