A general-purpose input/output (GPIO) is a generic pin on an integrated circuit whose behavior—whether it is an input or output pin—is controllable by the user at runtime.

You can use a standard GPIO for the following purposes:

  • Reading from switches.

  • Reading from sensors such as IR, liquid level.

  • Writing output to LEDs for status, relays, etc.

The ConnectCore platforms have several GPIO interfaces. You can find more information in Hardware reference manuals and General Purpose Input/Output (GPIO).

Digi adds an API to Linux that manages these GPIO interfaces. To use this API, include the following header file:

#include <libdigiapix/gpio.h>

You can configure them, read and set values, and listen for state changes.

Request a GPIO

Before using a GPIO, you must request that pin to ensure it is available on the system. You can request a GPIO with one of the following functions:

Function Description

gpio_t *ldx_gpio_request(unsigned int kernel_number, gpio_mode_t mode, request_mode_t request_mode)

Request a GPIO by its kernel number and configure it with the given working mode.

It returns a pointer to gpio_t on success, NULL on error.

gpio_t *ldx_gpio_request_by_alias(const char * const gpio_alias, gpio_mode_t mode, request_mode_t request_mode)

Request a GPIO by its alias name and configure it with the given working mode. You must define the GPIO alias mapping in the /etc/libdigiapix.conf file under the [GPIO] section. See Establish GPIO aliases.

It returns a pointer to gpio_t on success, NULL on error.

A requested GPIO must be freed once it is not needed anymore. See Free a GPIO.

Both functions may fail and return NULL for the following reasons:

  • The provided Linux ID or the associated ID to the given alias cannot be exported.

  • The API encountered problems allocating memory to initialize the GPIO. Your system may have run out of resources.

  • The GPIO cannot be configured.

  • The request mode is configured to REQUEST_WEAK and the GPIO is already exported.

The request operation also configures the GPIO with the provided gpio_mode_t working modes:

  • GPIO_INPUT: GPIO as an input; its value can be read.

  • GPIO_OUTPUT_LOW: GPIO as an output with low as initial value; its value can be written.

  • GPIO_OUTPUT_HIGH: GPIO as an output with high as initial value; its value can be written.

  • GPIO_IRQ_EDGE_RISING: GPIO as an interrupt on rising; interrupt is triggered on rising edge, from low to high.

  • GPIO_IRQ_EDGE_FALLING: GPIO as an interrupt on falling; interrupt is triggered on falling edge, from low to high.

  • GPIO_IRQ_EDGE_BOTH: GPIO as an interrupt on both; interrupt is triggered on rising and falling edges.

When requesting a GPIO, you can select the request mode from the following request_mode_t values:

  • REQUEST_SHARED: If the GPIO is already exported it will not be unexported on free. If it is not exported, it will be unexported on free.

  • REQUEST_GREEDY: The GPIO will be always unexported on free.

  • REQUEST_WEAK: If the GPIO is already exported, the request will fail. It will always be unexported on free.

Once you have requested the GPIO, you can read or set its working mode using the following functions:

Function Description

gpio_mode_t ldx_gpio_get_mode(gpio_t *gpio)

Gets the given GPIO working mode:

  • An input: GPIO_INPUT

  • An output: GPIO_OUTPUT_LOW or GPIO_OUTPUT_HIGH

  • Or interrupt generation: GPIO_IRQ_EDGE_RISING, GPIO_IRQ_EDGE_FALLING, or GPIO_IRQ_EDGE_BOTH

It returns GPIO_MODE_ERROR if it cannot be retrieved.

int ldx_gpio_set_mode(gpio_t *gpio, gpio_mode_t mode)

Configures the working mode of the provided GPIO:

  • An input for reading its value: GPIO_INPUT

  • An output for setting its value: GPIO_OUTPUT_LOW or GPIO_OUTPUT_HIGH

  • An interrupt trigger when there is a value change: GPIO_IRQ_EDGE_RISING, GPIO_IRQ_EDGE_FALLING, or GPIO_IRQ_EDGE_BOTH

