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 Peri, bind_interrupts,
9 i2c::{EventInterruptHandler, I2c as InnerI2c, SclPin, SdaPin, mode::Master},
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, Master>>>,
133 }
134
135 impl $peripheral {
136 #[expect(clippy::new_ret_no_self)]
139 #[must_use]
140 pub fn new(
141 sda_pin: Peri<'static, impl SdaPin<peripherals::$peripheral>>,
142 scl_pin: Peri<'static, impl SclPin<peripherals::$peripheral>>,
143 config: Config,
144 ) -> I2c {
145 let mut i2c_config = embassy_stm32::i2c::Config::default();
146 i2c_config.frequency = config.frequency.into();
147 i2c_config.sda_pullup = config.sda_pullup;
148 i2c_config.scl_pullup = config.scl_pullup;
149 i2c_config.timeout = ariel_os_embassy_common::i2c::controller::I2C_TIMEOUT;
150
151 bind_interrupts!(
152 struct Irqs {
153 $ev_interrupt => EventInterruptHandler<peripherals::$peripheral>;
154 $( $er_interrupt => embassy_stm32::i2c::ErrorInterruptHandler<peripherals::$peripheral>; )?
155 }
156 );
157
158 paste::paste! {
161 #[allow(dead_code)]
162 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
163 }
164
165 let twim_peripheral = unsafe { peripherals::$peripheral::steal() };
169
170 let i2c = InnerI2c::new_blocking(
171 twim_peripheral,
172 scl_pin,
173 sda_pin,
174 i2c_config,
175 );
176
177 I2c::$peripheral(Self { twim: YieldingAsync::new(BlockingAsync::new(i2c)) })
178 }
179 }
180 )*
181
182 pub enum I2c {
184 $(
185 #[doc = concat!(stringify!($peripheral), " peripheral.")]
186 $peripheral($peripheral),
187 )*
188 }
189
190 impl embedded_hal_async::i2c::ErrorType for I2c {
191 type Error = ariel_os_embassy_common::i2c::controller::Error;
192 }
193
194 impl_async_i2c_for_driver_enum!(I2c, $( $peripheral ),*);
195 }
196}
197
198fn from_error(err: embassy_stm32::i2c::Error) -> ariel_os_embassy_common::i2c::controller::Error {
200 use embassy_stm32::i2c::Error::{
201 Arbitration, Bus, Crc, Nack, Overrun, Timeout, ZeroLengthTransfer,
202 };
203
204 use ariel_os_embassy_common::i2c::controller::{Error, NoAcknowledgeSource};
205
206 match err {
207 Bus => Error::Bus,
208 Arbitration => Error::ArbitrationLoss,
209 Nack => Error::NoAcknowledge(NoAcknowledgeSource::Unknown),
210 Timeout => Error::Timeout,
211 Crc | ZeroLengthTransfer => Error::Other,
212 Overrun => Error::Overrun,
213 }
214}
215
216#[cfg(context = "stm32c031c6")]
218define_i2c_drivers!(
219 I2C1 => I2C1,
220);
221#[cfg(context = "stm32f042k6")]
222define_i2c_drivers!(
223 I2C1 => I2C1,
224);
225#[cfg(any(context = "stm32f401re", context = "stm32f411re"))]
226define_i2c_drivers!(
227 I2C1_EV + I2C1_ER => I2C1,
228 I2C2_EV + I2C2_ER => I2C2,
229 I2C3_EV + I2C3_ER => I2C3,
230);
231#[cfg(any(context = "stm32h755zi", context = "stm32h753zi"))]
232define_i2c_drivers!(
233 I2C1_EV + I2C1_ER => I2C1,
234 I2C2_EV + I2C2_ER => I2C2,
235 I2C3_EV + I2C3_ER => I2C3,
236 I2C4_EV + I2C4_ER => I2C4,
237);
238#[cfg(context = "stm32l475vg")]
239define_i2c_drivers!(
240 I2C1_EV + I2C1_ER => I2C1,
241 I2C2_EV + I2C2_ER => I2C2,
242 I2C3_EV + I2C3_ER => I2C3,
243);
244#[cfg(context = "stm32u585ai")]
245define_i2c_drivers!(
246 I2C1_EV + I2C1_ER => I2C1,
247 I2C2_EV + I2C2_ER => I2C2,
248 I2C3_EV + I2C3_ER => I2C3,
249 I2C4_EV + I2C4_ER => I2C4,
250);
251#[cfg(any(context = "stm32u073kc", context = "stm32u083mc"))]
252define_i2c_drivers!(
253 I2C1 => I2C1,
254 );
256#[cfg(context = "stm32wb55rg")]
257define_i2c_drivers!(
258 I2C1_EV + I2C1_ER => I2C1,
259 I2C3_EV + I2C3_ER => I2C3,
261);