---
title: "Setup de Ambiente Embedded com Zig: Compilacao Cruzada e Bare Metal"
url: "https://ziglang.com.br/tutoriais/zig-embedded-setup/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-embedded-setup.MD"
description: "Configure Zig para desenvolvimento de sistemas embarcados. Compilacao cruzada para ARM Cortex-M, RISC-V e AVR, primeiro programa bare-metal, ferramentas de debug. Tutorial completo em portugues."
date: "2026-02-21"
author: ""
---

# Setup de Ambiente Embedded com Zig: Compilacao Cruzada e Bare Metal

Configure Zig para desenvolvimento de sistemas embarcados. Compilacao cruzada para ARM Cortex-M, RISC-V e AVR, primeiro programa bare-metal, ferramentas de debug. Tutorial completo em portugues.


Programar sistemas embarcados com Zig e uma experiencia surpreendentemente suave. Diferente de C, onde voce precisa configurar toolchains complexas (gcc-arm-none-eabi, makefiles, etc.), Zig inclui compilacao cruzada nativa para dezenas de targets. Um unico comando compila para ARM, RISC-V, AVR ou qualquer outra arquitetura suportada. Neste artigo, configuramos tudo do zero.

> Para uma visao geral de embedded com Zig, veja [Zig Embedded Systems](/tutoriais/zig-embedded-systems/).

## Por Que Zig para Embedded?

| Aspecto | C Tradicional | Zig |
|---------|--------------|-----|
| **Toolchain** | gcc-arm + make + scripts | `zig build` (tudo incluido) |
| **Cross-compilation** | Instalar toolchain por target | `zig build -Dtarget=thumb-...` |
| **Seguranca** | Buffer overflows faceis | Verificacao de bounds |
| **Alocacao** | malloc/free (perigoso em embedded) | Allocators explicitos |
| **Interop C** | Nativo | Nativo (sem custo) |
| **Tamanho binario** | Pequeno | Equivalente ou menor |
| **Debug** | GDB | GDB (compativel) |

## Targets Suportados

Zig suporta nativamente estas plataformas embarcadas:

```bash
# Listar todos os targets disponiveis
zig targets | grep -E "(thumb|riscv32|avr)"
```

Principais targets para embedded:

| Familia | Target Zig | Exemplo |
|---------|-----------|---------|
| ARM Cortex-M0 | `thumb-freestanding-none` | RP2040 (Pico) |
| ARM Cortex-M3 | `thumb-freestanding-none` | STM32F1 |
| ARM Cortex-M4 | `thumb-freestanding-none` | STM32F4 |
| RISC-V 32 | `riscv32-freestanding-none` | ESP32-C3 |
| AVR | `avr-freestanding-none` | ATmega328P |

## Primeiro Programa Bare-Metal

Vamos criar um programa minimo que pisca um LED em um ARM Cortex-M (o classico "blink").

### Estrutura do Projeto

```
meu-projeto-embedded/
├── build.zig
├── build.zig.zon
├── linker.ld
└── src/
    └── main.zig
```

### Linker Script Basico

O linker script define o layout de memoria do microcontrolador:

```ld
/* linker.ld - Exemplo para STM32F103 */
MEMORY
{
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 64K
    RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}

SECTIONS
{
    .isr_vector :
    {
        KEEP(*(.isr_vector))
    } > FLASH

    .text :
    {
        *(.text*)
        *(.rodata*)
    } > FLASH

    .data :
    {
        _sdata = .;
        *(.data*)
        _edata = .;
    } > RAM AT > FLASH

    .bss :
    {
        _sbss = .;
        *(.bss*)
        _ebss = .;
    } > RAM

    _stack_top = ORIGIN(RAM) + LENGTH(RAM);
}
```

### build.zig para Embedded

```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_m3 },
    });

    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "firmware",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Usar linker script customizado
    exe.setLinkerScript(b.path("linker.ld"));

    // Desabilitar stack protector (nao disponivel em bare metal)
    exe.root_module.stack_protector = false;

    b.installArtifact(exe);

    // Gerar arquivo .bin para flash
    const bin = b.addObjCopy(exe.getEmittedBin(), .{
        .format = .bin,
    });
    const install_bin = b.addInstallBinFile(bin.getOutput(), "firmware.bin");

    const flash_step = b.step("bin", "Gerar firmware.bin");
    flash_step.dependOn(&install_bin.step);
}
```

### Codigo Bare-Metal

