Módulo 1.4

Logging y comunicación serie

Depuración y comunicación con el ordenador

Santi Scagliusi, PhD

Comenzar arrow_downward
terminal
printk
Salida básica
bug_report
LOG_*
Sistema profesional
cable
UART
Comunicación serie
sync_alt
Async API
Callbacks y buffers

printk - La salida más simple

Similar a printf de C, pero diseñado para sistemas embebidos sin sistema operativo completo.

terminal

printk()

Función de salida del kernel de Zephyr. Imprime directamente a la consola serie.

  • check_circle Sintaxis igual que printf
  • check_circle Soporta formateadores: %d, %s, %x, %p
  • warning Operación bloqueante (espera hasta enviar)
info
Configuración: Requiere CONFIG_PRINTK=y en prj.conf. Generalmente ya está habilitado por defecto.
src/main.c
#include <zephyr/kernel.h>
int main(void) {
int contador = 0;
const char *nombre = "Zephyr";
/* Mensaje simple */
printk("Hola desde %s!\n", nombre);
while (1) {
printk("Contador: %d\n", contador++);
k_msleep(1000);
}
return 0;
}
[00:00:00.000] Hola desde Zephyr!
[00:00:00.001] Contador: 0
[00:00:01.001] Contador: 1
[00:00:02.001] Contador: 2

Sistema de Logging de Zephyr

Sistema profesional con niveles, filtrado y múltiples backends de salida.

settings Configuración en prj.conf

prj.conf
# Habilitar sistema de logging
CONFIG_LOG=y
# Nivel máximo de compilación
CONFIG_LOG_MAX_LEVEL=4
# Nivel por defecto en runtime
CONFIG_LOG_DEFAULT_LEVEL=3

code Registrar módulo en código

src/main.c
#include <zephyr/logging/log.h>
/* Registrar módulo con nivel DEBUG */
LOG_MODULE_REGISTER(mi_app, LOG_LEVEL_DBG);
lightbulb
Ventaja: Cada módulo tiene su propio nombre y nivel de log. Puedes filtrar por módulo en runtime.
Uso de macros LOG_*
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sensor, LOG_LEVEL_DBG);
int main(void) {
int temp = 25;
/* Diferentes niveles */
LOG_ERR("Error crítico!");
LOG_WRN("Advertencia: temp alta");
LOG_INF("Temperatura: %d C", temp);
LOG_DBG("Valor raw: 0x%X", temp);
return 0;
}
[00:00:00.100] <err> sensor: Error crítico!
[00:00:00.101] <wrn> sensor: Advertencia: temp alta
[00:00:00.102] <inf> sensor: Temperatura: 25 C
[00:00:00.103] <dbg> sensor: Valor raw: 0x19

Niveles de Log

Los niveles permiten filtrar la cantidad de información según la necesidad.

0
LOG_ERR LOG_LEVEL_ERR

Errores que impiden el funcionamiento normal

1
LOG_WRN LOG_LEVEL_WRN

Situaciones anormales pero recuperables

2
LOG_INF LOG_LEVEL_INF

Información general del funcionamiento

3
LOG_DBG LOG_LEVEL_DBG

Detalles para depuración de código

filter_alt Funcionamiento del filtrado

Si configuras LOG_LEVEL_WRN, solo verás mensajes de nivel WRN y ERR (los más severos).

Nivel 0: ERR
Nivel 1: ERR WRN
Nivel 2: ERR WRN INF
Nivel 3: ERR WRN INF DBG
thumb_up Desarrollo

Usar LOG_LEVEL_DBG para ver todos los mensajes

rocket_launch Producción

Usar LOG_LEVEL_WRN para reducir overhead

warning
Importante: CONFIG_LOG_MAX_LEVEL en prj.conf define el máximo en compilación. Los mensajes de nivel superior no se compilan.

printk vs Sistema de Logging

Cada método tiene sus usos. Conoce cuándo usar cada uno.

terminal

printk()

  • add Simple, sin configuración
  • add Siempre disponible si CONFIG_PRINTK=y
  • remove Sin niveles ni filtrado
  • remove Bloqueante
  • remove Sin timestamps automáticos
Usar para: pruebas rápidas, boot temprano, ISRs simples
bug_report

LOG_* macros

  • add Niveles: ERR, WRN, INF, DBG
  • add Filtrado por módulo en runtime
  • add Timestamps automáticos
  • add Múltiples backends (UART, RTT, etc.)
  • remove Requiere configuración inicial
Usar para: aplicaciones de producción, debugging avanzado

UART: Comunicación serie asíncrona

Universal Asynchronous Receiver/Transmitter - El protocolo serie más común.

cable Características principales

  • TX
    Transmit (TX) - Línea de transmisión de datos
  • RX
    Receive (RX) - Línea de recepción de datos
  • GND
    Ground - Referencia común entre dispositivos

Parámetros de configuración

Baud rate
115200 bps
Bits de datos
8 bits
Paridad
None
Bits de parada
1 bit
Configuración típica: 115200 8N1

Conexión UART (TX cruzado con RX)

MCU
nRF52833
TX
RX
RX
TX
PC
Ordenador
TX de un dispositivo conecta a RX del otro

Trama UART (transmisión del byte 0x55)

Idle
Start
D0
D1
D2
D3
D4
D5
D6
D7
Stop
Idle
Alto (1) = Idle/Mark
Bajo (0) = Space

