Skip to content

Instantly share code, notes, and snippets.

@ajmontag
Created November 4, 2012 19:24
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ajmontag/4013192 to your computer and use it in GitHub Desktop.
Save ajmontag/4013192 to your computer and use it in GitHub Desktop.
A C program that listens for button presses on a Beaglebone GPIO pin and reports the press duration.
/**
* Title: gpio.c
*
* Author: Andrew Montag
* ajmontag@gmail.com
* sites.google.com/site/andrewmontag
*
* Licence: Boost Software Licence - Verison 1.0
* http://www.boost.org/users/license.html
*
* Purpose:
* Helpers for configuring and accessing GPIO pins.
*/
/**
* Original source from
* https://www.ridgerun.com/developer/wiki/index.php/Gpio-int-test.c
* as example code.
* Modularized by Andrew Montag, 10/30/2012
*
* Copyright (c) 2011, RidgeRun
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the RidgeRun.
* 4. Neither the name of the RidgeRun nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "gpio.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
/****************************************************************
* Constants
****************************************************************/
#define SYSFS_GPIO_DIR "/sys/class/gpio"
#define MAX_BUF 64
/****************************************************************
* gpio_export
****************************************************************/
int gpio_export(unsigned int gpio)
{
int fd, len;
char buf[MAX_BUF];
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
if (fd < 0) {
perror("gpio/export");
return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return 0;
}
/****************************************************************
* gpio_unexport
****************************************************************/
int gpio_unexport(unsigned int gpio)
{
int fd, len;
char buf[MAX_BUF];
fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
if (fd < 0) {
perror("gpio/export");
return fd;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return 0;
}
/****************************************************************
* gpio_set_dir
****************************************************************/
int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/direction");
return fd;
}
if (out_flag)
write(fd, "out", 4);
else
write(fd, "in", 3);
close(fd);
return 0;
}
/****************************************************************
* gpio_set_value
****************************************************************/
int gpio_set_value(unsigned int gpio, unsigned int value)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/set-value");
return fd;
}
if (value)
write(fd, "1", 2);
else
write(fd, "0", 2);
close(fd);
return 0;
}
/****************************************************************
* gpio_get_value
****************************************************************/
int gpio_get_value(unsigned int gpio, unsigned int *value)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fd = open(buf, O_RDONLY);
if (fd < 0) {
perror("gpio/get-value");
return fd;
}
gpio_get_value_fd(fd, value);
close(fd);
return 0;
}
int gpio_get_value_fd(int fd, unsigned int *value)
{
char ch;
read(fd, &ch, 1);
if (ch != '0') {
*value = 1;
} else {
*value = 0;
}
return 0;
}
/****************************************************************
* gpio_set_edge
****************************************************************/
int gpio_set_edge(unsigned int gpio, const char *edge)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0) {
perror("gpio/set-edge");
return fd;
}
write(fd, edge, strlen(edge) + 1);
close(fd);
return 0;
}
/****************************************************************
* gpio_fd_open
****************************************************************/
int gpio_fd_open(unsigned int gpio)
{
int fd, len;
char buf[MAX_BUF];
len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fd = open(buf, O_RDONLY | O_NONBLOCK );
if (fd < 0) {
perror("gpio/fd_open");
}
return fd;
}
/****************************************************************
* gpio_fd_close
****************************************************************/
int gpio_fd_close(int fd)
{
return close(fd);
}
/**
* Title: gpio.h
*
* Author: Andrew Montag
* ajmontag@gmail.com
* sites.google.com/site/andrewmontag
*
* Licence: Boost Software Licence - Verison 1.0
* http://www.boost.org/users/license.html
*
* Purpose:
* Helpers for configuring and accessing GPIO pins.
*/
#ifndef _GPIO_H_
#define _GPIO_H_
/**
* @ return 0 on success
*
* @param gpio the gpio pin number.
* If the pin is GPIOm_n, then the pin number is
* m * 32 + n. Example: GPIO3_21 = 3*32+21 = 117
*/
/**
* gpio_export
* export a gpio pin for use in the user space.
* must be called before the pin can be used.
*/
int gpio_export(unsigned int gpio);
/**
* gpio_unexport
* undo the export action.
*/
int gpio_unexport(unsigned int gpio);
#define GPIO_DIR_INPUT (0)
#define GPIO_DIR_OUTPUT (1)
/**
* gpio_set_dir
* @param out_flag true=output, false=input
*/
int gpio_set_dir(unsigned int gpio, unsigned int out_flag);
/**
* gpio_set_value
* writes the boolean value to the pin.
*/
int gpio_set_value(unsigned int gpio, unsigned int value);
/**
* gpio_get_value
* reads the state of the pin.
* @param as return, 0 or 1
*/
int gpio_get_value(unsigned int gpio, unsigned int *value);
/** @param fd an fd opened using gpio_fd_open */
int gpio_get_value_fd(int fd, unsigned int *value);
static const char* kPollEdge_rising = "rising";
static const char* kPollEdge_falling = "falling";
static const char* kPollEdge_both = "both";
/**
* gpio_set_edge
* @param edge should be "rising", "falling", or "both"
*/
int gpio_set_edge(unsigned int gpio, const char *edge);
/**
* gpio_fd_open
* @return an open an fd for later use.
* useful when using poll().
*/
int gpio_fd_open(unsigned int gpio);
/**
* gpio_fd_close
* close an open fd.
*/
int gpio_fd_close(int fd);
#endif
/**
* Title: press_duration.c
*
* Author: Andrew Montag
* ajmontag@gmail.com
* sites.google.com/site/andrewmontag
*
* Licence: Boost Software Licence - Verison 1.0
* http://www.boost.org/users/license.html
*
* Purpose:
* A program which listens for button presses on a GPIO pin and reports the
* press duration.
*
*/
#include "gpio.h"
#include <sys/time.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#define POLL_TIMEOUT_30_SEC (30 * 1000)
#define POLL_TIMEOUT_20_SEC (20 * 1000)
#define POLL_TIMEOUT_10_SEC (10 * 1000)
#define POLL_TIMEOUT_5_SEC (5 * 1000)
#define POLL_TIMEOUT_2_SEC (2 * 1000)
#define POLL_TIMEOUT_1_SEC (1 * 1000)
#define POLL_TIMEOUT_INF (-1)
#define MAX_BUF 64
void report_button_ms(int duration) {
printf("button pressed for %d ms.\n", duration);
}
int main(int argc, char **argv, char **envp)
{
struct pollfd pollfdset;
const int nfds = 1;
int timeout, rc;
char *buf[MAX_BUF];
unsigned int gpio;
int len;
// gpio pin number
if (argc < 2) {
printf("Usage: gpio-int <gpio-pin>\n\n");
printf("Reports button press duration on a gpio pin\n");
exit(-1);
}
gpio = atoi(argv[1]);
// export and configure the pin for our usage
gpio_export(gpio);
gpio_set_dir(gpio, GPIO_DIR_INPUT);
// stuff the poll structure
memset((void*) &pollfdset, 0, sizeof(pollfdset));
pollfdset.fd = gpio_fd_open(gpio);
pollfdset.events = POLLPRI;
// clear any backed up events
// XXX I'm not sure why we need this, but without it we get a false
// report of an edge from the first poll()
rc = poll(&pollfdset, nfds, 0);
len = read(pollfdset.fd, buf, MAX_BUF);
while (1) {
//printf("waiting for rising edge.\n");
// wait for rising edge, button press
pollfdset.revents = 0;
gpio_set_edge(gpio, kPollEdge_rising);
rc = poll(&pollfdset, nfds, POLL_TIMEOUT_INF);
len = read(pollfdset.fd, buf, MAX_BUF);
if (rc < 0) {
// TODO error
printf("\npoll() failed!\n");
return -1;
} /* else if (rc == 0) timeout */
if (pollfdset.revents & POLLPRI) {
// rising edge occurred
printf("rising edge!\n");
// make note of the time of the rising edge
struct timeval starttv;
gettimeofday(&starttv, NULL);
// now wait for the falling edge
gpio_set_edge(gpio, kPollEdge_falling);
// the button is currently being held down
int duration = -1;
while (duration < 0) {
//printf("waiting for falling edge\n");
pollfdset.revents = 0;
rc = poll(&pollfdset, nfds, POLL_TIMEOUT_5_SEC);
len = read(pollfdset.fd, buf, MAX_BUF);
if (rc < 0) {
printf("error!\n");
// TODO error
duration = 0;
} else if (0 == rc) {
// timeout, check the state of the button to make sure we didn't miss the edge
printf("timeout while waiting for falling edge\n");
unsigned int value = 0xFFFF;
rc = gpio_get_value_fd(pollfdset.fd, &value);
if (0 == value) {
// button is released, but we missed the falling edge
// report a very short duration
printf("Button falling edge missed!\n"); // debug only
duration = 1;
} else if (1 == value) {
// the button is still pressed, carry on
printf("Button still pressed\n");
}
if (rc < 0) {
printf("error2!");
// TODO error
duration = 0;
}
}
if (pollfdset.revents & POLLPRI) {
// falling edge occurred
printf("falling edge!\n");
// make note of the falling edge time
struct timeval endtv;
gettimeofday(&endtv, NULL);
// subtract the times and convert millisecond
struct timeval difftv;
timersub(&endtv, &starttv, &difftv);
duration = (difftv.tv_sec) * 1000 + (difftv.tv_usec) / 1000 ;
assert(duration >= 0); // XXX assert?
}
} // end while duration < 0
report_button_ms(duration);
}
}
// TODO unexport and put in the SIGTERM handler
gpio_fd_close(pollfdset.fd);
return 0;
}
@ajmontag
Copy link
Author

ajmontag commented Nov 4, 2012

Many thanks to Derek Molloy and his YouTube video

@ajmontag
Copy link
Author

ajmontag commented Nov 4, 2012

Note: I have this running under Ubuntu 12.04 on the beaglebone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment