ariel_os_stm32/i2c/controller/
mod.rs1#![expect(unsafe_code)]
4
5use portable_atomic::{AtomicBool, Ordering};
6
7use ariel_os_embassy_common::{i2c::controller::Kilohertz, impl_async_i2c_for_driver_enum};
8use embassy_embedded_hal::adapter::{BlockingAsync, YieldingAsync};
9use embassy_stm32::{
10 bind_interrupts,
11 i2c::{EventInterruptHandler, I2c as InnerI2c, SclPin, SdaPin, mode::Master},
12 mode::Blocking,
13 peripherals,
14 time::Hertz,
15};
16
17#[non_exhaustive]
19#[derive(Clone)]
20pub struct Config {
21 pub frequency: Frequency,
23 pub sda_pullup: bool,
25 pub scl_pullup: bool,
27}
28
29impl Default for Config {
30 fn default() -> Self {
31 Self {
32 frequency: Frequency::UpTo100k(Kilohertz::kHz(100)),
33 sda_pullup: false,
34 scl_pullup: false,
35 }
36 }
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44#[repr(u32)]
45pub enum Frequency {
46 UpTo100k(Kilohertz), UpTo400k(Kilohertz), }
51
52#[doc(hidden)]
53impl Frequency {
54 #[must_use]
55 pub const fn first() -> Self {
56 Self::UpTo100k(Kilohertz::kHz(1))
57 }
58
59 #[must_use]
60 pub const fn last() -> Self {
61 Self::UpTo400k(Kilohertz::kHz(400))
62 }
63
64 #[must_use]
65 pub const fn next(self) -> Option<Self> {
66 match self {
67 Self::UpTo100k(f) => {
68 if f.to_kHz() < 100 {
69 Some(Self::UpTo100k(Kilohertz::kHz(f.to_kHz() + 1)))
71 } else {
72 Some(Self::UpTo400k(Kilohertz::kHz(self.khz() + 1)))
73 }
74 }
75 Self::UpTo400k(f) => {
76 if f.to_kHz() < 400 {
77 Some(Self::UpTo400k(Kilohertz::kHz(f.to_kHz() + 1)))
79 } else {
80 None
81 }
82 }
83 }
84 }
85
86 #[must_use]
87 pub const fn prev(self) -> Option<Self> {
88 match self {
89 Self::UpTo100k(f) => {
90 if f.to_kHz() > 1 {
91 Some(Self::UpTo100k(Kilohertz::kHz(f.to_kHz() - 1)))
93 } else {
94 None
95 }
96 }
97 Self::UpTo400k(f) => {
98 if f.to_kHz() > 100 + 1 {
99 Some(Self::UpTo400k(Kilohertz::kHz(f.to_kHz() - 1)))
101 } else {
102 Some(Self::UpTo100k(Kilohertz::kHz(self.khz() - 1)))
103 }
104 }
105 }
106 }
107
108 #[must_use]
109 pub const fn khz(self) -> u32 {
110 match self {
111 Self::UpTo100k(f) | Self::UpTo400k(f) => f.to_kHz(),
112 }
113 }
114}
115
116ariel_os_embassy_common::impl_i2c_from_frequency_up_to!();
117
118impl From<Frequency> for Hertz {
119 fn from(freq: Frequency) -> Self {
120 match freq {
121 Frequency::UpTo100k(f) | Frequency::UpTo400k(f) => Hertz::khz(f.to_kHz()),
122 }
123 }
124}
125
126macro_rules! define_i2c_drivers {
127 ($( $ev_interrupt:ident $( + $er_interrupt:ident )? => $peripheral:ident ),* $(,)?) => {
128 $(
129 pub struct $peripheral {
134 twim: YieldingAsync<BlockingAsync<InnerI2c<'static, Blocking, Master>>>,
135 }
136
137 paste::paste! {
139 static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
140 }
141
142 impl $peripheral {
143 #[expect(clippy::new_ret_no_self)]
146 #[must_use]
147 pub fn new<SDA: SdaPin<peripherals::$peripheral>, SCL: SclPin<peripherals::$peripheral>>(
148 sda_pin: impl $crate::IntoPeripheral<'static, SDA>,
149 scl_pin: impl $crate::IntoPeripheral<'static, SCL>,
150 config: Config,
151 ) -> I2c {
152 let mut i2c_config = embassy_stm32::i2c::Config::default();
153 i2c_config.frequency = config.frequency.into();
154 i2c_config.sda_pullup = config.sda_pullup;
155 i2c_config.scl_pullup = config.scl_pullup;
156 i2c_config.timeout = ariel_os_embassy_common::i2c::controller::I2C_TIMEOUT;
157
158 bind_interrupts!(
159 struct Irqs {
160 $ev_interrupt => EventInterruptHandler<peripherals::$peripheral>;
161 $( $er_interrupt => embassy_stm32::i2c::ErrorInterruptHandler<peripherals::$peripheral>; )?
162 }
163 );
164
165 paste::paste! {
168 #[allow(dead_code)]
169 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
170 }
171
172 paste::paste! {
174 if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
175 panic!("I2C peripheral already initialized")
176 }
177 }
178
179 let twim_peripheral = unsafe { peripherals::$peripheral::steal() };
183
184 let i2c = InnerI2c::new_blocking(
185 twim_peripheral,
186 scl_pin.into_hal_peripheral(),
187 sda_pin.into_hal_peripheral(),
188 i2c_config,
189 );
190
191 I2c::$peripheral(Self { twim: YieldingAsync::new(BlockingAsync::new(i2c)) })
192 }
193 }
194
195 impl Drop for $peripheral {
196 fn drop(&mut self) {
197 paste::paste! {
198 [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
199 }
200 }
201 }
202 )*
203
204 pub enum I2c {
206 $(
207 #[doc = concat!(stringify!($peripheral), " peripheral.")]
208 $peripheral($peripheral),
209 )*
210 }
211
212 impl embedded_hal_async::i2c::ErrorType for I2c {
213 type Error = ariel_os_embassy_common::i2c::controller::Error;
214 }
215
216 impl_async_i2c_for_driver_enum!(I2c, $( $peripheral ),*);
217 }
218}
219
220fn from_error(err: embassy_stm32::i2c::Error) -> ariel_os_embassy_common::i2c::controller::Error {
222 use embassy_stm32::i2c::Error::{
223 Arbitration, Bus, Crc, Nack, Overrun, Timeout, ZeroLengthTransfer,
224 };
225
226 use ariel_os_embassy_common::i2c::controller::{Error, NoAcknowledgeSource};
227
228 match err {
229 Bus => Error::Bus,
230 Arbitration => Error::ArbitrationLoss,
231 Nack => Error::NoAcknowledge(NoAcknowledgeSource::Unknown),
232 Timeout => Error::Timeout,
233 Crc | ZeroLengthTransfer => Error::Other,
234 Overrun => Error::Overrun,
235 }
236}
237
238#[cfg(context = "stm32c031c6")]
240define_i2c_drivers!(
241 I2C1 => I2C1,
242);
243#[cfg(context = "stm32f042k6")]
244define_i2c_drivers!(
245 I2C1 => I2C1,
246);
247#[cfg(context = "stm32f303cb")]
248define_i2c_drivers!(
249 I2C1_EV + I2C1_ER => I2C1,
250 I2C2_EV + I2C2_ER => I2C2,
251);
252#[cfg(context = "stm32f303re")]
253define_i2c_drivers!(
254 I2C1_EV + I2C1_ER => I2C1,
255 I2C2_EV + I2C2_ER => I2C2,
256 I2C3_EV + I2C3_ER => I2C3,
257);
258#[cfg(any(context = "stm32f401re", context = "stm32f411re"))]
259define_i2c_drivers!(
260 I2C1_EV + I2C1_ER => I2C1,
261 I2C2_EV + I2C2_ER => I2C2,
262 I2C3_EV + I2C3_ER => I2C3,
263);
264#[cfg(context = "stm32g431rb")]
265define_i2c_drivers!(
266 I2C1_EV + I2C1_ER => I2C1,
267 I2C2_EV + I2C2_ER => I2C2,
268 I2C3_EV + I2C3_ER => I2C3,
269);
270#[cfg(any(context = "stm32h755zi", context = "stm32h753zi"))]
271define_i2c_drivers!(
272 I2C1_EV + I2C1_ER => I2C1,
273 I2C2_EV + I2C2_ER => I2C2,
274 I2C3_EV + I2C3_ER => I2C3,
275 I2C4_EV + I2C4_ER => I2C4,
276);
277#[cfg(context = "stm32l475vg")]
278define_i2c_drivers!(
279 I2C1_EV + I2C1_ER => I2C1,
280 I2C2_EV + I2C2_ER => I2C2,
281 I2C3_EV + I2C3_ER => I2C3,
282);
283#[cfg(context = "stm32u585ai")]
284define_i2c_drivers!(
285 I2C1_EV + I2C1_ER => I2C1,
286 I2C2_EV + I2C2_ER => I2C2,
287 I2C3_EV + I2C3_ER => I2C3,
288 I2C4_EV + I2C4_ER => I2C4,
289);
290#[cfg(any(context = "stm32u073kc", context = "stm32u083mc"))]
291define_i2c_drivers!(
292 I2C1 => I2C1,
293 );
295#[cfg(context = "stm32wb55rg")]
296define_i2c_drivers!(
297 I2C1_EV + I2C1_ER => I2C1,
298 I2C3_EV + I2C3_ER => I2C3,
300);