Skip to content

Examples

Every example is a single portable main.cpp with zero preprocessor conditionals — CI compiles each one for every board. Copy any of them into a project's src/.

Example Shows
blink the minimal portable app — board::led.toggle()
uart_echo UART TX/RX, the debug console
hello blink + echo together (the alloy new scaffold)
pwm_fade PWM duty ramps, an LED breathing
analog_probe ADC sampling, printing raw counts
i2c_scan probing an I²C bus for devices
spi_loopback SPI transfers (MOSI→MISO looped)
irq_echo interrupt-driven UART RX
dma_probe ADC burst + PWM waveform streamed by DMA, zero CPU
pwm_fade, services the cooperative scheduler + control loop
async_blink, async_heartbeat two concurrent coroutine tasks, no heap, no RTOS
async_sensor a vendored driver read inside an async task
async_io co_await over SPI, I²C and DMA interrupts — the driver awaitables
net_probe, net_echo Ethernet (SAM E70 GMAC + PHY)
tcp_echo, udp_echo, dhcp_echo, http_server the TCP/IP stack
fs a filesystem on on-chip or external storage
watchdog the independent watchdog
nvm, rtc, dac, can on-chip flash key/value, RTC, DAC, CAN
bootloader_uart + ota_app the field-update pair: update over UART, trial boot, confirm

The examples ending in _read (i2c_read, spi_read, adc_read, dma_uart) are the ones CI asserts on under emulation — they print a value that only a correct driver sequence can produce. async_io is asserted the same way, on a number only a coroutine that actually suspended and was woken by an interrupt can print.

Running an example

The examples live in the framework tree under examples/. Build one for any board:

$ cd examples/dma_probe
$ alloy build --board nucleo_f722ze
$ alloy run --board nucleo_f722ze     # flash + monitor

A guided read: dma_probe

dma_probe is a good tour of the design — it uses capability checks inside generic lambdas so the same file adapts to whatever the board offers:

[&uart]<class Dma, class Adc = board::adc>(Dma*) {
    if constexpr (HasDma<Dma> && board::caps::adc) {
        auto adc  = Adc::open();
        auto chan = alloy::dma::channel<Dma, 1>::claim();
        std::uint16_t samples[32] = {};
        adc.read_burst(chan, board::adc_vref_channel,
                       std::span<std::uint16_t>{samples});   // 32 samples, no CPU
        // ...
    } else {
        uart.write("adc burst DMA: not on this board\r\n");
    }
}(static_cast<board::dma_t*>(nullptr));

On a board with a DMA controller and an ADC, this streams 32 conversions into RAM while the CPU does nothing else; on a board without them, the whole block compiles away and prints the honest fallback. No #ifdef in sight.