MCU & FPGA RTOS Tutorial Zephyr OS: Como Criar o Primeiro Projeto com example-application, DeviceTree e West

Tutorial Zephyr OS: Como Criar o Primeiro Projeto com example-application, DeviceTree e West


Um “primeiro projeto” completo e idiomático no Zephyr: threads + logging + LED (com overlay) + console

Nesta seção eu vou fechar um tutorial que você consegue reaproveitar como “base padrão” para quase qualquer firmware Zephyr: ele sobe, inicializa log, cria duas threads (produtor/consumidor simples), pisca LED descrito por DeviceTree e manda mensagens no console.

A ideia é você ter um projeto que já nasce com arquitetura mínima saudável, sem virar “superloop gigante” e sem depender de pinos hardcoded.


Estrutura final do que vamos ter

Dentro do seu repo (renomeado ou não), o essencial fica assim:

  • app/CMakeLists.txt
  • app/prj.conf
  • app/src/main.c
  • app/boards/<sua_board>.overlay
  • app/boards/<sua_board>.conf (opcional)

app/prj.conf: console + logging + threads

Use isso como base:

# Console
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_PRINTK=y

# Logging (mais estruturado que printk)
CONFIG_LOG=y
CONFIG_LOG_MODE_DEFERRED=y
CONFIG_LOG_DEFAULT_LEVEL=3

# Kernel/Threads (normalmente  vem, mas deixo explícito)
CONFIG_MULTITHREADING=y

Por que LOG e não só printk? Porque LOG_* te dá níveis, tags por módulo, e controle fino (inclusive pra builds de release). O printk ainda é ótimo pra “pânico/early boot”, mas em firmware que cresce, log estruturado é mais sustentável.


app/CMakeLists.txt: simples e direto

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(primeiro_projeto_zephyr)

target_sources(app PRIVATE src/main.c)

Esse arquivo é o “ponto de entrada” do build da aplicação.


app/boards/<board>.overlay: LED via alias (relembrando)

Exemplo para nucleo_f411re (ajuste o GPIO para seu hardware real):

app/boards/nucleo_f411re.overlay

/ {
    leds {
        compatible = "gpio-leds";
        user_led: led_0 {
            gpios = <&gpioc 13 GPIO_ACTIVE_HIGH>;
            label = "USER_LED";
        };
    };

    aliases {
        led0 = &user_led;
    };
};

Se sua board já define led0 por padrão, você pode até não precisar disso. Mas eu gosto de manter no app quando estou “padronizando” comportamento entre placas.


app/src/main.c: 2 threads + fila + LED + logs

Aqui vai um exemplo completo e bem “Zephyr idiomático”, com comentários didáticos:

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/gpio.h>

/* Cria um "módulo de log" para este arquivo */
LOG_MODULE_REGISTER(app_main, LOG_LEVEL_INF);

/* LED vindo do DeviceTree alias led0 (definido no overlay) */
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);

/*
 * Vamos criar um canal simples produtor/consumidor:
 * - Produtor gera um contador (tick)
 * - Consumidor recebe e decide piscar LED
 */
K_MSGQ_DEFINE(tick_msgq, sizeof(uint32_t), 8, 4);

/* Stack e controle das threads */
#define STACK_SZ 1024
#define PRODUCER_PRIO 5
#define CONSUMER_PRIO 5

K_THREAD_STACK_DEFINE(producer_stack, STACK_SZ);
K_THREAD_STACK_DEFINE(consumer_stack, STACK_SZ);

static struct k_thread producer_thread;
static struct k_thread consumer_thread;

/* Função da thread produtora */
static void producer_fn(void *p1, void *p2, void *p3)
{
    ARG_UNUSED(p1);
    ARG_UNUSED(p2);
    ARG_UNUSED(p3);

    uint32_t tick = 0;

    while (1) {
        tick++;

        /* Envia tick para a fila (não bloqueia; se lotar, descarta o mais novo) */
        int rc = k_msgq_put(&tick_msgq, &tick, K_NO_WAIT);
        if (rc != 0) {
            LOG_WRN("Fila cheia: tick %u descartado", tick);
        } else {
            LOG_DBG("Produziu tick=%u", tick);
        }

        k_sleep(K_MSEC(200));
    }
}

/* Função da thread consumidora */
static void consumer_fn(void *p1, void *p2, void *p3)
{
    ARG_UNUSED(p1);
    ARG_UNUSED(p2);
    ARG_UNUSED(p3);

    uint32_t tick = 0;

    while (1) {
        /* Espera por um tick */
        int rc = k_msgq_get(&tick_msgq, &tick, K_FOREVER);
        if (rc != 0) {
            LOG_ERR("Falha ao receber da fila (%d)", rc);
            continue;
        }

        /* A cada 5 ticks, alterna o LED */
        if ((tick % 5U) == 0U) {
            gpio_pin_toggle_dt(&led);
            LOG_INF("tick=%u -> toggle LED", tick);
        }
    }
}

/* Entry point */
int main(void)
{
    /* Inicialização do LED */
    if (!gpio_is_ready_dt(&led)) {
        LOG_ERR("LED nao esta pronto. Verifique overlay/alias led0.");
        return 0;
    }

    int rc = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
    if (rc < 0) {
        LOG_ERR("Falha ao configurar LED (%d)", rc);
        return 0;
    }

    LOG_INF("Primeiro projeto Zephyr: threads + msgq + DT LED + logging");

    /* Criação das threads */
    k_thread_create(&producer_thread, producer_stack, STACK_SZ,
                    producer_fn, NULL, NULL, NULL,
                    PRODUCER_PRIO, 0, K_NO_WAIT);

    k_thread_create(&consumer_thread, consumer_stack, STACK_SZ,
                    consumer_fn, NULL, NULL, NULL,
                    CONSUMER_PRIO, 0, K_NO_WAIT);

    /* Em Zephyr, main pode terminar; mas aqui vamos “viver” no idle */
    return 0;
}

O que este main.c te entrega (em termos de arquitetura):

  • Separação clara: produção de dados (thread A) ≠ consumo/ação (thread B)
  • Fila com backpressure controlável: k_msgq é simples e previsível
  • Hardware descrito fora do código: LED vem do DeviceTree (overlay)
  • Logs estruturados: fica fácil subir nível, baixar nível, filtrar por módulo

7.6 Build e flash (rotina diária)

cd my-workspace/meu-produto-fw
west build -p always -b nucleo_f411re app
west flash

Se você estiver usando uma board diferente, só troca o -b.


0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

Debug, Tracing e Análise Temporal no FreeRTOS: Monitoramento Avançado de Tasks, Watermark e Confiabilidade em Tempo RealDebug, Tracing e Análise Temporal no FreeRTOS: Monitoramento Avançado de Tasks, Watermark e Confiabilidade em Tempo Real

Aprenda como aplicar debug estruturado, tracing, análise temporal e watermark no FreeRTOS para monitorar tasks, medir WCET, detectar jitter, prevenir stack overflow e aumentar a confiabilidade de sistemas embarcados em

0
Adoraria saber sua opinião, comente.x