Módulo 1.3

GPIO - Primer contacto con hardware

Controlando LEDs y leyendo botones con Zephyr

Santi Scagliusi, PhD

Comenzar arrow_downward
data_object
gpio_dt_spec
Estructura de GPIO
loop
Polling
Lectura continua
bolt
Interrupciones
Respuesta inmediata
account_tree
DeviceTree
Descripción HW

¿Qué es GPIO?

General Purpose Input/Output - La interfaz más básica entre software y mundo físico.

settings_input_component

Pines configurables

Los pines GPIO son conexiones físicas del microcontrolador que puedes configurar como entrada (leer) o salida (escribir).

  • check_circle Salida: Encender LEDs, activar relés, generar señales
  • check_circle Entrada: Leer botones, detectar sensores digitales
  • check_circle Niveles: Alto (3.3V) o Bajo (0V)
info
Concepto clave: GPIO es el puente entre tu código y el mundo real. Con él puedes interactuar con cualquier dispositivo digital.

Conexión MCU - Dispositivos

nRF52833
MCU
P0.13
P0.11
arrow_forward Salida
arrow_back Entrada
LED
Salida
Botón
Entrada
El MCU controla LEDs (salida) y lee botones (entrada) a través de GPIO

GPIO en el DeviceTree

El DeviceTree describe cómo están conectados los LEDs y botones en la placa.

nrf52833dk_nrf52833.dts (extracto)
/* Definición de LEDs */
leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
label = "Green LED 0";
};
};
/* Definición de botones */
buttons {
compatible = "gpio-keys";
button0: button_0 {
gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
label = "Push button 1";
};
};
/* Aliases para acceso fácil */
aliases { led0 = &led0; sw0 = &button0; };
lightbulb
Ventaja: El DeviceTree abstrae el hardware. Tu código usa led0 sin importar el pin físico.

Anatomía del DeviceTree GPIO

Entendiendo cada parte de la definición de GPIO.

1

gpios property

Define el controlador GPIO (&gpio0), el número de pin (13) y los flags (GPIO_ACTIVE_LOW).

2

GPIO_ACTIVE_LOW

Indica que el dispositivo se activa con nivel bajo (0V). Común en LEDs conectados a VCC.

3

Node labels (led0, button0)

Etiquetas que identifican los nodos. Se usan para crear referencias y aliases.

4

Aliases

Nombres cortos para acceder a los nodos desde el código C. led0 y sw0 son estándar.

sync_alt

Flujo de datos

DTS file arrow_forward Build system arrow_forward devicetree.h arrow_forward Tu código C

La estructura gpio_dt_spec

Contenedor que agrupa toda la información de un GPIO obtenida del DeviceTree.

data_object Contenido de gpio_dt_spec

port
Puntero al dispositivo GPIO
Referencia al controlador (gpio0, gpio1...)
pin
Número de pin
El pin físico (0-31 típicamente)
dt_flags
Flags del DeviceTree
ACTIVE_LOW, ACTIVE_HIGH, etc.
info
Importante: Los flags dt_flags se combinan automáticamente con los flags de configuración cuando llamas a gpio_pin_configure_dt().
Obtener gpio_dt_spec
/* Incluir headers necesarios */
#include <zephyr/drivers/gpio.h>
/* Obtener nodo del alias led0 */
#define LED0_NODE DT_ALIAS(led0)
/* Crear gpio_dt_spec desde el nodo */
static const struct gpio_dt_spec led =
GPIO_DT_SPEC_GET(LED0_NODE, gpios);
/* También para el botón */
#define SW0_NODE DT_ALIAS(sw0)
static const struct gpio_dt_spec button =
GPIO_DT_SPEC_GET(SW0_NODE, gpios);
sync_alt

Flujo de datos

DTS node arrow_forward DT_ALIAS() arrow_forward GPIO_DT_SPEC_GET() arrow_forward gpio_dt_spec

Inicialización y configuración

Tres pasos esenciales antes de usar cualquier GPIO.

1

Obtener gpio_dt_spec

Usar GPIO_DT_SPEC_GET() con el nodo del DeviceTree.

GPIO_DT_SPEC_GET(LED0_NODE, gpios)
2

Verificar disponibilidad

Comprobar que el GPIO está listo con gpio_is_ready_dt().

if (!gpio_is_ready_dt(&led)) { ... }
3

Configurar dirección

Establecer como entrada o salida con gpio_pin_configure_dt().

gpio_pin_configure_dt(&led, GPIO_OUTPUT)
warning
Siempre verifica: Omitir gpio_is_ready_dt() puede causar crashes si el hardware no está disponible.
Inicialización completa
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led =
GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void) {
int ret;
/* Paso 2: Verificar disponibilidad */
if (!gpio_is_ready_dt(&led)) {
return -1;
}
/* Paso 3: Configurar como salida */
ret = gpio_pin_configure_dt(&led,
GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return ret;
}
/* GPIO listo para usar */
return 0;
}
GPIO_OUTPUT
Salida, estado inicial bajo
GPIO_OUTPUT_ACTIVE
Salida, estado inicial activo
GPIO_INPUT
Entrada básica
GPIO_INPUT | GPIO_PULL_UP
Entrada con pull-up

Escribir en una salida

Controla LEDs y otros dispositivos con las funciones de escritura GPIO.

gpio_pin_set_dt(&led, 1)

Establece el pin al estado activo. Si el LED tiene flag ACTIVE_LOW, el pin irá a 0V (encendido).

LED encendido (estado activo)
gpio_pin_set_dt(&led, 0)

Establece el pin al estado inactivo. Con ACTIVE_LOW, el pin irá a 3.3V (apagado).

LED apagado (estado inactivo)
gpio_pin_toggle_dt(&led)

Cambia el estado del pin. Si estaba activo pasa a inactivo y viceversa. Ideal para blinky.

swap_horiz
Alterna estado
lightbulb
Abstracción: Las funciones *_dt() manejan automáticamente la lógica ACTIVE_LOW/HIGH. Tú solo piensas en "activo" o "inactivo".
Ejemplo: Blinky básico
int main(void) {
/* Inicialización (omitida) */
while (1) {
/* Encender LED */
gpio_pin_set_dt(&led, 1);
k_msleep(500);
/* Apagar LED */
gpio_pin_set_dt(&led, 0);
k_msleep(500);
}
}
O más elegante con toggle:
Usando toggle
while (1) {
gpio_pin_toggle_dt(&led);
k_msleep(500);
}

Leer entrada: Método Polling

Lee el estado del pin repetidamente en un bucle. Simple pero no eficiente.

loop

gpio_pin_get_dt()

Lee el estado actual del pin. Retorna 1 si está activo o 0 si está inactivo.

Funcionamiento:
loop Loop
arrow_forward
visibility Leer
arrow_forward
check Actuar
arrow_forward
refresh Repetir
thumb_up Ventajas
  • - Simple de implementar
  • - Fácil de entender
  • - Sin configuración extra
thumb_down Desventajas
  • - CPU siempre ocupada
  • - Alto consumo de energía
  • - Puede perder eventos
LED controlado por botón (Polling)
#define LED0_NODE DT_ALIAS(led0)
#define SW0_NODE DT_ALIAS(sw0)
static const struct gpio_dt_spec led =
GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec button =
GPIO_DT_SPEC_GET(SW0_NODE, gpios);
int main(void) {
/* Configurar LED como salida */
gpio_pin_configure_dt(&led, GPIO_OUTPUT);
/* Configurar botón como entrada */
gpio_pin_configure_dt(&button, GPIO_INPUT);
while (1) {
/* Leer estado del botón */
int val = gpio_pin_get_dt(&button);
/* Encender LED si botón presionado */
gpio_pin_set_dt(&led, val);
/* Pequeña pausa para debounce */
k_msleep(10);
}
}
warning
No recomendado para producción: El polling mantiene la CPU activa constantemente. Usa interrupciones para mejor eficiencia energética.

Leer entrada: Interrupciones

El hardware avisa cuando cambia el estado. Más eficiente y profesional.

bolt Configuración de interrupciones

1

Configurar trigger

gpio_pin_interrupt_configure_dt() - Define cuándo disparar.

2

Definir callback (ISR)