It returns EXIT_SUCCESS on success, EXIT_FAILURE otherwise.

Request a GPIO
[...]

/* Request an output (default to low) GPIO using its alias */
gpio_t *led = ldx_gpio_request_by_alias("USER_LED", GPIO_OUTPUT_LOW, REQUEST_SHARED);

/* Request a GPIO as an interrupt on rising edge using its kernel number */
gpio_t *button = ldx_gpio_request(505, GPIO_IRQ_EDGE_RISING, REQUEST_SHARED);

printf("LED GPIO %d configured as %d\n", led->kernel_number, ldx_gpio_get_mode(led));
printf("Button GPIO %d configured as %d\n", button->kernel_number, ldx_gpio_get_mode(button));

[...]

Establish GPIO aliases

To help you identify the GPIOs of your design, you can assign aliases to your GPIO Linux IDs. Map the assigned kernel number to a name in the /etc/libdigiapix.conf file:

  1. Add a section called [GPIO], if one doesn’t already exist.

  2. Below the section name, add the list of mapped GPIOs using the following format:

    <alias> = <kernel_number>

    Where:

    • <alias> is the human-readable name for the GPIO

    • <kernel_number> is the GPIO Linux ID

Example GPIO section
[GPIO]

# USER LED - I/O Expander IO23
USER_LED = 488

# USER BUTTON - MCA_IO1
USER_BUTTON = 505

For example, using the configuration above, you can request a GPIO with the API using USER_LED and USER_BUTTON alias instead of the kernel number. See Request a GPIO.

You can get the Linux ID or kernel number associated to an alias using the function:

int ldx_gpio_get_kernel_number(const char * const gpio_alias)
For information on including libdigiapix.conf in your Digi Embedded Yocto images, see Define interface aliases.

Free a GPIO

You must free a requested GPIO when it is not required anymore. To do so, use the ldx_gpio_free() function.

int ldx_gpio_free(gpio_t *gpio)
Free a requested GPIO
[...]

gpio_t *led = ...;

[...]

/* Free GPIO once it is not required anymore */
ldx_gpio_free(led);

[...]

Read and set the GPIO value

You can read and set the GPIO value:

Function Description

gpio_value_t ldx_gpio_get_value(gpio_t *gpio)

Retrieves the value of the GPIO, GPIO_HIGH or GPIO_LOW. If there is an error, GPIO_VALUE_ERROR is returned.

This function only has effect if GPIO is configured as:

  • GPIO_INPUT

  • GPIO_IRQ_EDGE_RISING

  • GPIO_IRQ_EDGE_FALLING

  • GPIO_IRQ_EDGE_BOTH

For GPIO_OUTPUT_LOW or GPIO_OUTPUT_HIGH; this function always returns GPIO_LOW. See Request a GPIO.

int ldx_gpio_set_value(gpio_t *gpio, gpio_value_t value)

Sets the GPIO value. It returns EXIT_SUCCESS on success, EXIT_FAILURE otherwise.

This function only has an effect if GPIO is configured as:

  • GPIO_OUTPUT_LOW

  • GPIO_OUTPUT_HIGH

If GPIO is configured as input or with any of the interrupt modes, this function returns EXIT_FAILURE. See Request a GPIO.

GPIO value is the enumerated type gpio_value_t:

  • GPIO_LOW: the GPIO value is low.

  • GPIO_HIGH: the GPIO value is high.

Read and set a GPIO value
[...]

gpio_t *led = ldx_gpio_request_by_alias("USER_LED", GPIO_OUTPUT_LOW, REQUEST_SHARED);
gpio_t *button = ldx_gpio_request(505, GPIO_INPUT, REQUEST_SHARED);

/* Read the value of the button GPIO */
gpio_value_t value = ldx_gpio_get_value(button);
printf("Button GPIO values is %d\n", value);