UART asíncrono en Zephyr

El API asíncrono usa callbacks para notificar eventos sin bloquear la CPU.

settings Configuración Kconfig

prj.conf
# Habilitar driver UART
CONFIG_SERIAL=y
# Usar API asíncrona
CONFIG_UART_ASYNC_API=y
# Para nRF: habilitar UARTE
CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n

code Obtener dispositivo UART

src/main.c
#include <zephyr/drivers/uart.h>
/* Obtener dispositivo del DeviceTree */
const struct device *uart =
DEVICE_DT_GET(DT_NODELABEL(uart0));
/* Verificar disponibilidad */
if (!device_is_ready(uart)) {
return -1;
}

Funciones principales del API asíncrono

uart_callback_set(dev, callback, user_data)
Registrar función callback para eventos
uart_rx_enable(dev, buf, len, timeout)
Habilitar recepción con buffer y timeout
uart_tx(dev, buf, len, timeout)
Transmitir datos de forma asíncrona
uart_rx_disable(dev)
Deshabilitar recepción
info
Nota: El API asíncrono es no bloqueante. La CPU puede hacer otras tareas mientras se transfieren datos.

Callback y eventos UART

El callback se ejecuta cuando ocurren eventos de transmisión o recepción.

notifications Eventos principales

check
UART_TX_DONE
Transmisión completada
download
UART_RX_RDY
Datos recibidos listos para procesar
pause
UART_RX_DISABLED
Recepción deshabilitada (requiere re-enable)
swap_horiz
UART_RX_BUF_REQUEST
Solicita nuevo buffer (doble buffering)
warning
Importante: Tras UART_RX_DISABLED debes llamar a uart_rx_enable() de nuevo con un buffer válido.
info
Nota: Los callbacks se ejecutan en contexto de interrupción. Evita operaciones bloqueantes.

Estructura del evento

struct uart_event {
enum uart_event_type type;
union { ... } data;
};

Estructura del callback UART

Ejemplo completo de manejo de eventos en el callback.

Estructura del callback
#define RX_BUF_SIZE 64
#define RX_TIMEOUT 100
static uint8_t rx_buf[RX_BUF_SIZE];
static void uart_cb(const struct device *dev,
struct uart_event *evt, void *user_data) {
switch (evt->type) {
case UART_TX_DONE:
/* Transmisión completada */
break;
case UART_RX_RDY:
/* Procesar: evt->data.rx.buf + offset, len */
break;
case UART_RX_DISABLED:
uart_rx_enable(dev, rx_buf, sizeof(rx_buf), RX_TIMEOUT);
break;
}
}
UART_TX_DONE
TX completado
UART_RX_RDY
Datos listos
UART_RX_DISABLED
Re-habilitar

Doble buffering para recepción continua

Usar dos buffers permite recibir datos sin perder ningún byte.

swap_horiz Concepto de doble buffer

Mientras un buffer se procesa, el otro recibe datos nuevos. Se alternan continuamente.

Buffer A
Recibiendo
sync_alt
Buffer B
Procesando
Cuando A se llena, se intercambia con B automáticamente

Flujo de eventos

1
UART_RX_BUF_REQUEST
Proporcionar buffer B
2
UART_RX_RDY
Procesar datos del buffer A
3
UART_RX_BUF_RELEASED
Buffer A disponible
4
Repetir con buffers intercambiados

Código de doble buffering

Implementación completa del patrón de doble buffer en UART.

Implementación doble buffer
#define BUF_SIZE 64
static uint8_t rx_buf_a[BUF_SIZE], rx_buf_b[BUF_SIZE];
static uint8_t *next_buf = rx_buf_b;
static void uart_cb(...) {
switch (evt->type) {
case UART_RX_BUF_REQUEST:
uart_rx_buf_rsp(dev, next_buf, BUF_SIZE);
break;
case UART_RX_RDY:
process_data(evt->data.rx.buf + evt->data.rx.offset,
evt->data.rx.len);
break;
case UART_RX_BUF_RELEASED:
next_buf = evt->data.rx_buf.buf;
break;
}
}
speed
Beneficio: Recepción continua sin pérdida de datos.
memory
Memoria: Requiere 2x tamaño de buffer.
check_circle

Lo que aprendiste hoy

bug_report Logging

  • check printk() para salida básica
  • check LOG_ERR/WRN/INF/DBG para logging profesional
  • check Filtrado por nivel y módulo

cable UART

  • check Comunicación serie asíncrona TX/RX
  • check API asíncrono con callbacks
  • check Doble buffering para recepción continua
tips_and_updates

"El logging es tu mejor herramienta de depuración en sistemas embebidos."

APIs principales

LOG_MODULE_REGISTER(name, level)
Registrar módulo de logging
LOG_ERR/WRN/INF/DBG(fmt, ...)
Macros de logging por nivel
uart_callback_set(dev, cb, data)
Registrar callback UART
uart_tx(dev, buf, len, timeout)
Transmitir datos
uart_rx_enable(dev, buf, len, timeout)
Habilitar recepción
uart_rx_buf_rsp(dev, buf, len)
Proporcionar buffer (doble buffering)
arrow_forward Siguiente módulo

1.5 Protocolos serie: I2C

Aprenderemos a comunicarnos con sensores y periféricos usando el protocolo I2C.