1#![no_std]
2use core::iter::once;
44
45use either::Either::{Left, Right};
46use fugit::HertzU32;
47use heapless::Deque;
48use i2c_cmd::{restart, start, CmdWord, Data};
49use pio::Instruction;
50use rp2040_hal::{
51 gpio::{
52 AnyPin, Function, FunctionNull, OutputEnableOverride, Pin, PinId, PullType, PullUp,
53 ValidFunction,
54 },
55 pio::{
56 PIOExt, PinDir, PinState, Rx, ShiftDirection, StateMachine, StateMachineIndex, Tx,
57 UninitStateMachine, PIO,
58 },
59};
60
61use crate::i2c_cmd::stop;
62
63mod eh0_2;
64mod eh1_0;
65mod i2c_cmd;
66
67#[derive(Debug, PartialEq, Eq)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
70pub enum AddressLength {
71 _7,
72 _10,
73}
74pub trait ValidAddressMode:
75 Copy + Into<u16> + embedded_hal::i2c::AddressMode + embedded_hal_0_2::blocking::i2c::AddressMode
76{
77 fn address_len() -> AddressLength;
78}
79macro_rules! impl_valid_addr {
80 ($t:path => $e:expr) => {
81 impl ValidAddressMode for $t {
82 fn address_len() -> AddressLength {
83 $e
84 }
85 }
86 };
87}
88impl_valid_addr!(u8 => AddressLength::_7);
89impl_valid_addr!(u16 => AddressLength::_10);
90fn setup<'b, A: ValidAddressMode>(
97 address: A,
98 read: bool,
99 do_restart: bool,
100) -> impl Iterator<Item = CmdWord<'b>> {
101 let read_flag = if read { 1 } else { 0 };
102 let address: u16 = address.into();
103 let address = match A::address_len() {
104 AddressLength::_7 => {
105 let address_and_flag = ((address as u8) << 1) | read_flag;
106 Left(once(address_and_flag).map(CmdWord::address))
107 }
108 AddressLength::_10 => {
109 let addr_hi = 0xF0 | ((address >> 7) as u8) & 0xFE;
110 let addr_lo = (address & 0xFF) as u8;
111
112 Right(if read {
113 let full_addr = [addr_hi, addr_lo]
114 .into_iter()
115 .map(Data::address)
116 .map(CmdWord::Data);
117 let read_addr =
118 restart().chain(once(CmdWord::Data(Data::address(addr_hi | read_flag))));
119
120 Left(full_addr.chain(read_addr))
121 } else {
122 Right(
123 [addr_hi | read_flag, addr_lo]
124 .into_iter()
125 .map(Data::address)
126 .map(CmdWord::Data),
127 )
128 })
129 }
130 };
131
132 if do_restart {
133 Left(restart())
134 } else {
135 Right(start())
136 }
137 .chain(address)
138}
139
140#[derive(Debug, PartialEq, Eq)]
141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142pub enum Error {
143 NoAcknowledgeAddress,
144 NoAcknowledgeData,
145 BusContention,
146}
147
148pub struct I2C<'pio, P, SMI, SDA, SCL>
150where
151 P: PIOExt,
152 SMI: StateMachineIndex,
153 SDA: AnyPin,
154 SCL: AnyPin,
155{
156 pio: &'pio mut PIO<P>,
157 sm: StateMachine<(P, SMI), rp2040_hal::pio::Running>,
158 tx: Tx<(P, SMI)>,
159 rx: Rx<(P, SMI)>,
160 sda: (Pin<SDA::Id, P::PinFunction, PullUp>, OutputEnableOverride),
161 scl: (Pin<SCL::Id, P::PinFunction, PullUp>, OutputEnableOverride),
162}
163
164impl<'pio, P, SMI, SDA, SCL> I2C<'pio, P, SMI, SDA, SCL>
165where
166 P: PIOExt,
167 SMI: StateMachineIndex,
168 SDA: AnyPin,
169 SCL: AnyPin,
170{
171 pub fn new(
175 pio: &'pio mut PIO<P>,
176 sda: SDA,
177 scl: SCL,
178 sm: UninitStateMachine<(P, SMI)>,
179 bus_freq: HertzU32,
180 clock_freq: HertzU32,
181 ) -> Self
182 where
183 SDA: AnyPin<Function = FunctionNull>,
184 SDA::Id: ValidFunction<P::PinFunction>,
185 SCL: AnyPin<Function = FunctionNull>,
186 SCL::Id: ValidFunction<P::PinFunction>,
187 {
188 let (sda, scl): (SDA::Type, SCL::Type) = (sda.into(), scl.into());
189
190 let mut program = pio_proc::pio_asm!(
191 ".side_set 1 opt pindirs"
192
193 "byte_nack:"
194 " jmp y-- byte_end ; continue if NAK was expected"
195 " irq wait 0 rel ; otherwise stop, ask for help (raises the irq line (0+SMI::id())%4)"
196 " jmp byte_end ; resumed, finalize the current byte"
197
198 "byte_send:"
199 " out y 1 ; Unpack FINAL"
200 " set x 7 ; loop 8 times"
201
202 "bitloop:"
203 " out pindirs 1 [7] ; Serialize write data (all-ones is reading)"
204 " nop side 1 [2] ; SCL rising edge"
205 " wait 1 gpio 0 [4] ; Allow clock to be stretched"
207 " in pins 1 [7] ; Sample read data in middle of SCL pulse"
208 " jmp x-- bitloop side 0 [7] ; SCL falling edge"
209
210 " out pindirs 1 [7] ; On reads, we provide the ACK"
212 " nop side 1 [7] ; SCL rising edge"
213 " wait 1 gpio 0 [7] ; Allow clock to be stretched"
215 " jmp pin byte_nack side 0 [2] ; Test SDA for ACK/NACK, fall through if ACK"
216
217 "byte_end:"
218 " push block ; flush the current byte in isr to the FIFO"
219
220 ".wrap_target"
221 " out x 6 ; Unpack Instr count"
222 " jmp !x byte_send ; Instr == 0, this is a data record"
223 " out null 10 ; Instr > 0, remainder of this OSR is invalid"
224
225 "do_exec:"
226 " out exec 16 ; Execute one instruction per FIFO word"
227 " jmp x-- do_exec"
228 ".wrap"
229 )
230 .program;
231 program.code[7] |= u16::from(scl.id().num);
233 program.code[12] |= u16::from(scl.id().num);
234
235 let installed = pio.install(&program).unwrap();
237 let wrap_target = installed.wrap_target();
238
239 let bit_freq = 32 * bus_freq;
241 let mut int = clock_freq / bit_freq;
242 let rem = clock_freq - (int * bit_freq);
243 let frac = (rem * 256) / bit_freq;
244
245 assert!(
246 (1..=65536).contains(&int) && (int != 65536 || frac == 0),
247 "The ratio between the bus frequency and the system clock must be within [1.0, 65536.0]."
248 );
249
250 if int == 65536 {
252 int = 0;
253 }
254 let int: u16 = int as u16;
256 let frac: u8 = frac as u8;
257
258 let (mut sm, rx, tx) = rp2040_hal::pio::PIOBuilder::from_installed_program(installed)
260 .buffers(rp2040_hal::pio::Buffers::RxTx)
262 .set_pins(sda.id().num, 1)
264 .out_pins(sda.id().num, 1)
265 .in_pin_base(sda.id().num)
266 .side_set_pin_base(scl.id().num)
267 .jmp_pin(sda.id().num)
268 .out_shift_direction(ShiftDirection::Left)
270 .autopull(true)
271 .pull_threshold(16)
272 .in_shift_direction(ShiftDirection::Left)
274 .push_threshold(8)
275 .clock_divisor_fixed_point(int, frac)
277 .build(sm);
278
279 let sda: Pin<_, _, PullUp> = sda.into_pull_type();
281 let scl: Pin<_, _, PullUp> = scl.into_pull_type();
282 let sda_override = sda.get_output_enable_override();
283 let scl_override = scl.get_output_enable_override();
284
285 sm.set_pins([
287 (scl.id().num, PinState::High),
288 (sda.id().num, PinState::High),
289 ]);
290 sm.set_pindirs([
291 (scl.id().num, PinDir::Output),
292 (sda.id().num, PinDir::Output),
293 ]);
294
295 let mut sda: Pin<SDA::Id, P::PinFunction, PullUp> = sda.into_function();
297 sda.set_output_enable_override(OutputEnableOverride::Invert);
299
300 let mut scl: Pin<SCL::Id, P::PinFunction, PullUp> = scl.into_function();
302 scl.set_output_enable_override(OutputEnableOverride::Invert);
304
305 sm.set_pins([(sda.id().num, PinState::Low), (scl.id().num, PinState::Low)]);
307
308 sm.exec_instruction(pio::Instruction {
310 operands: pio::InstructionOperands::JMP {
311 condition: pio::JmpCondition::Always,
312 address: wrap_target,
313 },
314 delay: 0,
315 side_set: None,
316 });
317
318 let sm = sm.start();
320
321 Self {
322 pio,
323 sm,
324 tx,
325 rx,
326 sda: (sda, sda_override),
327 scl: (scl, scl_override),
328 }
329 }
330
331 fn has_irq(&mut self) -> bool {
332 let mask = 1 << SMI::id();
333 self.pio.get_irq_raw() & mask != 0
334 }
335
336 fn err_with(&mut self, err: Error) -> Result<(), Error> {
337 while self.rx.read().is_some() {}
339 self.sm.drain_tx_fifo();
341 self.tx.clear_stalled_flag();
343 while !(self.tx.has_stalled() || self.has_irq()) {}
344
345 if self.has_irq() {
347 self.sm.exec_instruction(Instruction {
348 operands: pio::InstructionOperands::OUT {
349 destination: pio::OutDestination::NULL,
350 bit_count: 16,
351 },
352 delay: 0,
353 side_set: None,
354 });
355 self.pio.clear_irq(1 << SMI::id());
357 }
358 self.generate_stop();
360 Err(err)
361 }
362
363 fn generate_stop(&mut self) {
364 assert!(self.tx.is_empty(), "TX FIFO is empty");
367
368 stop().for_each(|encoded| {
369 self.tx.write_u16_replicated(encoded);
370 });
371 self.tx.clear_stalled_flag();
372 while !self.tx.has_stalled() {}
373 }
374
375 fn process_queue<'b>(
376 &mut self,
377 queue: impl IntoIterator<Item = CmdWord<'b>>,
378 ) -> Result<(), Error> {
379 let mut output = queue.into_iter().peekable();
380 let mut input: Deque<Data<'b>, 9> = Deque::new();
384
385 while output.peek().is_some() || !input.is_empty() {
387 if !self.tx.is_full() {
389 if let Some(mut word) = output.next() {
390 let last = matches!(
391 (&mut word, output.peek()),
392 (CmdWord::Data(_), None) | (CmdWord::Data(_), Some(CmdWord::Raw(_)))
393 );
394 let word_u16 = word.encode(last);
395 self.tx.write_u16_replicated(word_u16);
396 if let CmdWord::Data(d) = word {
397 input.push_back(d).expect("`input` is not full");
398 }
399 }
400 }
401
402 if let Some(word) = self.rx.read() {
403 let word = (word & 0xFF) as u8;
404 if let Some(d) = input.pop_front() {
405 match d.byte {
406 Left(exp) if word != exp => {
407 return self.err_with(Error::BusContention);
408 }
409 Right(inp) => *inp = word,
410 _ => {}
411 }
412 }
413 } else if self.has_irq() {
414 let Some(d) = input.pop_front() else {
417 unreachable!("There cannot be a failure without a transmition")
418 };
419 return self.err_with(if d.is_address {
420 Error::NoAcknowledgeAddress
421 } else {
422 Error::NoAcknowledgeData
423 });
424 }
425 }
426 Ok(())
427 }
428}
429
430impl<P, SMI, SDA, SCL> I2C<'_, P, SMI, SDA, SCL>
431where
432 P: PIOExt,
433 SMI: StateMachineIndex,
434 SDA: AnyPin,
435 SDA::Id: ValidFunction<SDA::Function>,
436 SCL: AnyPin,
437 SCL::Id: ValidFunction<SCL::Function>,
438{
439 fn reset_pin<I, F, T>(
440 (mut pin, override_): (Pin<I, P::PinFunction, PullUp>, OutputEnableOverride),
441 ) -> Pin<I, F, T>
442 where
443 I: PinId,
444 F: Function,
445 T: PullType,
446 I: ValidFunction<F>,
447 {
448 pin.set_output_enable_override(OutputEnableOverride::Disable);
450 let mut pin = pin.reconfigure();
452 pin.set_output_enable_override(override_);
454 pin
455 }
456
457 #[allow(clippy::type_complexity)]
459 pub fn free(self) -> ((SDA::Type, SCL::Type), UninitStateMachine<(P, SMI)>) {
460 let Self {
461 pio,
462 sm,
463 tx,
464 rx,
465 sda,
466 scl,
467 ..
468 } = self;
469 let (uninit, program) = sm.uninit(rx, tx);
470 pio.uninstall(program);
471
472 let scl = Self::reset_pin(scl);
473 let sda = Self::reset_pin(sda);
474
475 ((sda, scl), uninit)
476 }
477}