/* Set to HIGH the LED GPIO */
ldx_gpio_set_value(led, GPIO_HIGH);

[...]

Configure GPIO active mode

It is natural to assume that a GPIO is active when its signal value is 1 (high), and inactive when it is 0 (low). However, the device connected to a GPIO may have a different convention about what active means:

  • Active high: the device is activated when the GPIO is high; that is, 1 means active.

  • Active low: the device is activated when the GPIO is low; that is, 0 means active.

You can define the active attribute of a GPIO using the ldx_gpio_set_active_mode() function:

Function Description

int ldx_gpio_set_active_mode(gpio_t *gpio, gpio_active_mode_t value)

Changes the active mode of the given GPIO:

  • GPIO_ACTIVE_HIGH for active high behavior.

  • GPIO_ACTIVE_LOW for active low behavior.

It returns EXIT_SUCCESS on success, EXIT_FAILURE otherwise.

gpio_active_mode_t ldx_gpio_get_active_mode(gpio_t *gpio)

Gets the active mode of the given GPIO:

  • GPIO_ACTIVE_HIGH for active high behavior.

  • GPIO_ACTIVE_LOW for active low behavior.

  • GPIO_ACTIVE_MODE_ERROR on error.

GPIO active mode is the enumerated type gpio_active_mode_t:

  • GPIO_ACTIVE_HIGH for an active high GPIO.

  • GPIO_ACTIVE_LOW for an active low GPIO.

Configuring and getting a GPIO active mode
[...]

gpio_t *gpio = ...;

ldx_gpio_set_active_mode(gpio, GPIO_ACTIVE_HIGH);

[...]

switch(ldx_gpio_get_active_mode(gpio)) {
	case GPIO_ACTIVE_HIGH:
		printf("GPIO %d is active-high\n", gpio->kernel_number);
		break;
	case GPIO_ACTIVE_LOW:
		printf("GPIO %d is active-low\n", gpio->kernel_number);
		break;
	default:
		printf("Error while getting GPIO %d active mode\n", gpio->kernel_number);
		break;
}

[...]

Detect GPIO interruptions

When a GPIO is configured as an interrupt using GPIO_IRQ_EDGE_RISING, GPIO_IRQ_EDGE_FALLING or GPIO_IRQ_EDGE_BOTH, you can synchronously or asynchronously wait for value changes notifications.

Function Description

gpio_irq_error_t ldx_gpio_wait_interrupt(gpio_t *gpio, int timeout)

Blocks for a maximum of timeout milliseconds (or indefinitely for -1 ms) waiting for an interrupt on the given GPIO to occur

int ldx_gpio_start_wait_interrupt(gpio_t *gpio, const ldx_gpio_interrupt_cb_t interrupt_cb, void *arg)

Sets up an interrupt handler on the provided GPIO

int ldx_gpio_stop_wait_interrupt(gpio_t *gpio)

Removes interrupt detection on the given GPIO

Synchronous interrupt detection

ldx_gpio_wait_interrupt() is a blocking function to wait for an interrupt to occur on the given GPIO.

gpio_irq_error_t ldx_gpio_wait_interrupt(gpio_t *gpio, int timeout)

This function blocks a maximum of the specified amount of milliseconds in timeout or indefinitely for -1. It returns:

  • GPIO_IRQ_ERROR_NONE when the interrupt is captured.

  • GPIO_IRQ_ERROR_TIMEOUT if no interrupt is triggered during the specified timeout.

  • GPIO_IRQ_ERROR on error:

    • If timeout is less than -1.

    • If the GPIO is not configured as an interrupt. See Request a GPIO.

    • If the GPIO cannot be monitored.

Synchronous interrupt detection
[...]

gpio_t *led = ldx_gpio_request_by_alias("USER_LED", GPIO_OUTPUT_LOW, REQUEST_SHARED);
gpio_t *button = ldx_gpio_request(505, GPIO_IRQ_EDGE_RISING, REQUEST_SHARED);

