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 |
---|---|
|
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 |
|
Request a GPIO by its kernel number and configure it with the assigned working mode. It returns a pointer to |
|
Request a GPIO by its alias name and configure it with the assigned working mode.
You must define the GPIO alias mapping in the It returns a pointer to |
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 |
---|---|
|
Gets the given GPIO working mode:
It returns |
int ldx_gpio_set_mode(gpio_t *gpio, gpio_mode_t mode) |
Configures the working mode of the provided GPIO:
It returns |
[...]
/* 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:
-
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> = <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
-
[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:
-
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 - 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)
[...]
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 |
---|---|
|
Retrieves the value of the GPIO, This function only has effect if the GPIO is configured as:
For |
|
Sets the GPIO value.
It returns 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 |
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_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 |
---|---|
|
Changes the active mode of the given GPIO:
It returns |
|
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 %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 |
---|---|
|
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. |
|
Sets up an interrupt handler on the specified 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_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)
/* 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.
For the MCA GPIO, the maximum allowed debounce time is 12.75 s.
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 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.