```zig
// src/main.zig

// Definicoes de hardware para STM32F103
const RCC_BASE = 0x40021000;
const GPIOC_BASE = 0x40011000;

const RCC_APB2ENR: *volatile u32 = @ptrFromInt(RCC_BASE + 0x18);
const GPIOC_CRH: *volatile u32 = @ptrFromInt(GPIOC_BASE + 0x04);
const GPIOC_ODR: *volatile u32 = @ptrFromInt(GPIOC_BASE + 0x0C);

fn registrador(comptime endereco: usize) *volatile u32 {
    return @ptrFromInt(endereco);
}

// Tabela de vetores de interrupcao
export const vector_table linksection(".isr_vector") = blk: {
    const VectorTable = extern struct {
        stack_pointer: u32,
        reset: *const fn () callconv(.C) noreturn,
    };

    break :blk VectorTable{
        .stack_pointer = 0x20005000, // Topo da RAM
        .reset = &_start,
    };
};

fn _start() callconv(.C) noreturn {
    // Inicializar .bss com zeros
    // Copiar .data da Flash para RAM
    // (simplificado para este exemplo)

    main();

    while (true) {}
}

fn delay(contagem: u32) void {
    var i: u32 = 0;
    while (i < contagem) : (i += 1) {
        asm volatile ("nop");
    }
}

pub fn main() void {
    // Habilitar clock do GPIOC
    RCC_APB2ENR.* |= (1 << 4); // IOPCEN

    // Configurar PC13 como saida push-pull (LED onboard)
    GPIOC_CRH.* &= ~@as(u32, 0xF << 20); // Limpar bits
    GPIOC_CRH.* |= (0x2 << 20); // Output mode, 2MHz

    // Piscar LED infinitamente
    while (true) {
        GPIOC_ODR.* ^= (1 << 13); // Toggle PC13
        delay(500_000);
    }
}
```

### Compilando e Flashando

```bash
# Compilar
zig build -Doptimize=ReleaseSmall

# Gerar binario
zig build bin

# Flash com OpenOCD (STM32)
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \
    -c "program zig-out/bin/firmware.bin 0x08000000 verify reset exit"

# Ou com probe-rs (alternativa moderna)
probe-rs download --chip STM32F103C8 zig-out/bin/firmware.bin
```

## Usando microzig

O projeto [microzig](https://github.com/ZigEmbeddedGroup/microzig) oferece abstracoees de alto nivel para desenvolvimento embedded:

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

const led_pin = microzig.board.led;

pub fn main() !void {
    led_pin.setDir(.output);

    while (true) {
        led_pin.toggle();
        microzig.hal.time.sleep_ms(500);
    }
}
```

microzig abstrai as diferencas entre microcontroladores, oferecendo uma API unificada para GPIO, UART, SPI, I2C e timers.

## Ferramentas de Debug

### GDB com OpenOCD

```bash
# Terminal 1: Iniciar OpenOCD
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg

# Terminal 2: Conectar GDB
arm-none-eabi-gdb zig-out/bin/firmware.elf
(gdb) target remote :3333
(gdb) monitor reset halt
(gdb) break main
(gdb) continue
```

### Tamanho do Binario

Zig produz binarios muito pequenos para embedded:

```bash
# Verificar tamanho
arm-none-eabi-size zig-out/bin/firmware.elf

# Saida tipica para blink:
#    text    data     bss     dec     hex filename
#     284       0       0     284     11c firmware.elf
```

## Exercicios

1. **Multi-LED:** Modifique o programa para piscar 3 LEDs em sequencia com tempos diferentes.

2. **Botao:** Adicione leitura de um botao (GPIO input) que muda a velocidade do blink.

3. **Tamanho otimo:** Compare o tamanho do binario entre `ReleaseSmall`, `ReleaseSafe` e `Debug`.

---

## Proximo Artigo

No proximo artigo, exploramos [GPIO e Perifericos](/tutoriais/zig-embedded-series/artigo-2-gpio-peripherals/) em profundidade, incluindo UART, SPI e I2C.

### Conteudo Relacionado

- [Zig Cross Compilation](/tutoriais/zig-cross-compilation/) — Compilacao cruzada
- [Zig Build System](/tutoriais/zig-build-system/) — Configuracao de build
- [Zig e Interoperabilidade com C](/tutoriais/zig-c-interoperabilidade/) — Usando drivers C

---

*Duvidas sobre embedded com Zig? Participe da comunidade Zig Brasil!*
