I2C and SPI
I2C
micro/i2c provides I2C bus master support for sensors, displays, and other peripherals.
Initialization
use micro/i2c
use micro/gpio
let mut bus = i2c.bus(0)
.sda(gpio.pin(4))
.scl(gpio.pin(5))
.freq(400_000) // 400 kHz fast mode
.init()?
Bus 0 and Bus 1 correspond to the hardware I2C peripherals. init() is the fallible step.
Reading and Writing
// Write to device address 0x48
bus.write(0x48, b"\x00")? // write register address
// Read 2 bytes from device
let mut buf: [u8; 2] = [0; 2]
bus.read(0x48, buf)? // read into buf
// Write then read (common for register reads)
bus.write_read(0x68, b"\x3B", buf)?
I2C transactions are fallible; the device may NACK or the bus may not acknowledge.
Common Pattern: Sensor Register Read
use micro/i2c
use micro/gpio
use std/time
fn read_temperature(mut bus: i2c.Bus) -> Result<i16, Error> {
// Write register address
bus.write(0x48, b"\x00")?
// Read 2 bytes
let mut buf: [u8; 2] = [0; 2]
bus.read(0x48, buf)?
// Combine bytes (big-endian)
let raw = i16.from(u16.from(buf[0]) << 8 | u16.from(buf[1]))
return Ok(raw >> 4) // TMP102 12-bit temperature
}
SPI
micro/spi provides SPI bus master support for displays, flash chips, SD cards, and RF modules.
Initialization
use micro/spi
use micro/gpio
let mut bus = spi.bus(0)
.mosi(gpio.pin(19))
.miso(gpio.pin(16))
.sck(gpio.pin(18))
.freq(10_000_000) // 10 MHz
.mode(spi.Mode.Mode0)
.init()?
// Chip select is managed manually or through a device handle
let mut cs = gpio.pin(17).into_output()
cs.high() // deselect by default
Reading and Writing
// Transfer: write and read simultaneously
let mut rx: [u8; 4] = [0; 4]
let tx: [u8; 4] = [0x03, 0x00, 0x00, 0x00]
cs.low() // select device
bus.transfer(tx, rx)? // send tx, receive into rx
cs.high() // deselect device
// Write only
cs.low()
bus.write(b"\x02\x00\x00\x00")?
cs.high()
// Read only (sends zeros)
cs.low()
bus.read(buf)?
cs.high()
Do and Don’t
// Do: handle I2C/SPI errors; bus transactions can fail
bus.write(addr, data)?
// Do: use write_read for register reads in one operation
bus.write_read(addr, &[reg], result_buf)?
// Do: drive CS manually and use defer for safe deselect
cs.low()
defer cs.high()
bus.transfer(tx, rx)?
// Avoid: leaving CS asserted if an error occurs
// The defer pattern above ensures cs.high() even on error paths
// Avoid: using I2C/SPI in ISRs; use DMA or buffered transfers instead