embedded_registers/i2c/codecs/
simple_codec.rs

1use crate::{ReadableRegister, WritableRegister};
2use bytemuck::Zeroable;
3
4/// This codec represents the most commonly found codecs for I2C devices.
5/// The main variable is the size of register addresses in bytes.
6///
7/// This codec has no information over register sizes or the existence of
8/// read/write address-auto-increment which some devices support.
9/// It will always send one header and then receive/send the associated register data,
10/// so it's compatible with auto-increment usage, but cannot be used to read or write
11/// registers that require interspersing the address between bytes.
12///
13/// Devices often use a mixed mode, where some registers allow auto-increment while others
14/// don't, or where the address is directly associated with a specific, but varying, register size.
15/// Therefore, it is up to the user to make sure that accessing a register via this codec
16/// is supported by the hardware.
17///
18/// The following generic parameters are available:
19///
20/// | Parameter | Type | Description |
21/// |---|---|---|
22/// | `HEADER_SIZE` | `usize` | The size of the command header (register address) in bytes |
23#[derive(Default)]
24pub struct SimpleCodec<const HEADER_SIZE: usize> {}
25
26#[maybe_async_cfg::maybe(
27    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), Codec, I2cBoundBus),
28    sync(feature = "sync"),
29    async(feature = "async"),
30    keep_self
31)]
32impl<const HEADER_SIZE: usize> crate::i2c::Codec for SimpleCodec<HEADER_SIZE> {
33    #[inline]
34    async fn read_register<R, I, A>(bound_bus: &mut crate::i2c::I2cBoundBus<I, A>) -> Result<R, I::Error>
35    where
36        R: ReadableRegister,
37        I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
38        A: hal::i2c::AddressMode + Copy,
39    {
40        let header = &R::ADDRESS.to_be_bytes()[core::mem::size_of_val(&R::ADDRESS) - HEADER_SIZE..];
41        let mut register = R::zeroed();
42
43        bound_bus
44            .interface
45            .write_read(bound_bus.address, header, register.data_mut())
46            .await?;
47        Ok(register)
48    }
49
50    #[inline]
51    async fn write_register<R, I, A>(
52        bound_bus: &mut crate::i2c::I2cBoundBus<I, A>,
53        register: impl AsRef<R>,
54    ) -> Result<(), I::Error>
55    where
56        R: WritableRegister,
57        I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
58        A: hal::i2c::AddressMode + Copy,
59    {
60        #[repr(C, packed(1))]
61        #[derive(Copy, Clone, bytemuck::Pod, Zeroable)]
62        struct Buffer<const HEADER_SIZE: usize, R> {
63            header: [u8; HEADER_SIZE],
64            register: R,
65        }
66
67        let mut buffer = Buffer::<{ HEADER_SIZE }, R> {
68            header: R::ADDRESS.to_be_bytes()[core::mem::size_of_val(&R::ADDRESS) - HEADER_SIZE..]
69                .try_into()
70                .expect("Unexpected compile-time header address size. This is a bug in the chosen Codec or embedded-registers."),
71            register: *register.as_ref(),
72        };
73
74        let data = bytemuck::bytes_of_mut(&mut buffer);
75        bound_bus.interface.write(bound_bus.address, data).await
76    }
77}