Saturday, January 8, 2011

Digital I/O on the beagleboard using GPIO and the expansion header

I needed to send a digital output signal from my Beagleboard to a data acquisition system. This turned out to be more challenging than I thought it would be. I'm posting these notes in case they're helpful for anyone else.

I used as my reference this excellent post [Edit: the post moved to here]. Because I wanted to do output rather than input, it wasn't perfectly applicable, but a lot of what they do applies.

To access the necessary registers from user space, they use memory mapping. They then set up the Beagleboard expansion header pins to map to GPIO Bank 5, set the appropriate pullup registers, and enable input. They then set up Bank 5 as an input, and read from it.

I first tried to mirror that setup, but doing output instead. This didn't exactly work for me. One /huge/ problem is that I am communicating with my beagleboard over a USB network adaptor. For some reason (that I have yet to figure out), toggling some of the bits on GPIO5 clobbers the USB interface. I don't know why, but I do know that only happens with the high bits, so I settled for using the low bits.

Here's the relevant C code for output, based on the aforementioned post:

includes:


#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>


Setting the pin configuration:
//O_SYNC makes the memory uncacheable

int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
  sprintf(stderr,"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) {
  sprintf(stderr,"Pinconf Mapping failed\n");
  close(fd);
  return 0;
}
// set lower 16 pins to GPIO bank5
pinconf[0x2158/4] = 0x00040004;
pinconf[0x215C/4] = 0x00040004;
pinconf[0x2160/4] = 0x00040004;
pinconf[0x2164/4] = 0x00040004;
/* pinconf[0x2168/4] = 0x00040004; */
/* pinconf[0x216C/4] = 0x00040004; */
/* pinconf[0x2170/4] = 0x00040004; */
/* pinconf[0x2188/4] = 0x00040004; */
close(fd);

GPIO Bank 5 configuration:

volatile ulong * gpio_fd = open("/dev/mem", O_RDWR | O_SYNC);
if (gpio_fd < 0) {
  sprintf(stderr,"Could not open memory\n");
  return 0;
}
// First set all output on bank5 to high
// (set_data_out has offset 0x94)
gpio[0x6094/4]=0xFFFFFFFF;

// Configure low 16 GPIO pins on bank 5 as output.
// GPIO 5 is at physical address 0x49056000 = 0x49050000+0x6000
// GPIO Output enable (GPIO_OE) is offset by 0x34 for each bank
// (set low for output)
gpio[0x6034/4] = 0x00000000;
// Also disable the wakeupenable and irqenable intertupts
// GPIO clear_Wakeupenable is offset by 0x80 for each bank
gpio[0x6080/4] = 0x0000FFFF;
// GPIO clear_irqenable1 is offset by 0x60 for each bank
gpio[0x6060/4] = 0x0000FFFF;
// GPIO clear_irqenable2 is offset by 0x70 for each bank
gpio[0x6070/4] = 0x0000FFFF;


Toggling the pins from high to low, waste some time, then toggle pin back to high:

//clear_data_out has offset 0x90
gpio[0x6090/4]=0x0000FFFF;
usleep(500);
for (i=0;i<25000;i++);
//set_data_out has offset 0x94
gpio[0x6094/4]=0x0000FFFF;
usleep(500);


I do not know if the "usleep" commands are absolutely necessary, but I couldn't get this to work without them.

8 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hi Alex - I'm interested in adapting this for the overo as well. Would you mind letting me know what parts of the code had to be modified to get it to work?

    ReplyDelete
  3. Ended up using a sys call with devmem2
    and using sysfs

    system("devmem2 0x480021E0 h 0x10");

    eg
    (fp = fopen("/sys/class/gpio/gpio10/direction", "rb+")
    fp = fopen("/sys/class/gpio/export", "ab")
    (fp = fopen("/sys/class/gpio/gpio10/value", "rb+")
    then fwrite and fread

    original code from
    http://groups.google.com/group/beagleboard/browse_thread/thread/db4accfabf02d986/c623a16637625685?pli=1

    Trying to keep it simple as its for examples for students to use and build on.

    ReplyDelete
  4. Thanks very much for posting this back! Really helpful.

    ReplyDelete
  5. see gpio2.c code example at
    http://alexthegeek.com/omap/overo/code/

    ReplyDelete
  6. I've just added some adc code to the gumstix wiki
    at http://wiki.gumstix.org/index.php?title=Category:How_to_-_adc

    on reading adc's via both system calls and sysfs via fopen and fread

    ReplyDelete
  7. Hi,

    I'm trying to design a home automatic system using begaleboard XM, hence I need to access those GPIO. Here are my questions:

    1) I believe in order to use the GPIO I need to use user-space to do it, so is user-space code (your sample code) embedded with my application code?

    2) How did you get those values and what does it mean?
    pinconf[0x2158/4] = 0x00040004;
    pinconf[0x215C/4] = 0x00040004;
    pinconf[0x2160/4] = 0x00040004;
    pinconf[0x2164/4] = 0x00040004;

    3) Is using a text editor and native toolchain good enough to control the GPIO?

    Sorry if I asked too many questions, coz I'm really new to these stuff. And I really need your helps = )

    Thank You very much.
    Regards,
    Fiona

    ReplyDelete
  8. Hi Fiona,

    So normally one can't use GPIO from userspace. It requires getting to the GPIO configuration registers, which are normally in a memory location that userspace doesn't have access to. Parts of the code above "remap" the memory so that it can be accessed from userspace.

    More info on that in this blog post here:
    http://41j.com/blog/2011/09/beagleboard-gpio-input-driverless/

    2) The point of this code is to set the physical pins on the OMAP 3530 as output pins (rather than input, or some other state). I think you can find more in the TI documentation here: http://focus.ti.com/lit/ug/sprufd5a/sprufd5a.pdf

    3) Yes, that's all I use too.

    ReplyDelete