ariel_os_rp/i2c/controller/
mod.rs1#![expect(unsafe_code)]
4
5use ariel_os_embassy_common::{i2c::controller::Kilohertz, impl_async_i2c_for_driver_enum};
6use embassy_rp::{
7 Peripheral, bind_interrupts,
8 i2c::{InterruptHandler, SclPin, SdaPin},
9 peripherals,
10};
11
12const KHZ_TO_HZ: u32 = 1000;
13
14#[derive(Clone)]
18#[non_exhaustive]
19pub struct Config {
20 pub frequency: Frequency,
22}
23
24impl Default for Config {
25 fn default() -> Self {
26 Self {
27 frequency: Frequency::UpTo100k(Kilohertz::kHz(100)),
28 }
29 }
30}
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "defmt", derive(defmt::Format))]
37#[repr(u32)]
38pub enum Frequency {
39 UpTo100k(Kilohertz), UpTo400k(Kilohertz), }
44
45#[doc(hidden)]
46impl Frequency {
47 #[must_use]
48 pub const fn first() -> Self {
49 Self::UpTo100k(Kilohertz::kHz(1))
50 }
51
52 #[must_use]
53 pub const fn last() -> Self {
54 Self::UpTo400k(Kilohertz::kHz(400))
55 }
56
57 #[must_use]
58 pub const fn next(self) -> Option<Self> {
59 match self {
60 Self::UpTo100k(f) => {
61 if f.to_kHz() < 100 {
62 Some(Self::UpTo100k(Kilohertz::kHz(f.to_kHz() + 1)))
64 } else {
65 Some(Self::UpTo400k(Kilohertz::kHz(self.khz() + 1)))
66 }
67 }
68 Self::UpTo400k(f) => {
69 if f.to_kHz() < 400 {
70 Some(Self::UpTo400k(Kilohertz::kHz(f.to_kHz() + 1)))
72 } else {
73 None
74 }
75 }
76 }
77 }
78
79 #[must_use]
80 pub const fn prev(self) -> Option<Self> {
81 match self {
82 Self::UpTo100k(f) => {
83 if f.to_kHz() > 1 {
84 Some(Self::UpTo100k(Kilohertz::kHz(f.to_kHz() - 1)))
86 } else {
87 None
88 }
89 }
90 Self::UpTo400k(f) => {
91 if f.to_kHz() > 100 + 1 {
92 Some(Self::UpTo400k(Kilohertz::kHz(f.to_kHz() - 1)))
94 } else {
95 Some(Self::UpTo100k(Kilohertz::kHz(self.khz() - 1)))
96 }
97 }
98 }
99 }
100
101 #[must_use]
102 pub const fn khz(self) -> u32 {
103 match self {
104 Self::UpTo100k(f) | Self::UpTo400k(f) => f.to_kHz(),
105 }
106 }
107}
108
109ariel_os_embassy_common::impl_i2c_from_frequency_up_to!();
110
111macro_rules! define_i2c_drivers {
112 ($( $interrupt:ident => $peripheral:ident ),* $(,)?) => {
113 $(
114 pub struct $peripheral {
116 twim: embassy_rp::i2c::I2c<'static, peripherals::$peripheral, embassy_rp::i2c::Async>,
117 }
118
119 impl $peripheral {
120 #[expect(clippy::new_ret_no_self)]
123 #[must_use]
124 pub fn new(
125 sda_pin: impl Peripheral<P: SdaPin<peripherals::$peripheral>> + 'static,
126 scl_pin: impl Peripheral<P: SclPin<peripherals::$peripheral>> + 'static,
127 config: Config,
128 ) -> I2c {
129 paste::paste! {
132 #[allow(dead_code)]
133 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
134 }
135
136 let mut i2c_config = embassy_rp::i2c::Config::default();
137 i2c_config.frequency = config.frequency.khz() * KHZ_TO_HZ;
138
139 bind_interrupts!(
140 struct Irqs {
141 $interrupt => InterruptHandler<peripherals::$peripheral>;
142 }
143 );
144
145 let i2c_peripheral = unsafe { peripherals::$peripheral::steal() };
149
150 let i2c = embassy_rp::i2c::I2c::new_async(
153 i2c_peripheral,
154 scl_pin,
155 sda_pin,
156 Irqs,
157 i2c_config,
158 );
159
160 I2c::$peripheral(Self { twim: i2c })
161 }
162 }
163 )*
164
165 pub enum I2c {
167 $(
168 #[doc = concat!(stringify!($peripheral), " peripheral.")]
169 $peripheral($peripheral),
170 )*
171 }
172
173 impl embedded_hal_async::i2c::ErrorType for I2c {
174 type Error = ariel_os_embassy_common::i2c::controller::Error;
175 }
176
177 impl_async_i2c_for_driver_enum!(I2c, $( $peripheral ),*);
178 }
179}
180
181fn from_error(err: embassy_rp::i2c::Error) -> ariel_os_embassy_common::i2c::controller::Error {
183 #[allow(deprecated)]
184 use embassy_rp::i2c::{
185 AbortReason,
186 Error::{
187 Abort, AddressOutOfRange, AddressReserved, InvalidReadBufferLength,
188 InvalidWriteBufferLength,
189 },
190 };
191
192 use ariel_os_embassy_common::i2c::controller::{Error, NoAcknowledgeSource};
193
194 match err {
195 Abort(reason) => match reason {
196 AbortReason::NoAcknowledge => Error::NoAcknowledge(NoAcknowledgeSource::Unknown),
197 AbortReason::ArbitrationLoss => Error::ArbitrationLoss,
198 AbortReason::TxNotEmpty(_) | AbortReason::Other(_) => Error::Other,
199 },
200 #[allow(deprecated)]
201 AddressReserved(_)
202 | InvalidWriteBufferLength
203 | AddressOutOfRange(_)
204 | InvalidReadBufferLength => Error::Other,
205 }
206}
207
208define_i2c_drivers!(
210 I2C0_IRQ => I2C0,
211 I2C1_IRQ => I2C1,
212);