Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HAL and Micro Library

The micro/* packages are Flint’s cross-chip peripheral APIs. They provide a stable, typed interface to hardware without exposing registers, memory-mapped IO addresses, or vendor-specific SDK calls.

The Four Layers

Flint’s hardware support is organized into four layers:

┌─────────────────────────────────────────────┐
│              Application Code               │
├─────────────────────────────────────────────┤
│         micro/*   board/*                   │  ← What you use
├─────────────────────────────────────────────┤
│              hal/*                          │  ← Target-selected implementation
└─────────────────────────────────────────────┘
PackageWhat it is
hal/*Target-selected runtime, boot/startup, peripheral implementation. Internal.
micro/*Cross-chip peripheral interfaces. This is what you import.
board/*Board pin maps, default clocks, convenience constructors.
ApplicationYour code.

You import micro/* and board/*. You do not import hal/* directly.

Startup

When your main runs, the target hal/* package has already:

  • Released resets
  • Brought up the clock tree and PLLs
  • Configured the watchdog
  • Initialized the timer/timebase
  • Configured XIP/flash for normal operation

You do not write startup code. You do not call SystemInit(). You write application code.

Builder-Style APIs

Peripherals with many options use builder-style initialization:

use micro/i2c

let mut bus = i2c.bus(0)
    .sda(gpio.pin(4))
    .scl(gpio.pin(5))
    .freq(400_000)
    .init()?

Builder methods shape configuration. The terminal init() (or open(), attach()) is the one fallible step. Once initialized, steady-state operations are infallible:

bus.write(0x68, data)?    // fallible: I2C can NACK
bus.flush()?              // fallible: flush can timeout

led.high()                // infallible: GPIO state change always works
led.low()                 // infallible

Capability Discovery

Optional features are exposed through Option<T>. Use standard control flow to adapt:

use board/pico
use micro/dma

if pico.capabilities().dma_channels > 0 {
    if let Some(mut ch) = dma.channel(0) {
        ch.start_transfer()?
    }
}

Because the selected target and board are known at compile time, constant-folding removes dead branches for unsupported capabilities.

What Is Available

Required:

  • GPIO: digital I/O
  • UART: serial communication
  • ADC: analog-to-digital conversion
  • PWM: pulse-width modulation
  • I2C and SPI: bus protocols
  • DMA: direct memory access
  • PIO: RP2040 programmable IO state machines
  • micro/cpu: CPU hints (WFI, breakpoint, spin-loop)
  • micro/multicore: secondary core launch (RP2040)
  • USB device (CDC, HID, MSC)
  • XIP/SPI flash access
  • SD card over SPI
  • FAT filesystem
  • Interrupt registration