Using the RaspberryPi GPIOs on FreeBSD

15 Oct 2023 - tsp
Last update 15 Oct 2023
Reading time 5 mins

Since I often use the RaspberryPi board for some non critical automation that requires more than the computational power of an AVR or where having a full blown operating system is just more convenient I also often want to use the GPIO pins as on any embedded device. Since I usually don’t use a Linux distribution like Raspbian I had to look up how to use the GPIOs on FreeBSD every time again. This article is just a short summary on how to access them so I don’t have to look this up every time. It is based on two excellent resources:

In contrast to Linux which exposes the general purpose input/output pins using some sysfs hack FreeBSD uses a proper device /dev/gpioc0. The pins are controlled using a set of ioctl functions:

Those ioctl calls are used to:

Opening the device file

To open the device one uses the usual standard POSIX functions to access files opening the device file in read only mode. Read only mode is sufficient to perform the required ioctl requests.

First let’s include the necessary headers:

#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/gpio.h>

To open the device simply call open:

int fd;

fd = open("/dev/gpioc0", O_RDONLY);
if(fd < 0) {
    /* Handle error */
}

To query the number of available GPIO pins simply use the GPIOMAXPIN ioctl:

int maxPins;

if(ioctl(fd, GPIOMAXPIN, &maxPins) < 0) {
    /* Handle error ... */
}

Pin configuration

Now to the more fun part - how to configure the I/O pins. This is done using GPIOSETCONFIG to update the given configuration. The configuration is represented by a struct gpio_pin structure. This structure has four members that are defined in sys/gpio.h

struct gpio_pin {
	uint32_t gp_pin;			/* pin number */
	char gp_name[GPIOMAXNAME];		/* human-readable name */
	uint32_t gp_caps;			/* capabilities */
	uint32_t gp_flags;			/* current flags */
};

As one can see the gp_pin field refers the pin number (in this case the BCMs real pin number, not a wiringPi or pin header number). The capabilities is a bit field that identifies the capabilities of the given port. Those can be configured in the flags. It’s a good idea to first query that field as well as the gp_name (a human readable name) before setting a configuration.

The pin configurations are rather self explanatory:

#define GPIO_PIN_INPUT		0x0001	/* input direction */
#define GPIO_PIN_OUTPUT		0x0002	/* output direction */
#define GPIO_PIN_OPENDRAIN	0x0004	/* open-drain output */
#define GPIO_PIN_PUSHPULL	0x0008	/* push-pull output */
#define GPIO_PIN_TRISTATE	0x0010	/* output disabled */
#define GPIO_PIN_PULLUP		0x0020	/* internal pull-up enabled */
#define GPIO_PIN_PULLDOWN	0x0040	/* internal pull-down enabled */
#define GPIO_PIN_INVIN		0x0080	/* invert input */
#define GPIO_PIN_INVOUT		0x0100	/* invert output */
#define GPIO_PIN_PULSATE	0x0200	/* pulsate in hardware */

To setup a pin as input or output one can simply use the GPIOGETCONFIG and GPIOSETCONFIG ioctls setting the desired configuration:

int setInput(int fd, int pinNumber) {
    struct gpio_pin g_pin;

    /* One might check if pinNumber is below the maximum limit */
    g_pin.gp_pin = pinNumber;

    if(ioctl(fd, GPIOGETCONFIG, &g_pin) < 0) {
        return -1;
    }

    g_pin.gp_flags = GPIO_PIN_INPUT;
    if(ioctl(fd, GPIOSETCONFIG, &g_pin) < 0) {
        return -1;
    }

    return 0;
}

int setOutput(int fd, int pinNumber) {
    struct gpio_pin g_pin;

    /* One might check if pinNumber is below the maximum limit */
    g_pin.gp_pin = pinNumber;

    if(ioctl(fd, GPIOGETCONFIG, &g_pin) < 0) {
        return -1;
    }

    g_pin.gp_flags = GPIO_PIN_OUTPUT;
    if(ioctl(fd, GPIOSETCONFIG, &g_pin) < 0) {
        return -1;
    }

    return 0;
}

In addition one could set the proper pullup or pulldown flags if required.

Reading input pins

To read input pins one directly uses the GPIOGET ioctl. This uses a simple gpio_req structure:

struct gpio_req {
	uint32_t gp_pin;			/* pin number */
	uint32_t gp_value;			/* value */
};

This structure is passed to read, write as well as toggle (toggle does not use the value field):

int getGpioStatus(int fd, int pin) {
    struct gpio_req rq;

    rq.gp_pin = pin;

    if(ioctl(fd, GPIOGET, &rq) < 0) {
        /* Error */
        return -1;
    }
    return rq.gp_value == 0 ? 0 : 1;
}

Writing and toggling output pins

Writing and toggling output pins is also very simple. One simply sets up the gpio_req structure again and performs either the GPIOSET or the GPIOTOGGLE ioctl:

int setGpio(int fd, int pin, int status) {
    struct gpio_req rq;

    rq.gp_pin = pin;
    rq.gp_value = (status == 0) ? 0 : 1;

    if(ioctl(fd, GPIOSET, &rq) < 0) {
        return -1;
    }

    return 0;
}
int toggleGpio(int fd, int pin, int status) {
    struct gpio_req rq;

    rq.gp_pin = pin;

    if(ioctl(fd, GPIOTOGGLE, &rq) < 0) {
        return -1;
    }

    return 0;
}

This article is tagged:


Data protection policy

Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support