embedded_interfaces/registers/i2c/codecs/
standard_codec.rs

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