ariel_os_stm32/i2c/controller/
mod.rs1#![expect(unsafe_code)]
4
5use ariel_os_embassy_common::{i2c::controller::Kilohertz, impl_async_i2c_for_driver_enum};
6use embassy_embedded_hal::adapter::{BlockingAsync, YieldingAsync};
7use embassy_stm32::{
8 Peripheral, bind_interrupts,
9 i2c::{EventInterruptHandler, I2c as InnerI2c, SclPin, SdaPin},
10 mode::Blocking,
11 peripherals,
12 time::Hertz,
13};
14
15#[non_exhaustive]
17#[derive(Clone)]
18pub struct Config {
19 pub frequency: Frequency,
21 pub sda_pullup: bool,
23 pub scl_pullup: bool,
25}
26
27impl Default for Config {
28 fn default() -> Self {
29 Self {
30 frequency: Frequency::UpTo100k(Kilohertz::kHz(100)),
31 sda_pullup: false,
32 scl_pullup: false,
33 }
34 }
35}
36
37#[derive(Debug, Copy, Clone, PartialEq, Eq)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42#[repr(u32)]
43pub enum Frequency {
44 UpTo100k(Kilohertz), UpTo400k(Kilohertz), }
49
50#[doc(hidden)]
51impl Frequency {
52 #[must_use]
53 pub const fn first() -> Self {
54 Self::UpTo100k(Kilohertz::kHz(1))
55 }
56
57 #[must_use]
58 pub const fn last() -> Self {
59 Self::UpTo400k(Kilohertz::kHz(400))
60 }
61
62 #[must_use]
63 pub const fn next(self) -> Option<Self> {
64 match self {
65 Self::UpTo100k(f) => {
66 if f.to_kHz() < 100 {
67 Some(Self::UpTo100k(Kilohertz::kHz(f.to_kHz() + 1)))
69 } else {
70 Some(Self::UpTo400k(Kilohertz::kHz(self.khz() + 1)))
71 }
72 }
73 Self::UpTo400k(f) => {
74 if f.to_kHz() < 400 {
75 Some(Self::UpTo400k(Kilohertz::kHz(f.to_kHz() + 1)))
77 } else {
78 None
79 }
80 }
81 }
82 }
83
84 #[must_use]
85 pub const fn prev(self) -> Option<Self> {
86 match self {
87 Self::UpTo100k(f) => {
88 if f.to_kHz() > 1 {
89 Some(Self::UpTo100k(Kilohertz::kHz(f.to_kHz() - 1)))
91 } else {
92 None
93 }
94 }
95 Self::UpTo400k(f) => {
96 if f.to_kHz() > 100 + 1 {
97 Some(Self::UpTo400k(Kilohertz::kHz(f.to_kHz() - 1)))
99 } else {
100 Some(Self::UpTo100k(Kilohertz::kHz(self.khz() - 1)))
101 }
102 }
103 }
104 }
105
106 #[must_use]
107 pub const fn khz(self) -> u32 {
108 match self {
109 Self::UpTo100k(f) | Self::UpTo400k(f) => f.to_kHz(),
110 }
111 }
112}
113
114ariel_os_embassy_common::impl_i2c_from_frequency_up_to!();
115
116impl From<Frequency> for Hertz {
117 fn from(freq: Frequency) -> Self {
118 match freq {
119 Frequency::UpTo100k(f) | Frequency::UpTo400k(f) => Hertz::khz(f.to_kHz()),
120 }
121 }
122}
123
124macro_rules! define_i2c_drivers {
125 ($( $ev_interrupt:ident $( + $er_interrupt:ident )? => $peripheral:ident ),* $(,)?) => {
126 $(
127 pub struct $peripheral {
132 twim: YieldingAsync<BlockingAsync<InnerI2c<'static, Blocking>>>,
133 }
134
135 impl $peripheral {
136 #[expect(clippy::new_ret_no_self)]
139 #[must_use]
140 pub fn new(
141 sda_pin: impl Peripheral<P: SdaPin<peripherals::$peripheral>> + 'static,
142 scl_pin: impl Peripheral<P: SclPin<peripherals::$peripheral>> + 'static,
143 config: Config,
144 ) -> I2c {
145 let mut i2c_config = embassy_stm32::i2c::Config::default();
146 i2c_config.sda_pullup = config.sda_pullup;
147 i2c_config.scl_pullup = config.scl_pullup;
148 i2c_config.timeout = ariel_os_embassy_common::i2c::controller::I2C_TIMEOUT;
149
150 bind_interrupts!(
151 struct Irqs {
152 $ev_interrupt => EventInterruptHandler<peripherals::$peripheral>;
153 $( $er_interrupt => embassy_stm32::i2c::ErrorInterruptHandler<peripherals::$peripheral>; )?
154 }
155 );
156
157 paste::paste! {
160 #[allow(dead_code)]
161 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
162 }
163
164 let twim_peripheral = unsafe { peripherals::$peripheral::steal() };
168
169 let frequency = config.frequency;
170 let i2c = InnerI2c::new_blocking(
171 twim_peripheral,
172 scl_pin,
173 sda_pin,
174 frequency.into(),
175 i2c_config,
176 );
177
178 I2c::$peripheral(Self { twim: YieldingAsync::new(BlockingAsync::new(i2c)) })
179 }
180 }
181 )*
182
183 pub enum I2c {
185 $(
186 #[doc = concat!(stringify!($peripheral), " peripheral.")]
187 $peripheral($peripheral),
188 )*
189 }
190
191 impl embedded_hal_async::i2c::ErrorType for I2c {
192 type Error = ariel_os_embassy_common::i2c::controller::Error;
193 }
194
195 impl_async_i2c_for_driver_enum!(I2c, $( $peripheral ),*);
196 }
197}
198
199fn from_error(err: embassy_stm32::i2c::Error) -> ariel_os_embassy_common::i2c::controller::Error {
201 use embassy_stm32::i2c::Error::{
202 Arbitration, Bus, Crc, Nack, Overrun, Timeout, ZeroLengthTransfer,
203 };
204
205 use ariel_os_embassy_common::i2c::controller::{Error, NoAcknowledgeSource};
206
207 match err {
208 Bus => Error::Bus,
209 Arbitration => Error::ArbitrationLoss,
210 Nack => Error::NoAcknowledge(NoAcknowledgeSource::Unknown),
211 Timeout => Error::Timeout,
212 Crc | ZeroLengthTransfer => Error::Other,
213 Overrun => Error::Overrun,
214 }
215}
216
217#[cfg(context = "stm32c031c6")]
219define_i2c_drivers!(
220 I2C1 => I2C1,
221);
222#[cfg(context = "stm32f042k6")]
223define_i2c_drivers!(
224 I2C1 => I2C1,
225);
226#[cfg(any(context = "stm32f401re", context = "stm32f411re"))]
227define_i2c_drivers!(
228 I2C1_EV + I2C1_ER => I2C1,
229 I2C2_EV + I2C2_ER => I2C2,
230 I2C3_EV + I2C3_ER => I2C3,
231);
232#[cfg(any(context = "stm32h755zi", context = "stm32h753zi"))]
233define_i2c_drivers!(
234 I2C1_EV + I2C1_ER => I2C1,
235 I2C2_EV + I2C2_ER => I2C2,
236 I2C3_EV + I2C3_ER => I2C3,
237 I2C4_EV + I2C4_ER => I2C4,
238);
239#[cfg(context = "stm32l475vg")]
240define_i2c_drivers!(
241 I2C1_EV + I2C1_ER => I2C1,
242 I2C2_EV + I2C2_ER => I2C2,
243 I2C3_EV + I2C3_ER => I2C3,
244);
245#[cfg(context = "stm32u585ai")]
246define_i2c_drivers!(
247 I2C1_EV + I2C1_ER => I2C1,
248 I2C2_EV + I2C2_ER => I2C2,
249 I2C3_EV + I2C3_ER => I2C3,
250 I2C4_EV + I2C4_ER => I2C4,
251);
252#[cfg(any(context = "stm32u073kc", context = "stm32u083mc"))]
253define_i2c_drivers!(
254 I2C1 => I2C1,
255 );
257#[cfg(context = "stm32wb55rg")]
258define_i2c_drivers!(
259 I2C1_EV + I2C1_ER => I2C1,
260 I2C3_EV + I2C3_ER => I2C3,
262);