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(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 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, 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:
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:
It returns EXIT_SUCCESS on success, EXIT_FAILURE otherwise. |
[...]
/* 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:
-
Add a section called [GPIO], if one doesn’t already exist.
-
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
-
[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 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)
[...]
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:
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:
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.
[...]
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:
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 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.
[...]
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 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.
-
[...]
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 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)
/* 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. 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 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.