Using the standard library¶
The standard library lives in std/ and is written in ik itself. Each
module is a separate file you pull in with import.
Importing¶
Import a module by path, without the .ik extension:
import std/gpio
import std/delay
import std/uart
You can also import your own files the same way (the blink example imports a
local examples/demo_delay). Import resolution searches, in order:
the current working directory (
<path>.ik);the
IK8B_STD_PATHdirectory, if that environment variable is set;paths relative to the compiler executable, including the bundled
std/fallback forstd/*imports.
So import std/gpio works from anywhere as long as you run the shipped
./ik8b binary.
A UART “hello world”¶
target atmega328p
import std/uart
@main {
# 16 MHz, 9600 baud -> UBRR = 103. (UBRR = F_CPU / (16*baud) - 1.)
@uart_init(103)
loop * {
@uart_print_char('H')
@uart_print_char('i')
@uart_println() # sends CR + LF
}
}
Conventions across the library¶
A few rules hold throughout the standard library and are worth internalising:
No allocation. Nothing in the library uses a heap. When a function needs scratch space, you provide the buffer and pass a pointer. For example
@itoatakes aptr ram u8destination you must size yourself.Strings are caller-owned. The string functions operate on
str ram(NUL-terminated SRAM strings) andptr ram u8buffers. There are nostr flashvariants in those signatures.Peripherals depend on the target. Modules like
std/gpio,std/uart,std/spi, andstd/adcresolve their register addresses from thetargetyou selected, using compile-time? target == ...blocks. A function only makes sense on a device that actually has that peripheral.The clock is a project constant. Time-based modules (
std/delay) call a@cpu_mhz()function that you define once per project. See below.
Defining your clock for std/delay¶
The delay library cannot know your board’s clock, so it asks for it through a function you provide:
import std/delay
@cpu_mhz() -> u16 { return 16 } # define once; match your real clock
@main {
loop * {
@delay_ms(250)
}
}
If you call the underscore-prefixed primitives directly
(@_delay_ms), you pass the clock explicitly and do not need
@cpu_mhz.
Callbacks and streaming¶
Some modules take a function pointer so they can stream data without buffering. The font renderer is the canonical example — it walks text and hands each column byte to your callback:
import std/font
import std/spi
@to_display($b: u8) { @spi_transfer($b) }
@main {
@spi_init_master_raw()
@font_stream("Hello", &@to_display) # O(1) SRAM, any length text
}
The full per-module documentation, with every function signature and its behaviour, is in the Standard Library Reference.