Quando um projeto embarcado cresce alem de um simples super-loop, um RTOS (Real-Time Operating System) se torna essencial. Ele oferece multitarefa preemptiva, sincronizacao entre tarefas e gerenciamento de recursos — tudo com garantias de tempo real. Neste artigo final da serie, integramos Zig com FreeRTOS e construimos um projeto IoT completo.
Pre-requisito: Conhecimento dos artigos anteriores da serie, especialmente interrupts e timers.
Por Que Usar um RTOS?
| Sem RTOS (Super Loop) | Com RTOS |
|---|---|
| Uma tarefa por vez | Multiplas tarefas concorrentes |
| Timing por delays | Scheduling preemptivo com prioridades |
| Dificil escalar | Modular e escalavel |
| Compartilhamento manual de recursos | Mutexes e semaphores |
| Sem isolamento | Stacks separadas por tarefa |
Integrando Zig com FreeRTOS
FreeRTOS e escrito em C, o que torna a integracao com Zig natural gracas a interoperabilidade nativa.
Configuracao no build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(.{
.cpu_arch = .thumb,
.os_tag = .freestanding,
.abi = .none,
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "firmware",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Adicionar sources do FreeRTOS
exe.addCSourceFiles(.{
.files = &.{
"freertos/tasks.c",
"freertos/queue.c",
"freertos/list.c",
"freertos/timers.c",
"freertos/portable/GCC/ARM_CM4F/port.c",
"freertos/portable/MemMang/heap_4.c",
},
});
exe.addIncludePath(b.path("freertos/include"));
exe.addIncludePath(b.path("freertos/portable/GCC/ARM_CM4F"));
exe.addIncludePath(b.path("config")); // FreeRTOSConfig.h
exe.linkLibC();
exe.setLinkerScript(b.path("linker.ld"));
b.installArtifact(exe);
}
Bindings para FreeRTOS
const c = @cImport({
@cInclude("FreeRTOS.h");
@cInclude("task.h");
@cInclude("queue.h");
@cInclude("semphr.h");
});
pub const TaskHandle = c.TaskHandle_t;
pub const QueueHandle = c.QueueHandle_t;
pub const SemaphoreHandle = c.SemaphoreHandle_t;
pub fn criarTask(
funcao: c.TaskFunction_t,
nome: [*:0]const u8,
stack_size: u32,
prioridade: u32,
) !TaskHandle {
var handle: TaskHandle = undefined;
const resultado = c.xTaskCreate(
funcao,
nome,
@intCast(stack_size),
null,
@intCast(prioridade),
&handle,
);
if (resultado != c.pdPASS) return error.TaskCreationFailed;
return handle;
}
pub fn iniciarScheduler() void {
c.vTaskStartScheduler();
// Nunca retorna se bem-sucedido
unreachable;
}
pub fn delayTicks(ticks: u32) void {
c.vTaskDelay(ticks);
}
pub fn delayMs(ms: u32) void {
c.vTaskDelay(ms / c.portTICK_PERIOD_MS);
}
Criando Tasks em Zig
const std = @import("std");
const rtos = @import("rtos.zig");
const c = @cImport({
@cInclude("FreeRTOS.h");
@cInclude("task.h");
});
fn taskLed(_: ?*anyopaque) callconv(.C) void {
// Configurar GPIO LED
configurarPino(GPIOC, 13, .output);
while (true) {
GPIOC.odr ^= (1 << 13); // Toggle LED
rtos.delayMs(500);
}
}
fn taskSensor(_: ?*anyopaque) callconv(.C) void {
// Inicializar I2C
i2cInit();
while (true) {
const temp = lerTemperatura() catch 0;
// Processar temperatura...
_ = temp;
rtos.delayMs(1000);
}
}
fn taskUart(_: ?*anyopaque) callconv(.C) void {
uartInit(115200);
while (true) {
uartEnviarString("Sistema rodando\r\n");
rtos.delayMs(5000);
}
}
pub fn main() void {
// Configurar hardware basico
configurarClocks();
// Criar tasks com prioridades diferentes
_ = rtos.criarTask(taskLed, "LED", 128, 1) catch {};
_ = rtos.criarTask(taskSensor, "SENSOR", 256, 2) catch {};
_ = rtos.criarTask(taskUart, "UART", 256, 1) catch {};
// Iniciar scheduler (nunca retorna)
rtos.iniciarScheduler();
}
Sincronizacao entre Tasks
Queues (Filas)
Queues permitem comunicacao segura entre tasks:
const c = @cImport({
@cInclude("FreeRTOS.h");
@cInclude("queue.h");
});
const LeituraSensor = struct {
tipo: enum(u8) { temperatura, umidade, pressao },
valor: f32,
timestamp: u32,
};
var fila_sensores: c.QueueHandle_t = undefined;
fn inicializarFilas() void {
fila_sensores = c.xQueueCreate(
10, // Capacidade
@sizeOf(LeituraSensor),
);
}
fn taskProdutorSensor(_: ?*anyopaque) callconv(.C) void {
while (true) {
const leitura = LeituraSensor{
.tipo = .temperatura,
.valor = lerTemperatura() catch 0,
.timestamp = millis(),
};
// Enviar para a fila (espera ate 100ms)
_ = c.xQueueSend(fila_sensores, &leitura, 100);
rtos.delayMs(500);
}
}
fn taskConsumidor(_: ?*anyopaque) callconv(.C) void {
while (true) {
var leitura: LeituraSensor = undefined;
// Receber da fila (bloqueia ate receber)
if (c.xQueueReceive(fila_sensores, &leitura, c.portMAX_DELAY) == c.pdTRUE) {
// Processar leitura
uartEnviarString("Temperatura: ");
// enviar valor...
}
}
}
Semaphores e Mutexes
var mutex_uart: c.SemaphoreHandle_t = undefined;
var sem_dados_prontos: c.SemaphoreHandle_t = undefined;
fn inicializarSync() void {
mutex_uart = c.xSemaphoreCreateMutex();
sem_dados_prontos = c.xSemaphoreCreateBinary();
}
fn uartProtegido(mensagem: []const u8) void {
// Adquirir mutex antes de usar UART
if (c.xSemaphoreTake(mutex_uart, 1000) == c.pdTRUE) {
uartEnviarString(mensagem);
_ = c.xSemaphoreGive(mutex_uart);
}
}
// Qualquer task pode chamar uartProtegido() com seguranca
fn taskA(_: ?*anyopaque) callconv(.C) void {
while (true) {
uartProtegido("Task A executando\r\n");
rtos.delayMs(1000);
}
}
fn taskB(_: ?*anyopaque) callconv(.C) void {
while (true) {
uartProtegido("Task B executando\r\n");
rtos.delayMs(1500);
}
}
Projeto Completo: Sensor IoT
Integrando tudo em um projeto de monitoramento de temperatura com envio por UART:
const std = @import("std");
const rtos = @import("rtos.zig");
// Configuracoes do projeto
const INTERVALO_LEITURA_MS = 2000;
const INTERVALO_ENVIO_MS = 10000;
const MAX_LEITURAS_BUFFER = 20;
const Leitura = struct {
temperatura: f32,
umidade: f32,
timestamp_ms: u32,
};
var buffer_leituras: [MAX_LEITURAS_BUFFER]Leitura = undefined;
var idx_leitura: usize = 0;
var mutex_buffer: c.SemaphoreHandle_t = undefined;
fn taskColeta(_: ?*anyopaque) callconv(.C) void {
while (true) {
const temp = lerTemperatura() catch continue;
const umid = lerUmidade() catch continue;
if (c.xSemaphoreTake(mutex_buffer, 100) == c.pdTRUE) {
buffer_leituras[idx_leitura % MAX_LEITURAS_BUFFER] = .{
.temperatura = temp,
.umidade = umid,
.timestamp_ms = millis(),
};
idx_leitura += 1;
_ = c.xSemaphoreGive(mutex_buffer);
}
rtos.delayMs(INTERVALO_LEITURA_MS);
}
}
fn taskEnvio(_: ?*anyopaque) callconv(.C) void {
while (true) {
if (c.xSemaphoreTake(mutex_buffer, 100) == c.pdTRUE) {
// Calcular media
var soma_temp: f32 = 0;
var soma_umid: f32 = 0;
const count = @min(idx_leitura, MAX_LEITURAS_BUFFER);
for (buffer_leituras[0..count]) |l| {
soma_temp += l.temperatura;
soma_umid += l.umidade;
}
_ = c.xSemaphoreGive(mutex_buffer);
if (count > 0) {
const c_f: f32 = @floatFromInt(count);
// Enviar dados formatados via UART
uartProtegido("DADOS:");
// temperatura media, umidade media, count
}
}
rtos.delayMs(INTERVALO_ENVIO_MS);
}
}
fn taskWatchdog(_: ?*anyopaque) callconv(.C) void {
while (true) {
// Alimentar watchdog do hardware
feedWatchdog();
// Verificar saude do sistema
const free_heap = c.xPortGetFreeHeapSize();
if (free_heap < 1024) {
uartProtegido("ALERTA: Pouca memoria!\r\n");
}
rtos.delayMs(1000);
}
}
pub fn main() void {
configurarClocks();
i2cInit();
uartInit(115200);
inicializarSync();
uartEnviarString("=== Sensor IoT Zig + FreeRTOS ===\r\n");
_ = rtos.criarTask(taskColeta, "COLETA", 256, 2) catch {};
_ = rtos.criarTask(taskEnvio, "ENVIO", 512, 1) catch {};
_ = rtos.criarTask(taskWatchdog, "WDG", 128, 3) catch {};
rtos.iniciarScheduler();
}
Conclusao da Serie
Esta serie cobriu os fundamentos de programacao embarcada com Zig:
- Setup Embedded — Toolchain e bare metal
- GPIO e Perifericos — UART, SPI, I2C
- Interrupts e Timers — Event-driven
- Integracao com RTOS — FreeRTOS (este artigo)
Proximos Passos
- Programacao de Sistemas com Zig — Linux de baixo nivel
- Otimizacao de Performance — Maximize a eficiencia
- Zig e Interoperabilidade com C — Use drivers existentes
- Migrar de C para Zig — Transicao gradual
Concluiu a serie? Parabens! Compartilhe seus projetos embedded com Zig na comunidade Zig Brasil!