ariel_os_esp/i2c/controller/
mod.rs1#![expect(unsafe_code)]
4
5use ariel_os_embassy_common::impl_async_i2c_for_driver_enum;
6use esp_hal::{
7 Async,
8 gpio::interconnect::PeripheralOutput,
9 i2c::master::{BusTimeout, I2c as EspI2c},
10 peripherals,
11};
12
13#[non_exhaustive]
15#[derive(Clone)]
16pub struct Config {
17 pub frequency: Frequency,
19}
20
21impl Default for Config {
22 fn default() -> Self {
23 Self {
24 frequency: Frequency::_100k,
25 }
26 }
27}
28
29#[derive(Debug, Copy, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33pub enum Frequency {
34 _100k,
36 _400k,
38}
39
40#[doc(hidden)]
41impl Frequency {
42 #[must_use]
43 pub const fn first() -> Self {
44 Self::_100k
45 }
46
47 #[must_use]
48 pub const fn last() -> Self {
49 Self::_400k
50 }
51
52 #[must_use]
53 pub const fn next(self) -> Option<Self> {
54 match self {
55 Self::_100k => Some(Self::_400k),
56 Self::_400k => None,
57 }
58 }
59
60 #[must_use]
61 pub const fn prev(self) -> Option<Self> {
62 match self {
63 Self::_100k => None,
64 Self::_400k => Some(Self::_100k),
65 }
66 }
67
68 #[must_use]
69 pub const fn khz(self) -> u32 {
70 match self {
71 Self::_100k => 100,
72 Self::_400k => 400,
73 }
74 }
75}
76
77ariel_os_embassy_common::impl_i2c_from_frequency!();
78
79impl From<Frequency> for esp_hal::time::Rate {
80 fn from(freq: Frequency) -> Self {
81 match freq {
82 Frequency::_100k => esp_hal::time::Rate::from_khz(100),
83 Frequency::_400k => esp_hal::time::Rate::from_khz(400),
84 }
85 }
86}
87
88macro_rules! define_i2c_drivers {
89 ($( $peripheral:ident ),* $(,)?) => {
90 $(
91 pub struct $peripheral {
93 twim: EspI2c<'static, Async>,
94 }
95
96 impl $peripheral {
97 #[expect(clippy::new_ret_no_self)]
100 #[must_use]
101 pub fn new<SDA: PeripheralOutput<'static>, SCL: PeripheralOutput<'static>>(
102 sda_pin: impl $crate::IntoPeripheral<'static, SDA>,
103 scl_pin: impl $crate::IntoPeripheral<'static, SCL>,
104 config: Config,
105 ) -> I2c {
106 paste::paste! {
109 #[allow(dead_code)]
110 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
111 }
112
113 let twim_config = esp_hal::i2c::master::Config::default()
114 .with_frequency(config.frequency.into())
115 .with_timeout(
117 #[cfg(any(context = "esp32c3", context = "esp32c6", context = "esp32s2", context = "esp32s3"))]
118 BusTimeout::Disabled,
119 #[cfg(context = "esp32")]
121 BusTimeout::Maximum
122 );
123
124 let i2c_peripheral = unsafe { peripherals::$peripheral::steal() };
128
129 let twim = EspI2c::new(
130 i2c_peripheral,
131 twim_config,
132 )
133 .unwrap()
134 .into_async()
135 .with_sda(sda_pin.into_hal_peripheral())
136 .with_scl(scl_pin.into_hal_peripheral());
137
138 I2c::$peripheral(Self { twim })
139 }
140 }
141 )*
142
143 pub enum I2c {
145 $(
146 #[doc = concat!(stringify!($peripheral), " peripheral.")]
147 $peripheral($peripheral),
148 )*
149 }
150
151 impl embedded_hal_async::i2c::ErrorType for I2c {
152 type Error = ariel_os_embassy_common::i2c::controller::Error;
153 }
154
155 impl_async_i2c_for_driver_enum!(I2c, $( $peripheral ),*);
156 }
157}
158
159fn from_error(err: esp_hal::i2c::master::Error) -> ariel_os_embassy_common::i2c::controller::Error {
161 use esp_hal::i2c::master::{AcknowledgeCheckFailedReason, Error as EspError};
162
163 use ariel_os_embassy_common::i2c::controller::{Error, NoAcknowledgeSource};
164
165 #[expect(clippy::match_same_arms, reason = "non-exhaustive upstream enum")]
166 match err {
167 EspError::FifoExceeded => Error::Overrun,
168 EspError::AcknowledgeCheckFailed(reason) => {
169 let reason = match reason {
170 AcknowledgeCheckFailedReason::Address => NoAcknowledgeSource::Address,
171 AcknowledgeCheckFailedReason::Data => NoAcknowledgeSource::Data,
172 AcknowledgeCheckFailedReason::Unknown | _ => NoAcknowledgeSource::Unknown,
173 };
174 Error::NoAcknowledge(reason)
175 }
176 EspError::Timeout => Error::Timeout,
177 EspError::ArbitrationLost => Error::ArbitrationLoss,
178 EspError::ExecutionIncomplete
179 | EspError::CommandNumberExceeded
180 | EspError::ZeroLengthInvalid => Error::Other,
181 _ => Error::Other,
182 }
183}
184
185#[cfg(context = "esp32")]
187define_i2c_drivers!(I2C0, I2C1);
188#[cfg(context = "esp32c3")]
189define_i2c_drivers!(I2C0);
190#[cfg(context = "esp32c6")]
191define_i2c_drivers!(I2C0);
192#[cfg(context = "esp32s2")]
193define_i2c_drivers!(I2C0, I2C1);
194#[cfg(context = "esp32s3")]
195define_i2c_drivers!(I2C0, I2C1);