---
title: "Zig Embarcados: ARM, RISC-V e IoT com HAL | Zig Brasil"
url: "https://ziglang.com.br/ecossistema/zig-embarcados-arm-risc-v-e-iot-com-hal-zig-brasil/"
markdown_url: "https://ziglang.com.br/ecossistema/zig-embarcados-arm-risc-v-e-iot-com-hal-zig-brasil.MD"
description: "Use Zig para sistemas embarcados: HAL, microcontroladores ARM e RISC-V, drivers e bare metal. Guia completo com exemplos práticos em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Zig Embarcados: ARM, RISC-V e IoT com HAL | Zig Brasil

Use Zig para sistemas embarcados: HAL, microcontroladores ARM e RISC-V, drivers e bare metal. Guia completo com exemplos práticos em português.


# Zig para Embarcados — HAL, Microcontroladores e IoT

O Zig está se posicionando como uma alternativa moderna e segura ao C no desenvolvimento de sistemas embarcados. Com compilação cruzada nativa, zero overhead de runtime, controle total de memória e a capacidade de gerar binários para dezenas de arquiteturas, o Zig oferece tudo o que desenvolvedores embarcados precisam, com a vantagem de segurança e ergonomia superiores ao C.

## Por Que Zig para Embarcados

Desenvolvedores de sistemas embarcados tradicionalmente usam C (e ocasionalmente C++) por necessidade: são as únicas linguagens que oferecem o controle e a eficiência necessários para hardware com recursos limitados. O Zig desafia esse monopólio oferecendo:

- **Zero runtime**: Nenhum código de inicialização oculto
- **Sem alocação implícita**: Todas as alocações são explícitas e rastreáveis
- **Compilação cruzada nativa**: Compile para ARM, RISC-V, MIPS e outras arquiteturas sem toolchains externas
- **Interoperabilidade C total**: Use qualquer HAL ou SDK existente em C
- **Segurança de tipos**: Detecte erros em tempo de compilação que em C seriam undefined behavior
- **Tamanho binário mínimo**: Binários frequentemente menores que equivalentes em C

## MicroZig — O Framework Embarcado Principal

O MicroZig é o framework embarcado mais importante do ecossistema Zig. Ele fornece uma camada de abstração de hardware (HAL) unificada para diversos microcontroladores:

### Microcontroladores Suportados

- **STM32** (ARM Cortex-M): STM32F1, STM32F4, STM32H7 e mais
- **nRF** (Nordic Semiconductor): nRF52, nRF53
- **RP2040** (Raspberry Pi Pico)
- **ESP32** (Espressif) — suporte experimental
- **AVR** (Arduino/ATmega)
- **RISC-V**: GD32VF103, CH32V

### Exemplo: Blink LED no RP2040

```zig
const microzig = @import("microzig");
const rp2040 = microzig.hal;

const led = rp2040.gpio.num(25); // LED onboard do Pico

pub fn main() !void {
    led.setFunction(.sio);
    led.setDirection(.out);

    while (true) {
        led.put(1);
        rp2040.time.sleepMs(500);

        led.put(0);
        rp2040.time.sleepMs(500);
    }
}
```

### Configuração do build.zig

```zig
const std = @import("std");
const microzig = @import("microzig");

pub fn build(b: *std.Build) void {
    const optimize = b.standardOptimizeOption(.{});

    const firmware = microzig.addFirmware(b, .{
        .name = "meu-firmware",
        .root_source_file = b.path("src/main.zig"),
        .target = microzig.targets.rp2040,
        .optimize = optimize,
    });

    microzig.installFirmware(b, firmware, .{});

    // Step para flash
    const flash_step = b.step("flash", "Flash firmware no dispositivo");
    flash_step.dependOn(&microzig.addFlashStep(b, firmware).step);
}
```

## GPIO — Entrada e Saída Digital

```zig
const gpio = microzig.hal.gpio;

// Configurar pinos
const botao = gpio.num(13);
const led = gpio.num(25);
const buzzer = gpio.num(18);

pub fn init() void {
    // Saídas
    led.setDirection(.out);
    buzzer.setDirection(.out);

    // Entrada com pull-up
    botao.setDirection(.in);
    botao.setPullUp(true);
}

pub fn loop() void {
    // Ler botão (ativo baixo com pull-up)
    if (botao.read() == 0) {
        led.put(1);
        buzzer.put(1);
    } else {
        led.put(0);
        buzzer.put(0);
    }
}
```

## Comunicação Serial (UART)

```zig
const uart = microzig.hal.uart;

const serial = uart.num(0);

pub fn init() void {
    serial.apply(.{
        .baud_rate = 115200,
        .tx_pin = uart.Pin.gpio0,
        .rx_pin = uart.Pin.gpio1,
        .data_bits = .eight,
        .stop_bits = .one,
        .parity = .none,
    });
}

pub fn enviarDados(dados: []const u8) void {
    for (dados) |byte| {
        serial.writeByte(byte);
    }
}

pub fn lerDados(buffer: []u8) usize {
    var i: usize = 0;
    while (i < buffer.len) {
        if (serial.readByte()) |byte| {
            buffer[i] = byte;
            i += 1;
            if (byte == '\n') break;
        }
    }
    return i;
}
```

