MCU & FPGA geral,RTOS Zephyr e Integração Avançada: Interrupções + Soft Timers + Workqueues

Zephyr e Integração Avançada: Interrupções + Soft Timers + Workqueues



4 — Workqueues no Zephyr

Processamento desacoplado, escalável e seguro

Se interrupções e soft timers são fontes de eventos, workqueues são onde o trabalho de verdade acontece. Elas representam um dos mecanismos mais importantes do Zephyr para firmware industrial e IoT de produção, justamente por permitirem processamento assíncrono fora de ISR, com controle de contexto, prioridade e isolamento.

Em termos arquiteturais, pense em k_work como estágios de um pipeline, e em k_work_q como linhas de produção.


4.1 O que é uma Workqueue, de fato

Uma workqueue no Zephyr é:

  • Uma thread dedicada, gerenciada pelo kernel.
  • Um executor de tarefas (work items).
  • Um mecanismo para adiar e ordenar processamento.
  • Uma fronteira segura entre eventos e lógica pesada.

Por padrão, o Zephyr oferece:

  • system workqueue (k_sys_work_q)
  • Workqueues customizadas, criadas pelo desenvolvedor.

⚠️ Usar sempre a system workqueue funciona em exemplos,
mas firmware de produção quase sempre cria workqueues próprias.


4.2 O erro comum: jogar tudo na system workqueue

Exemplo ruim (mas frequente):

k_work_submit(&my_work);

Sem controle de:

  • Prioridade
  • Latência
  • Concorrência
  • Starvation
  • Dependência entre módulos

Resultado: um subsistema lento pode travar todo o sistema.

👉 Regra prática:
Se o trabalho é crítico ou específico, ele merece sua própria workqueue.


4.3 Criando uma Workqueue dedicada

Definição da workqueue

#include <zephyr/kernel.h>

#define WORKQ_STACK_SIZE 1024
#define WORKQ_PRIORITY   5

K_THREAD_STACK_DEFINE(workq_stack, WORKQ_STACK_SIZE);
static struct k_work_q processing_workq;

Inicialização

void workqueue_init(void)
{
    k_work_queue_start(
        &processing_workq,
        workq_stack,
        K_THREAD_STACK_SIZEOF(workq_stack),
        WORKQ_PRIORITY,
        NULL
    );
}

Agora você tem um executor isolado, previsível e controlado.


4.4 Criando um work item

Um k_work representa uma unidade de processamento:

static void process_work_handler(struct k_work *work)
{
    /* Aqui é contexto de thread normal */
    parse_data();
    run_algorithm();
    store_results();
}

K_WORK_DEFINE(process_work, process_work_handler);

Submissão do trabalho:

k_work_submit_to_queue(&processing_workq, &process_work);

✔ Pode usar mutex
✔ Pode alocar memória
✔ Pode acessar drivers
✔ Pode logar
✔ Pode chamar APIs bloqueantes


4.5 Integrando eventos → workqueues

Agora conectamos tudo:

ISR → Timer → Workqueue

static void gpio_event_isr(const struct device *dev,
                           struct gpio_callback *cb,
                           uint32_t pins)
{
    /* Apenas dispara o pipeline */
    k_work_submit_to_queue(&processing_workq, &process_work);
}

Ou, de forma mais limpa:

static void gpio_event_isr(...)
{
    k_sem_give(&event_sem);
}
void event_thread(void)
{
    while (1) {
        k_sem_take(&event_sem, K_FOREVER);
        k_work_submit_to_queue(&processing_workq, &process_work);
    }
}

👉 Importante:
Nunca submeta trabalho pesado diretamente da ISR se ele puder crescer no futuro.


4.6 Workqueues como estágios de pipeline

Em sistemas reais, você verá algo assim:

[ ISR ]

[ Timer / Debounce ]

[ Workqueue: Aquisição ]

[ Workqueue: Processamento ]

[ Workqueue: Comunicação ]

Cada estágio:

  • Tem sua própria prioridade.
  • Pode falhar sem quebrar o sistema.
  • Pode ser monitorado.
  • Pode ser desligado ou reiniciado.

Isso é arquitetura de firmware, não apenas código.


4.7 Regra de ouro das Workqueues

✔ ISR nunca processa
✔ Timer nunca processa
✔ Workqueue processa
✔ Workqueues isolam falhas
✔ Workqueues tornam o sistema escalável

Quando você domina workqueues, o firmware deixa de ser linear e passa a ser industrialmente robusto.

Related Post