MCU & FPGA RTOS Filas, Flags, Mutexes, Notificações e Eventos no Zephyr RTOS

Filas, Flags, Mutexes, Notificações e Eventos no Zephyr RTOS

Flags, Semáforos e Eventos no Zephyr: Sinalização e Coordenação de Threads

2.1 O que são “Flags” no contexto do Zephyr?

No Zephyr, o termo flag não aparece como um objeto único e explícito como em alguns RTOS clássicos. Em vez disso, a ideia de sinalização é implementada por meio de semáforos (k_sem), eventos (k_event) e do mecanismo mais avançado de polling (k_poll).

O ponto central é o seguinte:
flags não transportam dados, apenas significado. Elas indicam que algo aconteceu ou que uma condição foi satisfeita. Isso as torna extremamente eficientes para sincronização entre threads, interrupções (ISRs) e até drivers.


2.2 Semáforos (k_sem): a forma mais simples de flag

O semáforo do Zephyr (k_sem) é o mecanismo mais direto para sinalização. Ele pode ser entendido como um contador protegido pelo kernel.

Um semáforo é ideal quando:

  • Uma thread precisa esperar outra concluir algo
  • Um recurso está disponível ou indisponível
  • Um evento ocorre repetidamente

Declaração de um semáforo

#include <zephyr/kernel.h>

K_SEM_DEFINE(meu_sem, 0, 1);

Parâmetros:

  • Valor inicial: 0 → começa bloqueado
  • Valor máximo: 1 → comportamento de flag binária

Esse padrão é extremamente comum.


2.3 Thread aguardando um semáforo (consumidor do evento)

void thread_espera(void)
{
    while (1) {
        k_sem_take(&meu_sem, K_FOREVER);

        printk("Evento recebido!\n");
    }
}

Aqui:

  • A thread entra em estado bloqueado
  • Não consome CPU
  • Só é acordada quando o semáforo for liberado

Esse comportamento é determinístico e eficiente energeticamente, essencial para sistemas embarcados.


2.4 Thread ou ISR liberando o semáforo (produtor do evento)

void thread_sinaliza(void)
{
    while (1) {
        k_sleep(K_SECONDS(2));
        k_sem_give(&meu_sem);
    }
}

O mesmo código pode ser usado em uma interrupção:

void minha_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    k_sem_give(&meu_sem);
}

O Zephyr garante que k_sem_give() é ISR-safe, algo crítico para aplicações reais.


2.5 Semáforo binário vs semáforo contador

Embora seja comum usar semáforos como flags binárias, o Zephyr permite semáforos contadores.

Exemplo:

K_SEM_DEFINE(buffer_sem, 0, 10);

Isso permite:

  • Contabilizar múltiplos eventos
  • Modelar filas lógicas
  • Controlar acesso a buffers circulares

Porém, quanto maior o contador, maior o risco de esconder problemas de sincronização. Em sistemas críticos, prefira valores pequenos e bem controlados.


2.6 Limitações dos semáforos como flags

Semáforos resolvem muitos problemas, mas têm limitações claras:

  • Não permitem múltiplas condições simultâneas
  • Não carregam contexto semântico (qual evento ocorreu?)
  • Não são ideais para state machines complexas

Quando você precisa esperar mais de uma condição, ou combinar eventos, entra em cena o k_event.


2.7 Eventos (k_event): flags múltiplas em um único objeto

O k_event é um mecanismo poderoso que permite trabalhar com múltiplos bits de evento, de forma semelhante a event flags clássicos.

Declaração:

K_EVENT_DEFINE(meu_evento);

Cada bit do evento representa uma condição distinta.


2.8 Definindo flags de evento

#define EVT_SENSOR_OK   BIT(0)
#define EVT_TIMEOUT     BIT(1)
#define EVT_ERRO        BIT(2)

Essas flags podem ser combinadas livremente.


2.9 Sinalizando eventos

k_event_post(&meu_evento, EVT_SENSOR_OK);

Ou múltiplos eventos de uma vez:

k_event_post(&meu_evento, EVT_SENSOR_OK | EVT_TIMEOUT);

Isso torna o código muito mais expressivo do que múltiplos semáforos espalhados.


2.10 Aguardando eventos

uint32_t eventos;

eventos = k_event_wait(&meu_evento,
                        EVT_SENSOR_OK | EVT_ERRO,
                        false,
                        K_FOREVER);

if (eventos & EVT_SENSOR_OK) {
    printk("Sensor OK\n");
}

if (eventos & EVT_ERRO) {
    printk("Erro detectado\n");
}

Parâmetros importantes:

  • Máscara de eventos esperados
  • reset = false → evento permanece ativo
  • Timeout configurável

Esse modelo é ideal para máquinas de estado, drivers e sistemas reativos.


2.11 Quando usar k_sem vs k_event

Use k_sem quando:

  • Um único evento importa
  • Simplicidade é prioridade
  • Código precisa ser extremamente leve

Use k_event quando:

  • Vários eventos precisam ser monitorados
  • Há múltiplas fontes de sinalização
  • A lógica depende da combinação de estados

2.12 Erros comuns com Flags e Eventos

Alguns problemas recorrentes em projetos reais:

  • Usar múltiplos semáforos onde um evento resolveria
  • Não limpar eventos corretamente
  • Criar dependência circular de eventos
  • Tratar eventos como filas de dados (erro conceitual)

Esses erros geralmente aparecem como travamentos intermitentes, difíceis de depurar.


Related Post