std/uart — Serial USART

import std/uart

Blocking, polled access to the USART for asynchronous serial I/O. Each operation busy-waits on the relevant status flag, so there is no buffering and no interrupt handling in this module.

Configuring the baud rate

@uart_init takes the UBRR divisor value, not a baud rate. That is why the examples pass a number like 103 rather than 9600: 103 is the divisor that produces 9600 baud on a 16 MHz clock. Compute it from your clock and desired baud with:

\[\text{UBRR} = \frac{F_{CPU}}{16 \times \text{baud}} - 1\]

For 16 MHz at 9600 baud: 16000000 / (16 * 9600) - 1 = 103.

Common values (rounded to the nearest integer) for the two most common clocks:

Baud rate

UBRR at 8 MHz

UBRR at 16 MHz

2400

207

416

4800

103

207

9600

51

103

19200

25

51

38400

12

25

57600

8

16

115200

3

8

The further the real divisor is from a whole number, the larger the baud-rate error; the slower rates above are exact, while 57600/115200 carry a few percent error that most receivers still tolerate.

USART0

@uart_init($ubrr: u16)

Initialise USART0: set the baud divisor $ubrr and enable the transmitter and receiver in 8-N-1 frame format.

@uart_send($data: u8)

Transmit one byte, blocking until the transmit data register is empty first.

@uart_receive() -> u8

Block until a byte has been received, then return it.

@uart_available() -> u8

Return non-zero if a received byte is waiting to be read, 0 otherwise. Use this to poll without blocking in @uart_receive.

@uart_print_str($s: str ram)

Transmit a NUL-terminated string from RAM, one byte at a time, stopping at the terminating 0. Each byte is sent through @uart_send, so the call blocks until the whole string has been pushed out.

@uart_print_char($char: u8)

Convenience wrapper that transmits a single character $char.

@uart_println()

Send a carriage return (13) followed by a line feed (10) — a CRLF line terminator.

@_uart_wait_tx()

Internal: block until the transmit register is ready. Used by the public send routines.

Additional USART instances

Devices with more than one USART expose the same API on numbered modules. The uart1uart5 families mirror USART0 exactly, for use on parts that have those peripherals:

  • @uart1_init / @uart1_send / @uart1_receive / @uart1_available / @uart1_print_str / @uart1_print_char / @uart1_println

  • @uart2_*, @uart3_*, @uart4_*, @uart5_* — identical shape.

Only call into an instance that exists on your target.

Example

target atmega328p
import std/uart

@main {
    @uart_init(103)                # 9600 baud @ 16 MHz (UBRR = 103)
    @uart_print_str("ready\r\n")   # greet over the serial line
    loop * {
        ? @uart_available() != 0 {            # is a received byte waiting?
            ram imut $c: u8 = @uart_receive() # read it (blocks until one arrives)
            @uart_send($c)                    # echo the same byte back out
        }
    }
}