Defines traits for embedded-registers-derive. For Derive Docs see embedded-registers-derive.
This crate provides a procedural macro for effortless definitions of registers in embedded device drivers. This is automatically generates functions to read/write the register over I2C and SPI, although it isn't limited to those buses. The resulting struct may be trivially extended to work with any other similar communication bus.
- Allows defintion of read-only, write-only and read-write registers
- Generates I2C and SPI read/write functions
- Registers are defined as bitfields via bondrewd.
- Only the accessed bitfield members are decoded, conserving memory and saving on CPU time.
- Supports both async and blocking operation modes simultaneously by generating two versions of each driver using maybe_async_cfg
This crate was made primarily for embedded-devices, which is a collection of drivers for a variety of different embedded sensors and devices.
Defining a register
Registers are defined simply by annotating a bondrewd struct with #[register(address = 0x42, mode = "rw")].
The necessary derive attribute for Bondrewd is added automatically.
Take for example this 2-byte read-write register at device addresses 0x42,0x43, which contains two u8 values:
use register;
This will create two structs called ValueRegister and ValueRegisterBitfield.
The first will only contain a byte array [u8; 2] to store the packed register contents,
and the latter will contain the unpacked actual members as defined above.
You will always interface with a device using the packed data, which can be transferred over the bus as-is.
The packed data contains methods to directly read/write the underlying storage array, which means you can only unpack what you need, saving resources.
I find it a bit misleading that the members written in
ValueRegisterend up inValueRegisterBitfield. So this might change in the future, but I currently cannot think of another design that is as simple to use as this one right now. The issue is that we need a struct for the packed data and one for the unpacked data. Since we usually deal with the packed data, and want to allow direct read/write operations on the packed data for performance, the naming gets confusing quite quickly.
Accessing a register (async)
To access such a register, you need to obtain an interface that implements the RegisterInterfaceAsync trait.
This crate already comes with an implementation of that trait for I2C and SPI devices called [i2c::I2cDeviceAsync] and [spi::SpiDeviceAsync] respectively.
You may then read the register simply by calling read_register or write_register on that interface.
#
# use register;
#
#
#
use ;
async