15 Oct 2023 - tsp
Last update 15 Oct 2023
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:
GPIOMAXPIN
can tell one how many GPIO pins are available. They are numbered from zero.GPIOGETCONFIG
reads the configuration of the given pinGPIOGET
reads the current state of a GPIO pin configured as inputGPIOTOGGLE
swaps the state of a GPIO output pinGPIOSET
sets the current state of a GPIO pin configured as outputGPIOSETCONFIG
sets the current configuration of a pinThose ioctl
calls are used to:
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 ... */
}
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.
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 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:
Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)
This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/