[...]

gpio_value_t led_state = ldx_gpio_get_value(led);

while (running) {
	/* Wait 5 seconds for RISING interrupt to occur on button GPIO */
	gpio_irq_error_t ret = ldx_gpio_wait_interrupt(button, 5000);

	switch (ret) {
		case GPIO_IRQ_ERROR_NONE:
			/* Change LED value */
			led_state = (led_state == GPIO_HIGH ? GPIO_LOW : GPIO_HIGH);
			ldx_gpio_set_value(led, led_state);
			usleep(500000);
			break;
		case GPIO_IRQ_ERROR_TIMEOUT:
			printf("No interrupt captured during last 5 seconds.\n");
			break;
		case GPIO_IRQ_ERROR:
			printf("Error waiting for interrupt.\n");
			break;
		default:
			running = 0;
			break;
	}

	sleep(5);
}

[...]

Asynchronous interrupt detection

ldx_gpio_start_wait_interrupt() is a non-blocking function to wait for interrupts to occur on the given GPIO.

int ldx_gpio_start_wait_interrupt(gpio_t *gpio, const ldx_gpio_interrupt_cb_t interrupt_cb, void *arg)

This function registers the provided callback handler to be executed when an interrupt is triggered. After that, it continues waiting for new interrupts.

arg is passed as the sole argument of ldx_gpio_interrupt_cb_t callback.

ldx_gpio_start_wait_interrupt() returns:

  • EXIT_SUCCESS on success.

  • EXIT_FAILURE when failing:

    • If the GPIO is not configured as an interrupt. See Request a GPIO.

    • If the GPIO cannot be monitored.

The provided callback must follow this prototype:

typedef int (*ldx_gpio_interrupt_cb_t)(void *arg)

Only one interrupt is queued if it arrives while the callback function is being executed, so some interrupts may be missed if they happen too fast.

To stop listening for interrupts, use ldx_gpio_stop_wait_interrupt().

int ldx_gpio_stop_wait_interrupt(gpio_t *gpio)
Asynchronous interrupt detection
/* Interrupt callback */
static int counter_cb(void* arg)
{
	int* tmp_count = (int*) arg;

	*tmp_count = *tmp_count + 1;

	return EXIT_SUCCESS;
}

[...]

int interrupt_count = 0;
gpio_t *led = ldx_gpio_request_by_alias("USER_LED", GPIO_OUTPUT_LOW, REQUEST_SHARED);
gpio_t *button = ldx_gpio_request(505, GPIO_IRQ_EDGE_RISING, REQUEST_SHARED);

[...]

/* Start capturing interrupts */
ldx_gpio_start_wait_interrupt(button, counter_cb, (void*) &interrupt_count);

do {
	printf("Caught %d interrupts\n", interrupt_count");
	sleep(1):
} while(interrupt_count < 10);

[...]

/* Stop capturing interrupts */
ldx_gpio_stop_wait_interrupt(button);

[...]

Configure debounce

Bouncing is the effect by which a line is quickly pulled high/low at very short intervals for mechanical reasons, for example when connected to a button or switch. The MCA GPIOs that are IRQ-capable can be configured with a debounce filter, to remove the bounce effect.

int ldx_gpio_set_debounce(gpio_t *gpio, unsigned int usec)

This function creates a debounce filter. gpio is the gpio_t to configure and usec is the time in microseconds to set in that filter.

The debounce functionality only works with either rising or falling edge, but not with both. Only those pins that are IRQ-capable support the debounce capability. For additional information on the MCA GPIO debounce capability, see MCA General Purpose Input/Output (GPIO).

GPIO example

In this example, one GPIO is configured as output and another as input. You can press the button connected to the input GPIO to switch the LED on and off corresponding to the output GPIO.

You can import the example in Eclipse using the Digi Embeddded Yocto plugin. For more information, see Create a new DEY sample project. This example is included in Digi Embedded Yocto. Go to GitHub to see the application source code.