A general-purpose input/output (GPIO) is a generic input or output pin on an integrated circuit whose behavior 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 or 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_by_controller(const char * const controller, const unsigned char line_num, gpio_mode_t mode)

Use libgpiod to request a GPIO by its controller name or label and line number, and configure it with the assigned working mode.

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

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 assigned 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 assigned 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 no longer needed. See Free a GPIO.

Functions return NULL for the following failures:

  • The Linux ID, the ID associated with the assigned alias, or the controller/line 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 set low; its value can be written.

  • GPIO_OUTPUT_HIGH: GPIO as an output set high; 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, if using sysfs request methods, 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 a GPIO as an input using its controller label and line number */
gpio_t *button1 = ldx_gpio_request_by_controller("mca-gpio", 12, GPIO_INPUT);

/* 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 *button2 = ldx_gpio_request(505, GPIO_IRQ_EDGE_RISING, REQUEST_SHARED);

printf("Button1 GPIO %s %d configured as %d\n", button1->gpio_controller, button1->gpio_line, ldx_gpio_get_mode(button1));
printf("LED GPIO %d configured as %d\n", led->kernel_number, ldx_gpio_get_mode(led));
printf("Button2 GPIO %d configured as %d\n", button2->kernel_number, ldx_gpio_get_mode(button2));

[...]

Establish GPIO aliases

To help you identify the GPIOs of your design, you can assign aliases to your GPIOs using one of the following formats:

Aliases for GPIO using controller label and line

Map the assigned controller name or label and line 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> = <controller>,<line>

    Where:

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

    • <controller> is the GPIO controller name or label

    • <line> is the GPIO line number

Example GPIO section
[GPIO]

# USER LED
USER_LED = gpio1,23

# USER BUTTON
USER_BUTTON = mca-gpio,1

For example, using the configuration above, you can request a GPIO with the API using the USER_LED or USER_BUTTON alias instead of the controller label and line. See Request a GPIO.

You can get the controller label and line number associated to an alias using the functions:

int ldx_gpio_get_controller(const char * const gpio_alias, char * const controller)
int ldx_gpio_get_line(const char * const gpio_alias);

Aliases for GPIO using 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 - GPIO1 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 the USER_LED or USER_BUTTON alias instead of the kernel number. See Request a GPIO.

You can use the following function to get the Linux ID or kernel number associated with an alias:

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 no longer required. 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 the 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 the GPIO is configured as:

  • GPIO_OUTPUT_LOW

  • GPIO_OUTPUT_HIGH

If the 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_by_controller("mca-gpio", 12, GPIO_INPUT);

/* 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 %s %d is active-high\n", gpio->gpio_controller, gpio->gpio_line);
		break;
	case GPIO_ACTIVE_LOW:
		printf("GPIO %s %d is active-low\n", gpio->gpio_controller, gpio->gpio_line);
		break;
	default:
		printf("Error while getting GPIO %s %d active mode\n", gpio->gpio_controller, gpio->gpio_line);
		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 change notifications.

Function Description

gpio_irq_error_t ldx_gpio_wait_interrupt(gpio_t *gpio, int timeout)

Blocks a maximum time of timeout (in milliseconds) to wait for an interrupt to occur on the specified GPIO. A value of -1 instructs the system to wait indefinitely.

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 specified GPIO.

int ldx_gpio_stop_wait_interrupt(gpio_t *gpio)

Removes interrupt detection on the specified 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 time of timeout (in milliseconds) to wait for an interrupt to occur on the specified GPIO. A value of -1 instructs the system to wait indefinitely. 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_by_controller("mca-gpio", 12, GPIO_IRQ_EDGE_RISING);

[...]

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 specified GPIO.

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

This function triggers the specified callback handler when an interrupt is detected. After that, it continues to wait for new interrupts.

arg is passed as the sole argument of the 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 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_by_controller("mca-gpio", 12, GPIO_IRQ_EDGE_RISING);

[...]

/* 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 lower and upper limits for the debounce time are not defined by the APIX library. The library passes the value to the GPIO driver which, if it supports debouncing, is the one that defines the valid debounce values.

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 [{XREF_bsp_r_mca-gpio}].

GPIO example

In this example, one GPIO is configured as output and another as input. Press the button connected to the input GPIO to switch the LED associated with the output GPIO on and off.

You can import the example in Eclipse using the Digi Embedded 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.