UART — pymcu.hal.uart#
from pymcu.hal.uart import UART
Serial communication over USART0. UART is an @inline class — it compiles to USART register
writes with zero SRAM allocation.
class UART#
UART(baud=9600, bits=8, parity=0, stop=1)#
Initializes the hardware UART peripheral. On AVR, configures USART0.
bits, parity and stop are the frame format. Parity is numbered the same on every
architecture: 0 none, 1 even, 2 odd. All three are compile-time constants, so an 8N1
UART emits the same single register write it always did, and a frame the part cannot send is
refused where the UART is constructed: a 9-bit frame keeps its ninth bit in a different
register and this HAL reads one register per byte, so it says so rather than sending eight.
They used to be nowhere at all, and every layer above accepted them from its caller and dropped them: a program that asked for 7E1 ran 8N1 and the frames went out wrong in silence.
Baud rate |
UBRR0 (16 MHz) |
|---|---|
9600 |
103 |
19200 |
51 |
38400 |
25 |
57600 |
16 |
115200 |
8 |
Methods#
Method |
Description |
|---|---|
|
Send a single byte (blocks until UDRE0 is set) |
|
Receive a byte (blocks until RXC0 is set) |
|
Non-blocking read; returns byte if available, else 0 |
|
Poll RXC until byte arrives and return it |
|
Direct UDR0 read for use inside |
|
Returns 1 if a byte is waiting in the hardware’s receive register |
|
Poll for a byte, giving up after |
|
Turn on the interrupt-driven receive ring: RXCIE, SEI and the ISR that fills it. A |
|
How many bytes are waiting in the ring (a count, where |
|
The ring’s capacity, 64 bytes, as a compile-time constant |
|
Read one byte from the ring, giving up after |
|
Send a flash string via LPM loop |
|
|
|
Print |
|
Read until |
|
Enable RXC interrupt (RXCIE0 in UCSR0B) |
|
ISR body: reads UDR0 into ring buffer |
Examples#
Hello, world#
from pymcu.hal.uart import UART
uart = UART(9600)
uart.println("hello")
Send and receive bytes#
from pymcu.hal.uart import UART
from pymcu.types import uint8
uart = UART(9600)
uart.write(65) # sends 'A'
b: uint8 = uart.read() # blocking receive
uart.write(b) # echo back
Echo loop#
from pymcu.hal.uart import UART
from pymcu.types import uint8
def main():
uart = UART(9600)
while True:
b: uint8 = uart.read()
uart.write(b)
Non-blocking receive with walrus#
from pymcu.hal.uart import UART
from pymcu.types import uint8
def main():
uart = UART(9600)
while True:
if c := uart.read_nb(): # walrus: assign and test in one
uart.write(c)
Interrupt-driven ring buffer#
from pymcu.hal.uart import UART
from pymcu.types import uint8
uart = UART(9600)
@interrupt(0x0018) # USART0 RX Complete (ATmega328P)
def on_rx():
uart.rx_isr() # stores byte in ring buffer
def main():
uart.enable_rx_interrupt()
while True:
if uart.available():
b: uint8 = uart.read_nb()
uart.write(b)
Read a line of text#
from pymcu.hal.uart import UART
from pymcu.types import uint8
uart = UART(9600)
buf: uint8[64] = [0] * 64
def main():
uart.println("send a line:")
while True:
n: uint8 = uart.read_line(buf, 64) # blocks until '\n' or 64 bytes
for i in range(n):
uart.write(buf[i]) # echo line back
read_line reads bytes until a \n (0x0A) is received or max_len bytes have been
stored — whichever comes first. The newline is not stored in the buffer. Returns the
number of bytes written.
Receiving with a deadline#
A read that never returns is not a timeout. read_timeout and rx_read_timeout poll for a
byte and give up:
from pymcu.hal.uart import UART
from pymcu.types import int16
u = UART(9600)
b: int16 = u.read_timeout(500) # half a second
if b < 0:
u.write_str("no answer\n")
else:
u.write(b & 0xFF)
The inner loop runs 100 times per millisecond and spends 9 us of calibrated delay in each pass. Measured in avr8sharp at 16 MHz, a 100 ms timeout with nothing arriving takes 1 581 950 cycles, which is 98.9 ms: 1.1 % short. That is the accuracy on offer.
Buffered receive#
Polled, the UART holds one byte and available() is a flag. start_buffered_rx() turns on
the receive interrupt and the 64-byte ring behind it, and rx_count() becomes a real count:
u = UART(115200)
u.start_buffered_rx()
while u.rx_count() < 4:
pass
The ring is a fixed array allocated at compile time and costs nothing in a program that never turns it on: a UART program that only writes is the same size with the ring as without, because nothing references the array and it is eliminated. A byte that arrives with the ring full is dropped.