embedded_registers/i2c/
mod.rs

1pub mod codecs;
2
3use core::{any::TypeId, marker::PhantomData};
4
5use codecs::NoCodec;
6
7use crate::{ReadableRegister, WritableRegister};
8
9/// Represents a trait for I2C codecs. These are responsible to perform
10/// writes and reads to registers, given the register address and
11/// the raw data. Different devices can have different ways to encode
12/// the desired address, address size, continuous-read mode and more.
13#[maybe_async_cfg::maybe(
14    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), I2cBoundBus),
15    sync(feature = "sync"),
16    async(feature = "async")
17)]
18#[allow(async_fn_in_trait)]
19pub trait Codec: Default + 'static {
20    /// Read this register from the given I2C interface/device.
21    async fn read_register<R, I, A>(bound_bus: &mut I2cBoundBus<I, A>) -> Result<R, I::Error>
22    where
23        R: ReadableRegister,
24        I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
25        A: hal::i2c::AddressMode + Copy;
26
27    /// Write this register to the given I2C interface/device.
28    async fn write_register<R, I, A>(
29        bound_bus: &mut I2cBoundBus<I, A>,
30        register: impl AsRef<R>,
31    ) -> Result<(), I::Error>
32    where
33        R: WritableRegister,
34        I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
35        A: hal::i2c::AddressMode + Copy;
36}
37
38#[maybe_async_cfg::maybe(
39    idents(hal(sync = "embedded_hal", async = "embedded_hal_async")),
40    sync(feature = "sync"),
41    async(feature = "async")
42)]
43/// This represents a specific device bound to an I2C bus.
44pub struct I2cBoundBus<I, A>
45where
46    I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
47    A: hal::i2c::AddressMode + Copy,
48{
49    /// I2c interface
50    pub interface: I,
51    /// Device address
52    pub address: A,
53}
54
55#[maybe_async_cfg::maybe(
56    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), Codec, I2cBoundBus),
57    sync(feature = "sync"),
58    async(feature = "async")
59)]
60/// This represents an I2C device on an I2C bus, including
61/// a default codec.
62pub struct I2cDevice<I, A, C>
63where
64    I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
65    A: hal::i2c::AddressMode + Copy,
66    C: Codec,
67{
68    /// I2c interface and device address
69    pub bound_bus: I2cBoundBus<I, A>,
70    /// The default codec used to interface with registers
71    /// that don't explicitly specify a codec themselves.
72    /// Usually this is a simple codec specifying address size and some metadata.
73    /// See implementors of the Codec trait for more information on available codecs.
74    pub default_codec: PhantomData<C>,
75}
76
77#[maybe_async_cfg::maybe(
78    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), Codec, I2cBoundBus),
79    sync(feature = "sync"),
80    async(feature = "async")
81)]
82impl<I, A, C> I2cDevice<I, A, C>
83where
84    I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
85    A: hal::i2c::AddressMode + Copy,
86    C: Codec,
87{
88    /// Create a new I2cDevice from an interface and device address while specifying the default codec.
89    pub fn new(interface: I, address: A) -> Self {
90        Self {
91            bound_bus: I2cBoundBus { interface, address },
92            default_codec: Default::default(),
93        }
94    }
95}
96
97#[maybe_async_cfg::maybe(
98    idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), Codec, RegisterInterface),
99    sync(feature = "sync"),
100    async(feature = "async")
101)]
102impl<I, A, C> crate::RegisterInterface for I2cDevice<I, A, C>
103where
104    I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
105    A: hal::i2c::AddressMode + Copy,
106    C: Codec,
107{
108    type Error = I::Error;
109
110    /// Read this register from this spi device using the codec
111    /// specified by the register (if any) or otherwise the
112    /// default codec of the device.
113    #[inline]
114    async fn read_register<R>(&mut self) -> Result<R, I::Error>
115    where
116        R: ReadableRegister,
117    {
118        if TypeId::of::<R::I2cCodec>() == TypeId::of::<NoCodec>() {
119            C::read_register::<R, _, A>(&mut self.bound_bus).await
120        } else {
121            <R::I2cCodec as Codec>::read_register::<R, _, A>(&mut self.bound_bus).await
122        }
123    }
124
125    /// Write this register to this i2c device using the codec
126    /// specified by the register (if any) or otherwise the
127    /// default codec of the device.
128    #[inline]
129    async fn write_register<R>(&mut self, register: impl AsRef<R>) -> Result<(), I::Error>
130    where
131        R: WritableRegister,
132    {
133        if TypeId::of::<R::I2cCodec>() == TypeId::of::<NoCodec>() {
134            C::write_register(&mut self.bound_bus, register).await
135        } else {
136            <R::I2cCodec as Codec>::write_register(&mut self.bound_bus, register).await
137        }
138    }
139}