## SPI e I2C

### SPI

```zig
const spi = microzig.hal.spi;

const display_spi = spi.num(0);

pub fn init() void {
    display_spi.apply(.{
        .clock_pin = spi.Pin.gpio2,
        .mosi_pin = spi.Pin.gpio3,
        .miso_pin = spi.Pin.gpio4,
        .cs_pin = spi.Pin.gpio5,
        .baud_rate = 1_000_000,
        .mode = .mode_0,
    });
}

pub fn enviarComando(cmd: u8) void {
    display_spi.writeBlocking(&[_]u8{cmd});
}
```

### I2C

```zig
const i2c = microzig.hal.i2c;

const sensor_i2c = i2c.num(0);
const SENSOR_ADDR: u7 = 0x48; // Endereço do sensor de temperatura

pub fn lerTemperatura() !f32 {
    var buf: [2]u8 = undefined;
    try sensor_i2c.readFromDevice(SENSOR_ADDR, &buf);

    const raw = (@as(u16, buf[0]) << 8) | buf[1];
    return @as(f32, @floatFromInt(raw)) * 0.0625;
}
```

## Interrupções

```zig
const irq = microzig.hal.irq;

// Handler de interrupção para timer
fn timerHandler() callconv(.C) void {
    // Executar tarefa periódica
    toggleLed();
    clearTimerInterrupt();
}

pub fn configurarTimer() void {
    const timer = microzig.hal.timer.num(0);
    timer.configure(.{
        .period_ms = 1000,
        .callback = timerHandler,
    });
    timer.enable();
}

// Handler de interrupção para GPIO
fn botaoPressionado() callconv(.C) void {
    // Debounce
    const agora = microzig.hal.time.getTimestamp();
    if (agora - ultimo_clique > 200_000) { // 200ms debounce
        processar_evento();
        ultimo_clique = agora;
    }
}
```

## DMA (Direct Memory Access)

```zig
const dma = microzig.hal.dma;

pub fn transferirDadosComDma(origem: []const u8, destino: []u8) void {
    const canal = dma.channel(0);
    canal.configure(.{
        .source = @ptrToInt(origem.ptr),
        .destination = @ptrToInt(destino.ptr),
        .transfer_count = origem.len,
        .data_size = .byte,
        .increment_source = true,
        .increment_destination = true,
    });
    canal.trigger();
    canal.waitForCompletion();
}
```

## Bare Metal sem MicroZig

Para targets não suportados pelo MicroZig ou para controle total:

```zig
// Linker script customizado via build.zig
const exe = b.addExecutable(.{
    .name = "firmware",
    .root_source_file = b.path("src/main.zig"),
    .target = b.resolveTargetQuery(.{
        .cpu_arch = .thumb,
        .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
        .os_tag = .freestanding,
        .abi = .eabi,
    }),
    .optimize = .ReleaseSmall,
});
exe.setLinkerScript(b.path("linker.ld"));

// Código bare metal
export fn _start() callconv(.C) noreturn {
    // Inicializar stack pointer
    // Inicializar .bss e .data
    // Chamar main
    @call(.auto, main, .{});
    while (true) {
        asm volatile ("wfi");
    }
}

fn main() void {
    // Acessar registradores diretamente
    const GPIOA_BASE = 0x40020000;
    const GPIOA_MODER = @as(*volatile u32, @ptrFromInt(GPIOA_BASE + 0x00));
    const GPIOA_ODR = @as(*volatile u32, @ptrFromInt(GPIOA_BASE + 0x14));

    // Configurar PA5 como saída
    GPIOA_MODER.* = (GPIOA_MODER.* & ~@as(u32, 0x3 << 10)) | (0x1 << 10);

    // Toggle LED
    while (true) {
        GPIOA_ODR.* ^= (1 << 5);
        busyWait(1_000_000);
    }
}
```

## Casos de Uso em Produção

Zig para embarcados está sendo adotado em cenários reais. Confira nosso [case de Zig em sistemas embarcados industriais](/cases/case-zig-embedded-industria/) e veja como empresas de [telecomunicações](/cases/case-zig-telecom/) utilizam Zig em dispositivos de rede.

## Boas Práticas

1. **Use `ReleaseSmall`** para otimizar tamanho do binário
2. **Evite alocação dinâmica** — prefira buffers fixos
3. **Documente registradores** com comptime assertions
4. **Teste no hardware real** regularmente
5. **Use a stdlib com cuidado** — nem todas as funções estão disponíveis em freestanding

## Próximos Passos

Explore as [ferramentas de debug](/ecossistema/zig-debug-tools/) para depuração em hardware, as [ferramentas de profiling](/ecossistema/zig-profiling-tools/) para otimização, e as [bibliotecas de rede](/ecossistema/zig-network/) para conectividade IoT. Confira nossos [tutoriais](/tutoriais/) para projetos práticos e a seção de [carreira](/carreira/) para oportunidades em embarcados com Zig.
