aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Fitzhardinge <[email protected]>2022-09-28 01:20:04 -0700
committerJeremy Fitzhardinge <[email protected]>2022-10-01 13:43:37 -0700
commit5e2c52ee5b6fcc5b50589fd2590657e3f1083bff (patch)
tree90dd2a95bc276bed26bb2574deaed75891178e76
parent72b645b0c96c7b8312d0a64f851e807faacd78af (diff)
embassy-rp: async i2c implementation
This is an interrupt-driven async i2c master implementation. It makes as best use of the RP2040's i2c block's fifos as possible to minimize interrupts. It implements embedded_hal_async::i2c for easy interop. WIP async impl
-rw-r--r--embassy-rp/src/i2c.rs376
-rw-r--r--examples/rp/Cargo.toml3
2 files changed, 369 insertions, 10 deletions
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs
index c609b02ea..6fc64d849 100644
--- a/embassy-rp/src/i2c.rs
+++ b/embassy-rp/src/i2c.rs
@@ -1,6 +1,10 @@
1use core::future;
1use core::marker::PhantomData; 2use core::marker::PhantomData;
3use core::task::Poll;
2 4
5use embassy_cortex_m::interrupt::InterruptExt;
3use embassy_hal_common::{into_ref, PeripheralRef}; 6use embassy_hal_common::{into_ref, PeripheralRef};
7use embassy_sync::waitqueue::AtomicWaker;
4use pac::i2c; 8use pac::i2c;
5 9
6use crate::gpio::sealed::Pin; 10use crate::gpio::sealed::Pin;
@@ -79,7 +83,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> {
79 // NOTE(unsafe) We have &mut self 83 // NOTE(unsafe) We have &mut self
80 unsafe { 84 unsafe {
81 // wait until there is space in the FIFO to write the next byte 85 // wait until there is space in the FIFO to write the next byte
82 while p.ic_txflr().read().txflr() == FIFO_SIZE {} 86 while Self::tx_fifo_full() {}
83 87
84 p.ic_data_cmd().write(|w| { 88 p.ic_data_cmd().write(|w| {
85 w.set_restart(restart && first); 89 w.set_restart(restart && first);
@@ -88,7 +92,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> {
88 w.set_cmd(true); 92 w.set_cmd(true);
89 }); 93 });
90 94
91 while p.ic_rxflr().read().rxflr() == 0 { 95 while Self::rx_fifo_len() == 0 {
92 self.read_and_clear_abort_reason()?; 96 self.read_and_clear_abort_reason()?;
93 } 97 }
94 98
@@ -165,9 +169,250 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> {
165 // Automatic Stop 169 // Automatic Stop
166 } 170 }
167} 171}
172
173static I2C_WAKER: AtomicWaker = AtomicWaker::new();
174
175impl<'d, T: Instance> I2c<'d, T, Async> {
176 pub fn new_async(
177 peri: impl Peripheral<P = T> + 'd,
178 scl: impl Peripheral<P = impl SclPin<T>> + 'd,
179 sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
180 irq: impl Peripheral<P = T::Interrupt> + 'd,
181 config: Config,
182 ) -> Self {
183 into_ref!(scl, sda, irq);
184
185 let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config);
186
187 irq.set_handler(Self::on_interrupt);
188 unsafe {
189 let i2c = T::regs();
190
191 // mask everything initially
192 i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
193 }
194 irq.unpend();
195 debug_assert!(!irq.is_pending());
196 irq.enable();
197
198 i2c
199 }
200
201 /// Calls `f` to check if we are ready or not.
202 /// If not, `g` is called once the waker is set (to eg enable the required interrupts).
203 async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
204 where
205 F: FnMut(&mut Self) -> Poll<U>,
206 G: FnMut(&mut Self),
207 {
208 future::poll_fn(|cx| {
209 let r = f(self);
210
211 if r.is_pending() {
212 I2C_WAKER.register(cx.waker());
213 g(self);
214 }
215 r
216 })
217 .await
218 }
219
220 // Mask interrupts and wake any task waiting for this interrupt
221 unsafe fn on_interrupt(_: *mut ()) {
222 let i2c = T::regs();
223 i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());
224
225 I2C_WAKER.wake();
226 }
227
228 async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
229 if buffer.is_empty() {
230 return Err(Error::InvalidReadBufferLength);
231 }
232
233 let p = T::regs();
234
235 let mut remaining = buffer.len();
236 let mut remaining_queue = buffer.len();
237
238 let mut abort_reason = None;
239
240 while remaining > 0 {
241 // Waggle SCK - basically the same as write
242 let tx_fifo_space = Self::tx_fifo_capacity();
243 let mut batch = 0;
244
245 debug_assert!(remaining_queue > 0);
246
247 for _ in 0..remaining_queue.min(tx_fifo_space as usize) {
248 remaining_queue -= 1;
249 let last = remaining_queue == 0;
250 batch += 1;
251
252 unsafe {
253 p.ic_data_cmd().write(|w| {
254 w.set_restart(restart && remaining_queue == buffer.len() - 1);
255 w.set_stop(last && send_stop);
256 w.set_cmd(true);
257 });
258 }
259 }
260
261 // We've either run out of txfifo or just plain finished setting up
262 // the clocks for the message - either way we need to wait for rx
263 // data.
264
265 debug_assert!(batch > 0);
266 let res = self
267 .wait_on(
268 |me| {
269 let rxfifo = Self::rx_fifo_len();
270 if let Err(abort_reason) = me.read_and_clear_abort_reason() {
271 Poll::Ready(Err(abort_reason))
272 } else if rxfifo >= batch {
273 Poll::Ready(Ok(rxfifo))
274 } else {
275 Poll::Pending
276 }
277 },
278 |_me| unsafe {
279 // Set the read threshold to the number of bytes we're
280 // expecting so we don't get spurious interrupts.
281 p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));
282
283 p.ic_intr_mask().modify(|w| {
284 w.set_m_rx_full(true);
285 w.set_m_tx_abrt(true);
286 });
287 },
288 )
289 .await;
290
291 match res {
292 Err(reason) => {
293 abort_reason = Some(reason);
294 // XXX keep going anyway?
295 break;
296 }
297 Ok(rxfifo) => {
298 // Fetch things from rx fifo. We're assuming we're the only
299 // rxfifo reader, so nothing else can take things from it.
300 let rxbytes = (rxfifo as usize).min(remaining);
301 let received = buffer.len() - remaining;
302 for b in &mut buffer[received..received + rxbytes] {
303 *b = unsafe { p.ic_data_cmd().read().dat() };
304 }
305 remaining -= rxbytes;
306 }
307 };
308 }
309
310 // wait for stop condition to be emitted.
311 self.wait_on(
312 |_me| unsafe {
313 if !p.ic_raw_intr_stat().read().stop_det() && send_stop {
314 Poll::Pending
315 } else {
316 Poll::Ready(())
317 }
318 },
319 |_me| unsafe {
320 p.ic_intr_mask().modify(|w| {
321 w.set_m_stop_det(true);
322 w.set_m_tx_abrt(true);
323 });
324 },
325 )
326 .await;
327 unsafe { p.ic_clr_stop_det().read() };
328
329 if let Some(abort_reason) = abort_reason {
330 return Err(abort_reason);
331 }
332 Ok(())
333 }
334
335 async fn write_async_internal(
336 &mut self,
337 bytes: impl IntoIterator<Item = u8>,
338 send_stop: bool,
339 ) -> Result<(), Error> {
340 let p = T::regs();
341
342 let mut bytes = bytes.into_iter().peekable();
343
344 'xmit: loop {
345 let tx_fifo_space = Self::tx_fifo_capacity();
346
347 for _ in 0..tx_fifo_space {
348 if let Some(byte) = bytes.next() {
349 let last = bytes.peek().is_none();
350
351 unsafe {
352 p.ic_data_cmd().write(|w| {
353 w.set_stop(last && send_stop);
354 w.set_cmd(false);
355 w.set_dat(byte);
356 });
357 }
358 } else {
359 break 'xmit;
360 }
361 }
362
363 self.wait_on(
364 |me| {
365 if let Err(abort_reason) = me.read_and_clear_abort_reason() {
366 Poll::Ready(Err(abort_reason))
367 } else if !Self::tx_fifo_full() {
368 Poll::Ready(Ok(()))
369 } else {
370 Poll::Pending
371 }
372 },
373 |_me| unsafe {
374 p.ic_intr_mask().modify(|w| {
375 w.set_m_tx_empty(true);
376 w.set_m_tx_abrt(true);
377 })
378 },
379 )
380 .await?;
381 }
382
383 // wait for fifo to drain
384 self.wait_on(
385 |_me| unsafe {
386 if p.ic_raw_intr_stat().read().tx_empty() {
387 Poll::Ready(())
388 } else {
389 Poll::Pending
390 }
391 },
392 |_me| unsafe {
393 p.ic_intr_mask().modify(|w| {
394 w.set_m_tx_empty(true);
395 w.set_m_tx_abrt(true);
396 });
397 },
398 )
399 .await;
400
401 Ok(())
402 }
403
404 pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {
405 Self::setup(addr)?;
406 self.read_async_internal(buffer, false, true).await
407 }
408
409 pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> {
410 Self::setup(addr)?;
411 self.write_async_internal(buffer.iter().copied(), true).await
412 }
168} 413}
169 414
170impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { 415impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
171 fn new_inner( 416 fn new_inner(
172 _peri: impl Peripheral<P = T> + 'd, 417 _peri: impl Peripheral<P = T> + 'd,
173 scl: PeripheralRef<'d, AnyPin>, 418 scl: PeripheralRef<'d, AnyPin>,
@@ -182,6 +427,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
182 let p = T::regs(); 427 let p = T::regs();
183 428
184 unsafe { 429 unsafe {
430 let reset = T::reset();
431 crate::reset::reset(reset);
432 crate::reset::unreset_wait(reset);
433
185 p.ic_enable().write(|w| w.set_enable(false)); 434 p.ic_enable().write(|w| w.set_enable(false));
186 435
187 // Select controller mode & speed 436 // Select controller mode & speed
@@ -267,9 +516,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
267 p.ic_enable().write(|w| w.set_enable(true)); 516 p.ic_enable().write(|w| w.set_enable(true));
268 } 517 }
269 518
270 Self { 519 Self { phantom: PhantomData }
271 phantom: PhantomData,
272 }
273 } 520 }
274 521
275 fn setup(addr: u16) -> Result<(), Error> { 522 fn setup(addr: u16) -> Result<(), Error> {
@@ -290,6 +537,23 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
290 Ok(()) 537 Ok(())
291 } 538 }
292 539
540 #[inline]
541 fn tx_fifo_full() -> bool {
542 Self::tx_fifo_capacity() == 0
543 }
544
545 #[inline]
546 fn tx_fifo_capacity() -> u8 {
547 let p = T::regs();
548 unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() }
549 }
550
551 #[inline]
552 fn rx_fifo_len() -> u8 {
553 let p = T::regs();
554 unsafe { p.ic_rxflr().read().rxflr() }
555 }
556
293 fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { 557 fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
294 let p = T::regs(); 558 let p = T::regs();
295 unsafe { 559 unsafe {
@@ -317,7 +581,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
317 } 581 }
318 } 582 }
319 } 583 }
320
321} 584}
322 585
323mod eh02 { 586mod eh02 {
@@ -444,6 +707,91 @@ mod eh1 {
444 } 707 }
445 } 708 }
446} 709}
710#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
711mod nightly {
712 use core::future::Future;
713
714 use embedded_hal_1::i2c::Operation;
715 use embedded_hal_async::i2c::AddressMode;
716
717 use super::*;
718
719 impl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>
720 where
721 A: AddressMode + Into<u16> + 'static,
722 T: Instance + 'd,
723 {
724 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
725 where Self: 'a;
726 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
727 where Self: 'a;
728 type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
729 where Self: 'a;
730 type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Error>> + 'a
731 where Self: 'a, 'b: 'a;
732
733 fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
734 let addr: u16 = address.into();
735
736 async move {
737 Self::setup(addr)?;
738 self.read_async_internal(buffer, false, true).await
739 }
740 }
741
742 fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
743 let addr: u16 = address.into();
744
745 async move {
746 Self::setup(addr)?;
747 self.write_async_internal(write.iter().copied(), true).await
748 }
749 }
750
751 fn write_read<'a>(
752 &'a mut self,
753 address: A,
754 bytes: &'a [u8],
755 buffer: &'a mut [u8],
756 ) -> Self::WriteReadFuture<'a> {
757 let addr: u16 = address.into();
758
759 async move {
760 Self::setup(addr)?;
761 self.write_async_internal(bytes.iter().cloned(), false).await?;
762 self.read_async_internal(buffer, false, true).await
763 }
764 }
765
766 fn transaction<'a, 'b>(
767 &'a mut self,
768 address: A,
769 operations: &'a mut [Operation<'b>],
770 ) -> Self::TransactionFuture<'a, 'b> {
771 let addr: u16 = address.into();
772
773 async move {
774 let mut iterator = operations.iter_mut();
775
776 while let Some(op) = iterator.next() {
777 let last = iterator.len() == 0;
778
779 match op {
780 Operation::Read(buffer) => {
781 Self::setup(addr)?;
782 self.read_async_internal(buffer, false, last).await?;
783 }
784 Operation::Write(buffer) => {
785 Self::setup(addr)?;
786 self.write_async_internal(buffer.into_iter().cloned(), last).await?;
787 }
788 }
789 }
790 Ok(())
791 }
792 }
793 }
794}
447 795
448fn i2c_reserved_addr(addr: u16) -> bool { 796fn i2c_reserved_addr(addr: u16) -> bool {
449 (addr & 0x78) == 0 || (addr & 0x78) == 0x78 797 (addr & 0x78) == 0 || (addr & 0x78) == 0x78
@@ -459,6 +807,7 @@ mod sealed {
459 type Interrupt: Interrupt; 807 type Interrupt: Interrupt;
460 808
461 fn regs() -> crate::pac::i2c::I2c; 809 fn regs() -> crate::pac::i2c::I2c;
810 fn reset() -> crate::pac::resets::regs::Peripherals;
462 } 811 }
463 812
464 pub trait Mode {} 813 pub trait Mode {}
@@ -485,7 +834,7 @@ impl_mode!(Async);
485pub trait Instance: sealed::Instance {} 834pub trait Instance: sealed::Instance {}
486 835
487macro_rules! impl_instance { 836macro_rules! impl_instance {
488 ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { 837 ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => {
489 impl sealed::Instance for peripherals::$type { 838 impl sealed::Instance for peripherals::$type {
490 const TX_DREQ: u8 = $tx_dreq; 839 const TX_DREQ: u8 = $tx_dreq;
491 const RX_DREQ: u8 = $rx_dreq; 840 const RX_DREQ: u8 = $rx_dreq;
@@ -496,13 +845,20 @@ macro_rules! impl_instance {
496 fn regs() -> pac::i2c::I2c { 845 fn regs() -> pac::i2c::I2c {
497 pac::$type 846 pac::$type
498 } 847 }
848
849 #[inline]
850 fn reset() -> pac::resets::regs::Peripherals {
851 let mut ret = pac::resets::regs::Peripherals::default();
852 ret.$reset(true);
853 ret
854 }
499 } 855 }
500 impl Instance for peripherals::$type {} 856 impl Instance for peripherals::$type {}
501 }; 857 };
502} 858}
503 859
504impl_instance!(I2C0, I2C0_IRQ, 32, 33); 860impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33);
505impl_instance!(I2C1, I2C1_IRQ, 34, 35); 861impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35);
506 862
507pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} 863pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
508pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} 864pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 3c8f923e7..e689df6bd 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -30,3 +30,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
30embedded-hal-async = { version = "0.1.0-alpha.1" } 30embedded-hal-async = { version = "0.1.0-alpha.1" }
31embedded-io = { version = "0.3.0", features = ["async", "defmt"] } 31embedded-io = { version = "0.3.0", features = ["async", "defmt"] }
32static_cell = "1.0.0" 32static_cell = "1.0.0"
33
34[profile.release]
35debug = true