zephyr-estructura-proyecto-compilacion

Estructura de un proyecto Zephyr y sistema de compilación

  • 5 min

Una aplicación fuera del árbol (out-of-tree) es un proyecto que vive separado del código fuente de Zephyr y se vincula con él durante la compilación.

Esta separación evita modificar los ejemplos o las fuentes del sistema operativo. Así puedes actualizar Zephyr sin perder cambios ni mezclar el código de tu aplicación con el del proyecto base.

Hoy vamos a ver cómo crear esa estructura desde cero y entender qué hace cada fichero.

Anatomía de un proyecto Zephyr

Un proyecto mínimo en Zephyr no es solo un fichero .c. Necesitamos decirle al sistema qué hardware vamos a usar, qué librerías del OS necesitamos y cómo compilarlo todo.

La estructura de carpetas mínima viable se ve así:

mi-proyecto/ ├── CMakeLists.txt <— El punto de entrada del sistema de construcción ├── prj.conf <— Configuración del Kernel (Kconfig) └── src/ └── main.c <— Tu código fuente C

Vamos a desgranar cada uno, porque entender esto es la base de todo lo que haremos después.

CMakeLists.txt

Si vienes de Arduino, esto puede parecer marciano. Zephyr utiliza CMake como sistema de generación de proyectos.

El fichero CMakeLists.txt es el “pegamento”. Su trabajo principal es encontrar dónde está instalado Zephyr en tu disco duro y decirle: “Oye, compila este proyecto usando tus herramientas”.

Un contenido típico y mínimo es este:

cmake_minimum_required(VERSION 3.20.0)

# Buscamos el paquete de Zephyr. Esto carga toda la maquinaria del OS.
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

# Definimos nuestro proyecto
project(mi_proyecto)

# Añadimos nuestros ficheros fuente a la compilación
target_sources(app PRIVATE src/main.c)
Copied!

La línea find_package(Zephyr ...) conecta el proyecto con Zephyr. Importa las definiciones de placas, drivers y reglas de compilación.

prj.conf (Kconfig)

Este es, posiblemente, el fichero más importante y característico de Zephyr.

En otros sistemas, para activar una funcionalidad (como el Bluetooth o el USB), sueles tener que buscar un fichero de cabecera .h gigante y descomentar líneas, o usar una herramienta gráfica que genera código.

Zephyr usa Kconfig (el mismo sistema que el Kernel de Linux). El fichero prj.conf es una lista de símbolos de configuración que activan o desactivan partes del Sistema Operativo.

Por ejemplo, si quieres usar printf y manejar pines GPIO, tu prj.conf se verá así:

CONFIG_GPIO=y
CONFIG_PRINTK=y
Copied!

Concepto clave: En Zephyr, solo se compila lo que activas aquí. Si no pones CONFIG_BT=y, la pila de Bluetooth ni siquiera se incluirá en el binario final, ahorrando memoria. Es un sistema modular extremo.

src/main.c

Aquí vive tu lógica. Es un fichero C estándar. La única diferencia con un C convencional es que la función main() es el punto de entrada de un hilo del sistema operativo, no el arranque bare-metal del procesador (de eso ya se ha encargado Zephyr antes de llamarte).

Nuestro espacio de trabajo (workspace)

Para mantener el orden, recomiendo la siguiente topología de carpetas, conocida como T2 o Freestanding Application dentro del workspace:

Volvemos a la carpeta zephyrproject donde ejecutamos west init. Junto al directorio zephyr, creamos otra carpeta llamada app-test o mis-proyectos.

zephyrproject/ ├── .west/ ├── bootloader/ ├── modules/ ├── tools/ ├── zephyr/ <— El OS (¡No tocar!) └── mis-proyectos/ └── hola-mundo/ <— Aquí trabajaremos ├── CMakeLists.txt ├── prj.conf └── src/ └── main.c

El ciclo de compilación con west

Ahora que tenemos los archivos, ¿cómo se convierte esto en un binario .hex o .bin para nuestra placa?

West orquesta el proceso mediante el siguiente flujo:

West lee tu CMakeLists.txt y los argumentos que le pasas (como la placa).

Llama a CMake, que genera los ficheros de construcción (Makefiles o Ninja files) en una carpeta llamada build.

Llama a Ninja (un sistema de construcción ultrarrápido) para compilar el código C y enlazarlo.

Genera los binarios finales.

Comando de compilación

Desde la terminal, con el entorno virtual de Python activado, entramos en mis-proyectos/hola-mundo y ejecutamos:

west build -p always -b <nombre_de_placa> .
Copied!

Desglosemos este comando:

  • build: La acción a realizar.

  • -p always: Significa “pristine” (prístino). Le dice a West que borre la carpeta build anterior y compile desde cero.

  • -p always: Significa pristine. Elimina la compilación anterior y configura el proyecto desde cero. Es útil al cambiar de placa o para descartar una caché problemática; para cambios normales, CMake vuelve a configurar lo necesario y la compilación incremental es más rápida.

  • -b <nombre_de_placa>: Especifica el board target. Por ejemplo, nrf52840dk/nrf52840; consulta west boards para ver los targets disponibles en tu versión.

  • .: Indica que el proyecto está en el directorio actual.

La carpeta build/

Al ejecutar el comando, aparecerá una carpeta build/ dentro de tu proyecto.

Dentro de build/zephyr/ encontrarás:

  • zephyr.elf: El binario con símbolos de depuración.
  • zephyr.hex o zephyr.bin: El archivo listo para flashear.
  • zephyr.dts: El árbol de dispositivos final compilado (muy útil para depurar hardware).

No subas la carpeta build/ a Git. Contiene archivos temporales y específicos de tu máquina. Añade build/ al archivo .gitignore.

Con la estructura preparada, ya podemos escribir código y hacer nuestro primer programa real.