diff options
| author | Mathias <[email protected]> | 2022-09-27 07:55:28 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-09-27 22:08:49 +0200 |
| commit | 44c46e3c93ae0718110a8805cab4c6c8a9b5df55 (patch) | |
| tree | c31ee1fecfd3dc8d1aa6dbdbbddf959543610e1c | |
| parent | b0d91e9f310f86b4eb9d75c92471831f1656ed1b (diff) | |
Move async i2c implementation to new PR, to merge working blocking implementation faster
| -rw-r--r-- | embassy-rp/src/i2c.rs | 179 |
1 files changed, 1 insertions, 178 deletions
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 12fae3b7b..d1ec77d33 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs | |||
| @@ -1,11 +1,9 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | 2 | ||
| 3 | use atomic_polyfill::Ordering; | ||
| 4 | use embassy_cortex_m::interrupt::InterruptExt; | ||
| 5 | use embassy_hal_common::{into_ref, PeripheralRef}; | 3 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 6 | use pac::i2c; | 4 | use pac::i2c; |
| 7 | 5 | ||
| 8 | use crate::dma::{AnyChannel, Channel}; | 6 | use crate::dma::AnyChannel; |
| 9 | use crate::gpio::sealed::Pin; | 7 | use crate::gpio::sealed::Pin; |
| 10 | use crate::gpio::AnyPin; | 8 | use crate::gpio::AnyPin; |
| 11 | use crate::{pac, peripherals, Peripheral}; | 9 | use crate::{pac, peripherals, Peripheral}; |
| @@ -60,159 +58,6 @@ pub struct I2c<'d, T: Instance, M: Mode> { | |||
| 60 | phantom: PhantomData<(&'d mut T, M)>, | 58 | phantom: PhantomData<(&'d mut T, M)>, |
| 61 | } | 59 | } |
| 62 | 60 | ||
| 63 | impl<'d, T: Instance> I2c<'d, T, Async> { | ||
| 64 | pub fn new( | ||
| 65 | _peri: impl Peripheral<P = T> + 'd, | ||
| 66 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||
| 67 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | ||
| 68 | irq: impl Peripheral<P = T::Interrupt> + 'd, | ||
| 69 | tx_dma: impl Peripheral<P = impl Channel> + 'd, | ||
| 70 | rx_dma: impl Peripheral<P = impl Channel> + 'd, | ||
| 71 | config: Config, | ||
| 72 | ) -> Self { | ||
| 73 | into_ref!(scl, sda, irq, tx_dma, rx_dma); | ||
| 74 | |||
| 75 | // Enable interrupts | ||
| 76 | unsafe { | ||
| 77 | T::regs().ic_intr_mask().modify(|w| { | ||
| 78 | w.set_m_rx_done(true); | ||
| 79 | }); | ||
| 80 | } | ||
| 81 | |||
| 82 | irq.set_handler(Self::on_interrupt); | ||
| 83 | irq.unpend(); | ||
| 84 | irq.enable(); | ||
| 85 | |||
| 86 | Self::new_inner( | ||
| 87 | _peri, | ||
| 88 | scl.map_into(), | ||
| 89 | sda.map_into(), | ||
| 90 | Some(tx_dma.map_into()), | ||
| 91 | Some(rx_dma.map_into()), | ||
| 92 | config, | ||
| 93 | ) | ||
| 94 | } | ||
| 95 | |||
| 96 | unsafe fn on_interrupt(_: *mut ()) { | ||
| 97 | let status = T::regs().ic_intr_stat().read(); | ||
| 98 | |||
| 99 | // FIXME: | ||
| 100 | if status.tcr() || status.tc() { | ||
| 101 | let state = T::state(); | ||
| 102 | state.chunks_transferred.fetch_add(1, Ordering::Relaxed); | ||
| 103 | state.waker.wake(); | ||
| 104 | } | ||
| 105 | // The flag can only be cleared by writting to nbytes, we won't do that here, so disable | ||
| 106 | // the interrupt | ||
| 107 | // critical_section::with(|_| { | ||
| 108 | // regs.cr1().modify(|w| w.set_tcie(false)); | ||
| 109 | // }); | ||
| 110 | } | ||
| 111 | |||
| 112 | async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { | ||
| 113 | let len = bytes.len(); | ||
| 114 | for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { | ||
| 115 | let first = idx == 0; | ||
| 116 | let last = idx * self.dma_buf.len() + chunk.len() == len; | ||
| 117 | |||
| 118 | for (i, byte) in chunk.iter().enumerate() { | ||
| 119 | let mut b = i2c::regs::IcDataCmd::default(); | ||
| 120 | b.set_dat(*byte); | ||
| 121 | b.set_stop(send_stop && last); | ||
| 122 | |||
| 123 | self.dma_buf[i] = b.0 as u16; | ||
| 124 | } | ||
| 125 | |||
| 126 | // Note(safety): Unwrap should be safe, as this can only be called | ||
| 127 | // when `Mode == Async`, where we have dma channels. | ||
| 128 | let ch = self.tx_dma.as_mut().unwrap(); | ||
| 129 | let transfer = unsafe { | ||
| 130 | T::regs().ic_dma_cr().modify(|w| { | ||
| 131 | w.set_tdmae(true); | ||
| 132 | }); | ||
| 133 | |||
| 134 | crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) | ||
| 135 | }; | ||
| 136 | |||
| 137 | transfer.await; | ||
| 138 | } | ||
| 139 | |||
| 140 | Ok(()) | ||
| 141 | } | ||
| 142 | |||
| 143 | async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | ||
| 144 | let len = buffer.len(); | ||
| 145 | self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; | ||
| 146 | |||
| 147 | if len >= 2 { | ||
| 148 | // Note(safety): Unwrap should be safe, as this can only be called | ||
| 149 | // when `Mode == Async`, where we have dma channels. | ||
| 150 | let ch = self.rx_dma.as_mut().unwrap(); | ||
| 151 | let transfer = unsafe { | ||
| 152 | T::regs().ic_data_cmd().modify(|w| { | ||
| 153 | w.set_cmd(true); | ||
| 154 | }); | ||
| 155 | |||
| 156 | T::regs().ic_dma_cr().modify(|reg| { | ||
| 157 | reg.set_rdmae(true); | ||
| 158 | }); | ||
| 159 | // If we don't assign future to a variable, the data register pointer | ||
| 160 | // is held across an await and makes the future non-Send. | ||
| 161 | crate::dma::read( | ||
| 162 | ch, | ||
| 163 | T::regs().ic_data_cmd().ptr() as *const _, | ||
| 164 | &mut buffer[1..len - 1], | ||
| 165 | T::RX_DREQ, | ||
| 166 | ) | ||
| 167 | }; | ||
| 168 | transfer.await; | ||
| 169 | } | ||
| 170 | |||
| 171 | if len >= 2 { | ||
| 172 | self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; | ||
| 173 | } | ||
| 174 | |||
| 175 | Ok(()) | ||
| 176 | } | ||
| 177 | |||
| 178 | // ========================= | ||
| 179 | // Async public API | ||
| 180 | // ========================= | ||
| 181 | |||
| 182 | pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { | ||
| 183 | Self::setup(address.into())?; | ||
| 184 | if bytes.is_empty() { | ||
| 185 | self.write_blocking_internal(bytes, true) | ||
| 186 | } else { | ||
| 187 | self.write_internal(bytes, true).await | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | ||
| 192 | Self::setup(address.into())?; | ||
| 193 | if buffer.is_empty() { | ||
| 194 | self.read_blocking_internal(buffer, true, true) | ||
| 195 | } else { | ||
| 196 | self.read_internal(buffer).await | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { | ||
| 201 | Self::setup(address.into())?; | ||
| 202 | if bytes.is_empty() { | ||
| 203 | self.write_blocking_internal(bytes, false)?; | ||
| 204 | } else { | ||
| 205 | self.write_internal(bytes, true).await?; | ||
| 206 | } | ||
| 207 | |||
| 208 | if buffer.is_empty() { | ||
| 209 | self.read_blocking_internal(buffer, true, true) | ||
| 210 | } else { | ||
| 211 | self.read_internal(buffer).await | ||
| 212 | } | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | impl<'d, T: Instance> I2c<'d, T, Blocking> { | 61 | impl<'d, T: Instance> I2c<'d, T, Blocking> { |
| 217 | pub fn new_blocking( | 62 | pub fn new_blocking( |
| 218 | _peri: impl Peripheral<P = T> + 'd, | 63 | _peri: impl Peripheral<P = T> + 'd, |
| @@ -616,23 +461,7 @@ fn i2c_reserved_addr(addr: u16) -> bool { | |||
| 616 | } | 461 | } |
| 617 | 462 | ||
| 618 | mod sealed { | 463 | mod sealed { |
| 619 | use atomic_polyfill::AtomicUsize; | ||
| 620 | use embassy_cortex_m::interrupt::Interrupt; | 464 | use embassy_cortex_m::interrupt::Interrupt; |
| 621 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 622 | |||
| 623 | pub(crate) struct State { | ||
| 624 | pub(crate) waker: AtomicWaker, | ||
| 625 | pub(crate) chunks_transferred: AtomicUsize, | ||
| 626 | } | ||
| 627 | |||
| 628 | impl State { | ||
| 629 | pub(crate) const fn new() -> Self { | ||
| 630 | Self { | ||
| 631 | waker: AtomicWaker::new(), | ||
| 632 | chunks_transferred: AtomicUsize::new(0), | ||
| 633 | } | ||
| 634 | } | ||
| 635 | } | ||
| 636 | 465 | ||
| 637 | pub trait Instance { | 466 | pub trait Instance { |
| 638 | const TX_DREQ: u8; | 467 | const TX_DREQ: u8; |
| @@ -641,7 +470,6 @@ mod sealed { | |||
| 641 | type Interrupt: Interrupt; | 470 | type Interrupt: Interrupt; |
| 642 | 471 | ||
| 643 | fn regs() -> crate::pac::i2c::I2c; | 472 | fn regs() -> crate::pac::i2c::I2c; |
| 644 | fn state() -> &'static State; | ||
| 645 | } | 473 | } |
| 646 | 474 | ||
| 647 | pub trait Mode {} | 475 | pub trait Mode {} |
| @@ -678,11 +506,6 @@ macro_rules! impl_instance { | |||
| 678 | fn regs() -> pac::i2c::I2c { | 506 | fn regs() -> pac::i2c::I2c { |
| 679 | pac::$type | 507 | pac::$type |
| 680 | } | 508 | } |
| 681 | |||
| 682 | fn state() -> &'static sealed::State { | ||
| 683 | static STATE: sealed::State = sealed::State::new(); | ||
| 684 | &STATE | ||
| 685 | } | ||
| 686 | } | 509 | } |
| 687 | impl Instance for peripherals::$type {} | 510 | impl Instance for peripherals::$type {} |
| 688 | }; | 511 | }; |
