i2c-slave
Make this MCU answer on the bus as a register-mapped I²C target (slave), using the register-file helper from <alp/i2c_regfile.h> — a pure layer over the portable alp_i2c_target_* surface in <alp/peripheral.h>.
Both were added in v0.9 and are marked [ABI-EXPERIMENTAL].
Source: examples/peripheral-io/i2c-slave/.
What it does
alp_init()— checked; the example bails out on failure.- Primes an 8-byte
g_regs[]array with0xA0..0xA7. alp_i2c_regfile_open(BOARD_I2C_SENSORS, 0x42, g_regs, 8, &rf)— claims the bus as a target at 7-bit address0x42and exposes the caller-owned buffer as a classic register file.alp_i2c_regfile_set_write_window(rf, 2, 6)— carves out read-only registers. Registers 0–1 become device-ID style: writes to them are dropped.- Five one-second ticks, each printing
alp_i2c_regfile_stats()counters (writes_seen/reads_seen) plus the first four register values. alp_i2c_regfile_close(rf)— unregister and release the bus.
The helper ships the state machine every target application used to hand-roll: the first written byte latches the register pointer, subsequent bytes store with wraparound auto-increment, reads serve from the pointer, and STOP re-arms the pointer latch.
When the register-file idiom doesn't fit
Command/response protocols and FIFOs don't map onto a register file. Drop down to the raw byte-granular callbacks — alp_i2c_target_open() with on_write / on_read / on_stop in <alp/peripheral.h>. The example's own git history shows this same register file hand-rolled on those callbacks.
board.yaml
som:
sku: E1M-AEN801
preset: e1m-evk
supported_boards:
- e1m-evk
- e1m-x-evk
pins:
- { e1m: E1M_I2C0, macro: EVK_I2C_BUS_SENSORS, doc: "Shared sensor + IO-expander + INA236 bus" }
cores:
m55_hp:
app: ./src
peripherals:
- i2c
diagnostics:
log_level: info
prj.conf adds one line that board.yaml derivation does not cover:
CONFIG_I2C_TARGET=y
That's an app-specific knob: it compiles the controller driver's target-mode hooks so alp_i2c_target_open can register this MCU as a bus target.
Availability
Target mode needs controller-driver support — on Zephyr that means CONFIG_I2C_TARGET plus a driver implementing target_register. The helper degrades exactly as alp_i2c_target_open does: backends or drivers without target mode fail the open with ALP_ERR_NOSUPPORT, which the example handles by printing a diagnostic and exiting.
Expected output
On native_sim the emulated controller accepts the registration, but nothing ever drives the emulated bus, so the ticks show writes_seen=0:
[i2c-slave] listening @ 0x42 on BOARD_I2C_SENSORS
[i2c-slave] tick 0 writes_seen=0 regs={0xa0,0xa1,0xa2,0xa3,...}
...
[i2c-slave] done
Test setup (real hardware)
- Flash this example onto board A.
- Drive it from board B — adapt
i2c-masterto point at0x42, or probe from a USB-I²C adapter.i2ctransfer -y 0 w1@0x42 0x00 r4should return0xa0 0xa1 0xa2 0xa3. - Wire SDA–SDA, SCL–SCL, GND–GND. Add external 4.7 kΩ pull-ups if neither board provides them.
- Board A's console shows
writes_seenincrementing. Remember registers 0–1 are read-only here, so writes to them are dropped.
Before flipping the target on, run i2c-scanner from board B to confirm 0x42 isn't already taken.
Status
Two twister rows, one per EVK route table, both on native_sim/native/64; the harness latches on [i2c-slave] done. The register-file behaviour itself is a bench signal — nothing drives the emulated bus in CI.
See also
<alp/i2c_regfile.h>reference — the register-file helper contract<alp/peripheral.h>reference — the rawalp_i2c_target_*callback contract underneathi2c-master— master-side known-address readi2c-scanner— master-side discoveryspi-slave— the SPI target-mode sibling- Examples overview