Función que se ejecuta cuando ocurre la interrupción.

3

Inicializar callback

gpio_init_callback() - Asociar función y pin.

4

Registrar callback

gpio_add_callback() - Activar la interrupción.

GPIO_INT_EDGE_TO_ACTIVE
Flanco hacia activo
GPIO_INT_EDGE_TO_INACTIVE
Flanco hacia inactivo
GPIO_INT_EDGE_BOTH
Ambos flancos
GPIO_INT_LEVEL_ACTIVE
Nivel activo
battery_saver
Eficiencia energética: Con interrupciones, la CPU puede dormir y solo despierta cuando hay un evento real.
bolt Flujo de interrupción
Evento HW arrow_forward ISR arrow_forward Acción arrow_forward CPU duerme

Código de interrupción GPIO

Implementación completa con callback y registro.

Configuración de interrupción GPIO
/* Estructura para el callback */
static struct gpio_callback button_cb_data;
/* Función callback (ISR) */
void button_pressed(
const struct device *dev,
struct gpio_callback *cb,
uint32_t pins)
{
gpio_pin_toggle_dt(&led);
}
int main(void) {
/* 1. Configurar interrupción */
gpio_pin_interrupt_configure_dt(
&button, GPIO_INT_EDGE_TO_ACTIVE);
/* 2. Inicializar callback */
gpio_init_callback(&button_cb_data,
button_pressed, BIT(button.pin));
/* 3. Registrar callback */
gpio_add_callback(button.port,
&button_cb_data);
/* CPU puede dormir */
while (1) k_sleep(K_FOREVER);
}
info
Nota: El callback se ejecuta en contexto de interrupción. Evita operaciones bloqueantes o muy largas dentro del ISR.

El ejemplo Blinky completo

Todo junto: inicialización, verificación, configuración y bucle principal.

description

1. Includes

Headers de kernel y GPIO

data_object

2. Definir spec

GPIO_DT_SPEC_GET

play_arrow

3. main()

Check, config, loop

src/main.c - Blinky completo
/* Paso 1: Incluir headers */
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
/* Intervalo de parpadeo */
#define SLEEP_TIME_MS 1000
/* Paso 2: Obtener nodo del alias */
#define LED0_NODE DT_ALIAS(led0)
/* Paso 3: Crear gpio_dt_spec */
static const struct gpio_dt_spec led =
GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void) {
int ret;
/* Paso 4: Verificar GPIO listo */
if (!gpio_is_ready_dt(&led)) {
return 0;
}
/* Paso 5: Configurar como salida */
ret = gpio_pin_configure_dt(&led,
GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
/* Paso 6: Bucle de parpadeo */
while (1) {
gpio_pin_toggle_dt(&led);
k_msleep(SLEEP_TIME_MS);
}
return 0;
}
Resultado

El LED de la placa parpadea cada segundo. Si funciona, tu configuración de GPIO está correcta y lista para proyectos más complejos.

check_circle

Lo que aprendiste hoy

APIs principales de GPIO

GPIO_DT_SPEC_GET(node, prop)
Obtener spec desde DeviceTree
gpio_is_ready_dt(&spec)
Verificar disponibilidad
gpio_pin_configure_dt(&spec, flags)
Configurar dirección
gpio_pin_set_dt(&spec, value)
Escribir salida
gpio_pin_toggle_dt(&spec)
Alternar estado
gpio_pin_get_dt(&spec)
Leer entrada
tips_and_updates

"GPIO es tu puente al mundo real. Las APIs _dt() hacen el trabajo pesado."

Siempre usa las funciones que terminan en _dt() - manejan automáticamente la lógica de activación.

Polling vs Interrupciones

loop

Polling

  • add Simple de implementar
  • remove CPU siempre activa
  • remove Alto consumo energético
Útil para: prototipos rápidos, debugging
bolt

Interrupciones

  • add CPU puede dormir
  • add Bajo consumo
  • remove Más código inicial
Útil para: productos, bajo consumo
arrow_forward Siguiente módulo

1.4 Logging y comunicación serie

Aprenderemos a usar el sistema de logging de Zephyr y la comunicación UART para depurar nuestras aplicaciones.