Communication Buses

Four buses come ready to use as board singletons: UART0 for serial, USB0 for the USB console, and I2C0/SPI0 for devices. I2C0.device(0x44) and SPI0.device(PA4) hand you a working handle to one device — an address, or a chip-select — with nothing else to configure. Constructing anything yourself only matters for non-default facts: a different baud, a bigger receive buffer, a faster bus.

start here

The board singletons

Every bus the board has is exported, ready to use — numbered by instance when the board has more than one: UART0 and UART1, I2C0 and I2C1, SPI0 and SPI1, USB0. A board without a bus doesn't export its name — using it is a build error at import, not a runtime surprise.

Example first

singletons.ts
import { UART0, I2C0, SPI0, PA4 } from '@typecad/hal';

UART0.writeLine('hello');                  // the controller, default 115200
const dev = I2C0.device(0x44);             // device at address 0x44
const id = dev.readReg(0xD0);
const flash = SPI0.device(PA4);            // device behind chip-select PA4
bus

I2C

Talk to one device at one address. I2C0.device(address) hands back a working handle — register verbs callable immediately. (The handle is an I2CTarget instance, a name you'll rarely need to type.)

Example first

i2c.ts
import { I2C0 } from '@typecad/hal';

const sensor = I2C0.device(0x44);

sensor.writeReg(0xF4, 0x27);            // one-byte register write
const id = sensor.readReg(0xD0);        // one-byte register read
sensor.updateReg(0xF5, 0x0F, 0x02);     // read-modify-write
sensor.write([0x2C, 0x06]);             // raw bytes

All methods

MemberDescription
I2C0.device(address)Handle to the device at a 7-bit address
new I2CTarget(I2C0, address, opts?)Explicit equivalent, only for a custom bus speed (hz)
writeReg(r, v)One-byte register write
readReg(r)One-byte register read
updateReg(r, mask, v)Read-modify-write
write(bytes)Raw bytes
bus

SPI

Talk to one device behind one chip-select. SPI0.device(cs, { hz, mode }) hands back a working handle, and the chip select is handled around every operation — there is no transaction API to get wrong.

Example first

spi.ts
import { SPI0, PA4 } from '@typecad/hal';

const display = SPI0.device(PA4, { hz: 10000000, mode: 0 });

const rx = new Uint8Array(4);
display.transceive([0x42, 0x00, 0x00, 0x00], rx);   // full duplex
display.write([0xAA, 0xBB]);                         // write only
const id = display.readReg(0x00);                    // one-byte register read

Multiple devices on one bus is the normal shape — one SPI0.device(...) per chip-select.

All methods

MemberDescription
SPI0.device(cs, opts?)Handle to the device behind one chip-select; hz and mode (0-3) set at creation
new SPITarget(SPI0, cs, opts?)Explicit equivalent
transceive(tx, rx?)Full-duplex transfer
write(tx)Write-only transfer
readReg(r)One-byte register read
bus

UART

A serial port. The UART0 / UART1 singletons work with default facts (115200); construct a UART only for a different baud or a bigger receive buffer.

Example first

uart.ts
import { UART, UART0 } from '@typecad/hal';

UART0.writeLine("hello");                        // default 115200 — no construction

const gps = new UART(UART0, { baud: 9600, rxBufferBytes: 128 });

gps.write("$PMTK220,1000*2F\r\n");   // no newline appended
gps.writeLine("hello");               // with a newline

if (gps.available() > 0) {            // bytes waiting
  const b = gps.read();               // pop the oldest byte (-1 when empty)
  const next = gps.peek();            // look without consuming
}

All methods

MemberDescription
UART0 / UART1 …Board singletons — ready to use, default 115200
new UART(UART0, opts?)Only for non-default facts: baud, rxBufferBytes (default 64)
write(v) / writeLine(v)Send — v is text, a number, or a boolean
available()Bytes waiting in the receive buffer
read() / peek()Pop the oldest byte / look without consuming (byte or -1)
bus

USB

The USB serial port the host sees as a COM/tty device. USB0 is ready to use; early output written before the host opens the port is dropped by most hosts, so gate it on linked() or waitLinked().

Example first

usb.ts
import { USB0 } from '@typecad/hal';

if (!USB0.linked()) {
  USB0.waitLinked(3000);    // bounded wait — returns once the host opens the port
}
USB0.writeLine('hello, host');

All methods

MemberDescription
USB0The USB serial port singleton, ready to use
write(v) / writeLine(v)Send — v is text, a number, or a boolean
linked() / waitLinked(ms)Host-opened check / bounded blocking wait
read() / available()Input from the host
© 2026 typeCAD — code-defined electronics github