diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-09-10 21:02:32 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-10 21:02:32 +0000 |
| commit | ceb13def1945e164ceef28013497514760fef70f (patch) | |
| tree | 910a0f6a8712f781149b0e3aa4e567faf3f6cdd8 | |
| parent | ceca8b4336d8706a9e767c0d397182d02527d8cc (diff) | |
| parent | 5cf494113f7681ac6436bdbd7972e0074d1d26b7 (diff) | |
Merge pull request #1850 from CBJamo/i2cdev
rp2040: I2cDevice
| -rw-r--r-- | embassy-rp/src/i2c.rs | 9 | ||||
| -rw-r--r-- | embassy-rp/src/i2c_slave.rs | 338 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 1 | ||||
| -rw-r--r-- | examples/rp/src/bin/i2c_slave.rs | 118 | ||||
| -rw-r--r-- | tests/rp/src/bin/i2c.rs | 212 |
5 files changed, 675 insertions, 3 deletions
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 7a5ddd325..c358682c5 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs | |||
| @@ -21,6 +21,8 @@ pub enum AbortReason { | |||
| 21 | NoAcknowledge, | 21 | NoAcknowledge, |
| 22 | /// The arbitration was lost, e.g. electrical problems with the clock signal | 22 | /// The arbitration was lost, e.g. electrical problems with the clock signal |
| 23 | ArbitrationLoss, | 23 | ArbitrationLoss, |
| 24 | /// Transmit ended with data still in fifo | ||
| 25 | TxNotEmpty(u16), | ||
| 24 | Other(u32), | 26 | Other(u32), |
| 25 | } | 27 | } |
| 26 | 28 | ||
| @@ -52,7 +54,7 @@ impl Default for Config { | |||
| 52 | } | 54 | } |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | const FIFO_SIZE: u8 = 16; | 57 | pub const FIFO_SIZE: u8 = 16; |
| 56 | 58 | ||
| 57 | pub struct I2c<'d, T: Instance, M: Mode> { | 59 | pub struct I2c<'d, T: Instance, M: Mode> { |
| 58 | phantom: PhantomData<(&'d mut T, M)>, | 60 | phantom: PhantomData<(&'d mut T, M)>, |
| @@ -636,6 +638,7 @@ mod eh1 { | |||
| 636 | Self::Abort(AbortReason::NoAcknowledge) => { | 638 | Self::Abort(AbortReason::NoAcknowledge) => { |
| 637 | embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) | 639 | embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) |
| 638 | } | 640 | } |
| 641 | Self::Abort(AbortReason::TxNotEmpty(_)) => embedded_hal_1::i2c::ErrorKind::Other, | ||
| 639 | Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, | 642 | Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, |
| 640 | Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, | 643 | Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, |
| 641 | Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, | 644 | Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, |
| @@ -738,8 +741,8 @@ mod nightly { | |||
| 738 | } | 741 | } |
| 739 | } | 742 | } |
| 740 | 743 | ||
| 741 | fn i2c_reserved_addr(addr: u16) -> bool { | 744 | pub fn i2c_reserved_addr(addr: u16) -> bool { |
| 742 | (addr & 0x78) == 0 || (addr & 0x78) == 0x78 | 745 | ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 |
| 743 | } | 746 | } |
| 744 | 747 | ||
| 745 | mod sealed { | 748 | mod sealed { |
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs new file mode 100644 index 000000000..6136d69c6 --- /dev/null +++ b/embassy-rp/src/i2c_slave.rs | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | use core::future; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::task::Poll; | ||
| 4 | |||
| 5 | use embassy_hal_internal::into_ref; | ||
| 6 | use pac::i2c; | ||
| 7 | |||
| 8 | use crate::i2c::{i2c_reserved_addr, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE}; | ||
| 9 | use crate::interrupt::typelevel::{Binding, Interrupt}; | ||
| 10 | use crate::{pac, Peripheral}; | ||
| 11 | |||
| 12 | /// I2C error | ||
| 13 | #[derive(Debug)] | ||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | #[non_exhaustive] | ||
| 16 | pub enum Error { | ||
| 17 | /// I2C abort with error | ||
| 18 | Abort(AbortReason), | ||
| 19 | /// User passed in a response buffer that was 0 length | ||
| 20 | InvalidResponseBufferLength, | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Received command | ||
| 24 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub enum Command { | ||
| 27 | /// General Call | ||
| 28 | GeneralCall(usize), | ||
| 29 | /// Read | ||
| 30 | Read, | ||
| 31 | /// Write+read | ||
| 32 | WriteRead(usize), | ||
| 33 | /// Write | ||
| 34 | Write(usize), | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Possible responses to responding to a read | ||
| 38 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 40 | pub enum ReadStatus { | ||
| 41 | /// Transaction Complete, controller naked our last byte | ||
| 42 | Done, | ||
| 43 | /// Transaction Incomplete, controller trying to read more bytes than were provided | ||
| 44 | NeedMoreBytes, | ||
| 45 | /// Transaction Complere, but controller stopped reading bytes before we ran out | ||
| 46 | LeftoverBytes(u16), | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Slave Configuration | ||
| 50 | #[non_exhaustive] | ||
| 51 | #[derive(Copy, Clone)] | ||
| 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 53 | pub struct Config { | ||
| 54 | /// Target Address | ||
| 55 | pub addr: u16, | ||
| 56 | } | ||
| 57 | |||
| 58 | impl Default for Config { | ||
| 59 | fn default() -> Self { | ||
| 60 | Self { addr: 0x55 } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | pub struct I2cSlave<'d, T: Instance> { | ||
| 65 | phantom: PhantomData<&'d mut T>, | ||
| 66 | } | ||
| 67 | |||
| 68 | impl<'d, T: Instance> I2cSlave<'d, T> { | ||
| 69 | pub fn new( | ||
| 70 | _peri: impl Peripheral<P = T> + 'd, | ||
| 71 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, | ||
| 72 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | ||
| 73 | _irq: impl Binding<T::Interrupt, InterruptHandler<T>>, | ||
| 74 | config: Config, | ||
| 75 | ) -> Self { | ||
| 76 | into_ref!(_peri, scl, sda); | ||
| 77 | |||
| 78 | assert!(!i2c_reserved_addr(config.addr)); | ||
| 79 | assert!(config.addr != 0); | ||
| 80 | |||
| 81 | let p = T::regs(); | ||
| 82 | |||
| 83 | let reset = T::reset(); | ||
| 84 | crate::reset::reset(reset); | ||
| 85 | crate::reset::unreset_wait(reset); | ||
| 86 | |||
| 87 | p.ic_enable().write(|w| w.set_enable(false)); | ||
| 88 | |||
| 89 | p.ic_sar().write(|w| w.set_ic_sar(config.addr)); | ||
| 90 | p.ic_con().modify(|w| { | ||
| 91 | w.set_master_mode(false); | ||
| 92 | w.set_ic_slave_disable(false); | ||
| 93 | w.set_tx_empty_ctrl(true); | ||
| 94 | }); | ||
| 95 | |||
| 96 | // Set FIFO watermarks to 1 to make things simpler. This is encoded | ||
| 97 | // by a register value of 0. Rx watermark should never change, but Tx watermark will be | ||
| 98 | // adjusted in operation. | ||
| 99 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); | ||
| 100 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||
| 101 | |||
| 102 | // Configure SCL & SDA pins | ||
| 103 | scl.gpio().ctrl().write(|w| w.set_funcsel(3)); | ||
| 104 | sda.gpio().ctrl().write(|w| w.set_funcsel(3)); | ||
| 105 | |||
| 106 | scl.pad_ctrl().write(|w| { | ||
| 107 | w.set_schmitt(true); | ||
| 108 | w.set_ie(true); | ||
| 109 | w.set_od(false); | ||
| 110 | w.set_pue(true); | ||
| 111 | w.set_pde(false); | ||
| 112 | }); | ||
| 113 | sda.pad_ctrl().write(|w| { | ||
| 114 | w.set_schmitt(true); | ||
| 115 | w.set_ie(true); | ||
| 116 | w.set_od(false); | ||
| 117 | w.set_pue(true); | ||
| 118 | w.set_pde(false); | ||
| 119 | }); | ||
| 120 | |||
| 121 | // Clear interrupts | ||
| 122 | p.ic_clr_intr().read(); | ||
| 123 | |||
| 124 | // Enable I2C block | ||
| 125 | p.ic_enable().write(|w| w.set_enable(true)); | ||
| 126 | |||
| 127 | // mask everything initially | ||
| 128 | p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); | ||
| 129 | T::Interrupt::unpend(); | ||
| 130 | unsafe { T::Interrupt::enable() }; | ||
| 131 | |||
| 132 | Self { phantom: PhantomData } | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Calls `f` to check if we are ready or not. | ||
| 136 | /// If not, `g` is called once the waker is set (to eg enable the required interrupts). | ||
| 137 | #[inline(always)] | ||
| 138 | async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U | ||
| 139 | where | ||
| 140 | F: FnMut(&mut Self) -> Poll<U>, | ||
| 141 | G: FnMut(&mut Self), | ||
| 142 | { | ||
| 143 | future::poll_fn(|cx| { | ||
| 144 | let r = f(self); | ||
| 145 | |||
| 146 | trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0); | ||
| 147 | |||
| 148 | if r.is_pending() { | ||
| 149 | T::waker().register(cx.waker()); | ||
| 150 | g(self); | ||
| 151 | } | ||
| 152 | |||
| 153 | r | ||
| 154 | }) | ||
| 155 | .await | ||
| 156 | } | ||
| 157 | |||
| 158 | #[inline(always)] | ||
| 159 | fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { | ||
| 160 | let p = T::regs(); | ||
| 161 | let len = p.ic_rxflr().read().rxflr() as usize; | ||
| 162 | let end = offset + len; | ||
| 163 | for i in offset..end { | ||
| 164 | buffer[i] = p.ic_data_cmd().read().dat(); | ||
| 165 | } | ||
| 166 | end | ||
| 167 | } | ||
| 168 | |||
| 169 | #[inline(always)] | ||
| 170 | fn write_to_fifo(&mut self, buffer: &[u8]) { | ||
| 171 | let p = T::regs(); | ||
| 172 | for byte in buffer { | ||
| 173 | p.ic_data_cmd().write(|w| w.set_dat(*byte)); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Wait asynchronously for commands from an I2C master. | ||
| 178 | /// `buffer` is provided in case master does a 'write' and is unused for 'read'. | ||
| 179 | pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { | ||
| 180 | let p = T::regs(); | ||
| 181 | |||
| 182 | p.ic_clr_intr().read(); | ||
| 183 | // set rx fifo watermark to 1 byte | ||
| 184 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||
| 185 | |||
| 186 | let mut len = 0; | ||
| 187 | let ret = self | ||
| 188 | .wait_on( | ||
| 189 | |me| { | ||
| 190 | let stat = p.ic_raw_intr_stat().read(); | ||
| 191 | if p.ic_rxflr().read().rxflr() > 0 { | ||
| 192 | len = me.drain_fifo(buffer, len); | ||
| 193 | // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise | ||
| 194 | p.ic_rx_tl().write(|w| w.set_rx_tl(11)); | ||
| 195 | } | ||
| 196 | |||
| 197 | if stat.restart_det() && stat.rd_req() { | ||
| 198 | Poll::Ready(Ok(Command::WriteRead(len))) | ||
| 199 | } else if stat.gen_call() && stat.stop_det() && len > 0 { | ||
| 200 | Poll::Ready(Ok(Command::GeneralCall(len))) | ||
| 201 | } else if stat.stop_det() { | ||
| 202 | Poll::Ready(Ok(Command::Write(len))) | ||
| 203 | } else if stat.rd_req() { | ||
| 204 | Poll::Ready(Ok(Command::Read)) | ||
| 205 | } else { | ||
| 206 | Poll::Pending | ||
| 207 | } | ||
| 208 | }, | ||
| 209 | |_me| { | ||
| 210 | p.ic_intr_mask().modify(|w| { | ||
| 211 | w.set_m_stop_det(true); | ||
| 212 | w.set_m_restart_det(true); | ||
| 213 | w.set_m_gen_call(true); | ||
| 214 | w.set_m_rd_req(true); | ||
| 215 | w.set_m_rx_full(true); | ||
| 216 | }); | ||
| 217 | }, | ||
| 218 | ) | ||
| 219 | .await; | ||
| 220 | |||
| 221 | p.ic_clr_intr().read(); | ||
| 222 | |||
| 223 | ret | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Respond to an I2C master READ command, asynchronously. | ||
| 227 | pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> { | ||
| 228 | let p = T::regs(); | ||
| 229 | |||
| 230 | if buffer.len() == 0 { | ||
| 231 | return Err(Error::InvalidResponseBufferLength); | ||
| 232 | } | ||
| 233 | |||
| 234 | let mut chunks = buffer.chunks(FIFO_SIZE as usize); | ||
| 235 | |||
| 236 | let ret = self | ||
| 237 | .wait_on( | ||
| 238 | |me| { | ||
| 239 | if let Err(abort_reason) = me.read_and_clear_abort_reason() { | ||
| 240 | if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { | ||
| 241 | return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); | ||
| 242 | } else { | ||
| 243 | return Poll::Ready(Err(abort_reason)); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | if let Some(chunk) = chunks.next() { | ||
| 248 | me.write_to_fifo(chunk); | ||
| 249 | |||
| 250 | Poll::Pending | ||
| 251 | } else { | ||
| 252 | let stat = p.ic_raw_intr_stat().read(); | ||
| 253 | |||
| 254 | if stat.rx_done() && stat.stop_det() { | ||
| 255 | Poll::Ready(Ok(ReadStatus::Done)) | ||
| 256 | } else if stat.rd_req() { | ||
| 257 | Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) | ||
| 258 | } else { | ||
| 259 | Poll::Pending | ||
| 260 | } | ||
| 261 | } | ||
| 262 | }, | ||
| 263 | |_me| { | ||
| 264 | p.ic_intr_mask().modify(|w| { | ||
| 265 | w.set_m_stop_det(true); | ||
| 266 | w.set_m_rx_done(true); | ||
| 267 | w.set_m_tx_empty(true); | ||
| 268 | w.set_m_tx_abrt(true); | ||
| 269 | }) | ||
| 270 | }, | ||
| 271 | ) | ||
| 272 | .await; | ||
| 273 | |||
| 274 | p.ic_clr_intr().read(); | ||
| 275 | |||
| 276 | ret | ||
| 277 | } | ||
| 278 | |||
| 279 | /// Respond to reads with the fill byte until the controller stops asking | ||
| 280 | pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { | ||
| 281 | loop { | ||
| 282 | match self.respond_to_read(&[fill]).await { | ||
| 283 | Ok(ReadStatus::NeedMoreBytes) => (), | ||
| 284 | Ok(_) => break Ok(()), | ||
| 285 | Err(e) => break Err(e), | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Respond to a master read, then fill any remaining read bytes with `fill` | ||
| 291 | pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result<ReadStatus, Error> { | ||
| 292 | let resp_stat = self.respond_to_read(buffer).await?; | ||
| 293 | |||
| 294 | if resp_stat == ReadStatus::NeedMoreBytes { | ||
| 295 | self.respond_till_stop(fill).await?; | ||
| 296 | Ok(ReadStatus::Done) | ||
| 297 | } else { | ||
| 298 | Ok(resp_stat) | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | #[inline(always)] | ||
| 303 | fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { | ||
| 304 | let p = T::regs(); | ||
| 305 | let mut abort_reason = p.ic_tx_abrt_source().read(); | ||
| 306 | |||
| 307 | // Mask off fifo flush count | ||
| 308 | let tx_flush_cnt = abort_reason.tx_flush_cnt(); | ||
| 309 | abort_reason.set_tx_flush_cnt(0); | ||
| 310 | |||
| 311 | // Mask off master_dis | ||
| 312 | abort_reason.set_abrt_master_dis(false); | ||
| 313 | |||
| 314 | if abort_reason.0 != 0 { | ||
| 315 | // Note clearing the abort flag also clears the reason, and this | ||
| 316 | // instance of flag is clear-on-read! Note also the | ||
| 317 | // IC_CLR_TX_ABRT register always reads as 0. | ||
| 318 | p.ic_clr_tx_abrt().read(); | ||
| 319 | |||
| 320 | let reason = if abort_reason.abrt_7b_addr_noack() | ||
| 321 | | abort_reason.abrt_10addr1_noack() | ||
| 322 | | abort_reason.abrt_10addr2_noack() | ||
| 323 | { | ||
| 324 | AbortReason::NoAcknowledge | ||
| 325 | } else if abort_reason.arb_lost() { | ||
| 326 | AbortReason::ArbitrationLoss | ||
| 327 | } else if abort_reason.abrt_slvflush_txfifo() { | ||
| 328 | AbortReason::TxNotEmpty(tx_flush_cnt) | ||
| 329 | } else { | ||
| 330 | AbortReason::Other(abort_reason.0) | ||
| 331 | }; | ||
| 332 | |||
| 333 | Err(Error::Abort(reason)) | ||
| 334 | } else { | ||
| 335 | Ok(()) | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 49bd3533e..2a1bca4b9 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -16,6 +16,7 @@ pub mod flash; | |||
| 16 | mod float; | 16 | mod float; |
| 17 | pub mod gpio; | 17 | pub mod gpio; |
| 18 | pub mod i2c; | 18 | pub mod i2c; |
| 19 | pub mod i2c_slave; | ||
| 19 | pub mod multicore; | 20 | pub mod multicore; |
| 20 | pub mod pwm; | 21 | pub mod pwm; |
| 21 | mod reset; | 22 | mod reset; |
diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs new file mode 100644 index 000000000..7de300fb8 --- /dev/null +++ b/examples/rp/src/bin/i2c_slave.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | //! This example shows how to use the 2040 as an i2c slave. | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::peripherals::{I2C0, I2C1}; | ||
| 9 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use embedded_hal_async::i2c::I2c; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; | ||
| 16 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | const DEV_ADDR: u8 = 0x42; | ||
| 20 | |||
| 21 | #[embassy_executor::task] | ||
| 22 | async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { | ||
| 23 | info!("Device start"); | ||
| 24 | |||
| 25 | let mut state = 0; | ||
| 26 | |||
| 27 | loop { | ||
| 28 | let mut buf = [0u8; 128]; | ||
| 29 | match dev.listen(&mut buf).await { | ||
| 30 | Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device recieved general call write: {}", buf[..len]), | ||
| 31 | Ok(i2c_slave::Command::Read) => loop { | ||
| 32 | match dev.respond_to_read(&[state]).await { | ||
| 33 | Ok(x) => match x { | ||
| 34 | i2c_slave::ReadStatus::Done => break, | ||
| 35 | i2c_slave::ReadStatus::NeedMoreBytes => (), | ||
| 36 | i2c_slave::ReadStatus::LeftoverBytes(x) => { | ||
| 37 | info!("tried to write {} extra bytes", x); | ||
| 38 | break; | ||
| 39 | } | ||
| 40 | }, | ||
| 41 | Err(e) => error!("error while responding {}", e), | ||
| 42 | } | ||
| 43 | }, | ||
| 44 | Ok(i2c_slave::Command::Write(len)) => info!("Device recieved write: {}", buf[..len]), | ||
| 45 | Ok(i2c_slave::Command::WriteRead(len)) => { | ||
| 46 | info!("device recieved write read: {:x}", buf[..len]); | ||
| 47 | match buf[0] { | ||
| 48 | // Set the state | ||
| 49 | 0xC2 => { | ||
| 50 | state = buf[1]; | ||
| 51 | match dev.respond_and_fill(&[state], 0x00).await { | ||
| 52 | Ok(read_status) => info!("response read status {}", read_status), | ||
| 53 | Err(e) => error!("error while responding {}", e), | ||
| 54 | } | ||
| 55 | } | ||
| 56 | // Reset State | ||
| 57 | 0xC8 => { | ||
| 58 | state = 0; | ||
| 59 | match dev.respond_and_fill(&[state], 0x00).await { | ||
| 60 | Ok(read_status) => info!("response read status {}", read_status), | ||
| 61 | Err(e) => error!("error while responding {}", e), | ||
| 62 | } | ||
| 63 | } | ||
| 64 | x => error!("Invalid Write Read {:x}", x), | ||
| 65 | } | ||
| 66 | } | ||
| 67 | Err(e) => error!("{}", e), | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | #[embassy_executor::task] | ||
| 73 | async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { | ||
| 74 | info!("Controller start"); | ||
| 75 | |||
| 76 | loop { | ||
| 77 | let mut resp_buff = [0u8; 2]; | ||
| 78 | for i in 0..10 { | ||
| 79 | match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await { | ||
| 80 | Ok(_) => info!("write_read response: {}", resp_buff), | ||
| 81 | Err(e) => error!("Error writing {}", e), | ||
| 82 | } | ||
| 83 | |||
| 84 | Timer::after(Duration::from_millis(100)).await; | ||
| 85 | } | ||
| 86 | match con.read(DEV_ADDR, &mut resp_buff).await { | ||
| 87 | Ok(_) => info!("read response: {}", resp_buff), | ||
| 88 | Err(e) => error!("Error writing {}", e), | ||
| 89 | } | ||
| 90 | match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await { | ||
| 91 | Ok(_) => info!("write_read response: {}", resp_buff), | ||
| 92 | Err(e) => error!("Error writing {}", e), | ||
| 93 | } | ||
| 94 | Timer::after(Duration::from_millis(100)).await; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | #[embassy_executor::main] | ||
| 99 | async fn main(spawner: Spawner) { | ||
| 100 | let p = embassy_rp::init(Default::default()); | ||
| 101 | info!("Hello World!"); | ||
| 102 | |||
| 103 | let d_sda = p.PIN_3; | ||
| 104 | let d_scl = p.PIN_2; | ||
| 105 | let mut config = i2c_slave::Config::default(); | ||
| 106 | config.addr = DEV_ADDR as u16; | ||
| 107 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); | ||
| 108 | |||
| 109 | unwrap!(spawner.spawn(device_task(device))); | ||
| 110 | |||
| 111 | let c_sda = p.PIN_1; | ||
| 112 | let c_scl = p.PIN_0; | ||
| 113 | let mut config = i2c::Config::default(); | ||
| 114 | config.frequency = 5_000; | ||
| 115 | let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); | ||
| 116 | |||
| 117 | unwrap!(spawner.spawn(controller_task(controller))); | ||
| 118 | } | ||
diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs new file mode 100644 index 000000000..425f2d086 --- /dev/null +++ b/tests/rp/src/bin/i2c.rs | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | teleprobe_meta::target!(b"rpi-pico"); | ||
| 5 | |||
| 6 | use defmt::{assert_eq, info, panic, unwrap}; | ||
| 7 | use embassy_executor::Executor; | ||
| 8 | use embassy_executor::_export::StaticCell; | ||
| 9 | use embassy_rp::multicore::{spawn_core1, Stack}; | ||
| 10 | use embassy_rp::peripherals::{I2C0, I2C1}; | ||
| 11 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; | ||
| 12 | use embedded_hal_1::i2c::Operation; | ||
| 13 | use embedded_hal_async::i2c::I2c; | ||
| 14 | use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; | ||
| 15 | |||
| 16 | static mut CORE1_STACK: Stack<1024> = Stack::new(); | ||
| 17 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 18 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||
| 19 | |||
| 20 | use crate::i2c::AbortReason; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | I2C0_IRQ => i2c::InterruptHandler<I2C0>; | ||
| 24 | I2C1_IRQ => i2c::InterruptHandler<I2C1>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | const DEV_ADDR: u8 = 0x42; | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { | ||
| 31 | info!("Device start"); | ||
| 32 | |||
| 33 | let mut count = 0xD0; | ||
| 34 | |||
| 35 | loop { | ||
| 36 | let mut buf = [0u8; 128]; | ||
| 37 | match dev.listen(&mut buf).await { | ||
| 38 | Ok(i2c_slave::Command::GeneralCall(len)) => { | ||
| 39 | assert_eq!(buf[..len], [0xCA, 0x11], "recieving the general call failed"); | ||
| 40 | info!("General Call - OK"); | ||
| 41 | } | ||
| 42 | Ok(i2c_slave::Command::Read) => { | ||
| 43 | loop { | ||
| 44 | match dev.respond_to_read(&[count]).await { | ||
| 45 | Ok(x) => match x { | ||
| 46 | i2c_slave::ReadStatus::Done => break, | ||
| 47 | i2c_slave::ReadStatus::NeedMoreBytes => count += 1, | ||
| 48 | i2c_slave::ReadStatus::LeftoverBytes(x) => { | ||
| 49 | info!("tried to write {} extra bytes", x); | ||
| 50 | break; | ||
| 51 | } | ||
| 52 | }, | ||
| 53 | Err(e) => match e { | ||
| 54 | embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), | ||
| 55 | _ => panic!("{}", e), | ||
| 56 | }, | ||
| 57 | } | ||
| 58 | } | ||
| 59 | count += 1; | ||
| 60 | } | ||
| 61 | Ok(i2c_slave::Command::Write(len)) => match len { | ||
| 62 | 1 => { | ||
| 63 | assert_eq!(buf[..len], [0xAA], "recieving a single byte failed"); | ||
| 64 | info!("Single Byte Write - OK") | ||
| 65 | } | ||
| 66 | 4 => { | ||
| 67 | assert_eq!(buf[..len], [0xAA, 0xBB, 0xCC, 0xDD], "recieving 4 bytes failed"); | ||
| 68 | info!("4 Byte Write - OK") | ||
| 69 | } | ||
| 70 | 32 => { | ||
| 71 | assert_eq!( | ||
| 72 | buf[..len], | ||
| 73 | [ | ||
| 74 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, | ||
| 75 | 25, 26, 27, 28, 29, 30, 31 | ||
| 76 | ], | ||
| 77 | "recieving 32 bytes failed" | ||
| 78 | ); | ||
| 79 | info!("32 Byte Write - OK") | ||
| 80 | } | ||
| 81 | _ => panic!("Invalid write length {}", len), | ||
| 82 | }, | ||
| 83 | Ok(i2c_slave::Command::WriteRead(len)) => { | ||
| 84 | info!("device recieved write read: {:x}", buf[..len]); | ||
| 85 | match buf[0] { | ||
| 86 | 0xC2 => { | ||
| 87 | let resp_buff = [0xD1, 0xD2, 0xD3, 0xD4]; | ||
| 88 | dev.respond_to_read(&resp_buff).await.unwrap(); | ||
| 89 | } | ||
| 90 | 0xC8 => { | ||
| 91 | let mut resp_buff = [0u8; 32]; | ||
| 92 | for i in 0..32 { | ||
| 93 | resp_buff[i] = i as u8; | ||
| 94 | } | ||
| 95 | dev.respond_to_read(&resp_buff).await.unwrap(); | ||
| 96 | } | ||
| 97 | x => panic!("Invalid Write Read {:x}", x), | ||
| 98 | } | ||
| 99 | } | ||
| 100 | Err(e) => match e { | ||
| 101 | embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), | ||
| 102 | _ => panic!("{}", e), | ||
| 103 | }, | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | #[embassy_executor::task] | ||
| 109 | async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { | ||
| 110 | info!("Device start"); | ||
| 111 | |||
| 112 | { | ||
| 113 | let buf = [0xCA, 0x11]; | ||
| 114 | con.write(0u16, &buf).await.unwrap(); | ||
| 115 | info!("Controler general call write"); | ||
| 116 | embassy_futures::yield_now().await; | ||
| 117 | } | ||
| 118 | |||
| 119 | { | ||
| 120 | let mut buf = [0u8]; | ||
| 121 | con.read(DEV_ADDR, &mut buf).await.unwrap(); | ||
| 122 | assert_eq!(buf, [0xD0], "single byte read failed"); | ||
| 123 | info!("single byte read - OK"); | ||
| 124 | embassy_futures::yield_now().await; | ||
| 125 | } | ||
| 126 | |||
| 127 | { | ||
| 128 | let mut buf = [0u8; 4]; | ||
| 129 | con.read(DEV_ADDR, &mut buf).await.unwrap(); | ||
| 130 | assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], "single byte read failed"); | ||
| 131 | info!("4 byte read - OK"); | ||
| 132 | embassy_futures::yield_now().await; | ||
| 133 | } | ||
| 134 | |||
| 135 | { | ||
| 136 | let buf = [0xAA]; | ||
| 137 | con.write(DEV_ADDR, &buf).await.unwrap(); | ||
| 138 | info!("Controler single byte write"); | ||
| 139 | embassy_futures::yield_now().await; | ||
| 140 | } | ||
| 141 | |||
| 142 | { | ||
| 143 | let buf = [0xAA, 0xBB, 0xCC, 0xDD]; | ||
| 144 | con.write(DEV_ADDR, &buf).await.unwrap(); | ||
| 145 | info!("Controler 4 byte write"); | ||
| 146 | embassy_futures::yield_now().await; | ||
| 147 | } | ||
| 148 | |||
| 149 | { | ||
| 150 | let mut buf = [0u8; 32]; | ||
| 151 | for i in 0..32 { | ||
| 152 | buf[i] = i as u8; | ||
| 153 | } | ||
| 154 | con.write(DEV_ADDR, &buf).await.unwrap(); | ||
| 155 | info!("Controler 32 byte write"); | ||
| 156 | embassy_futures::yield_now().await; | ||
| 157 | } | ||
| 158 | |||
| 159 | { | ||
| 160 | let mut buf = [0u8; 4]; | ||
| 161 | let mut ops = [Operation::Write(&[0xC2]), Operation::Read(&mut buf)]; | ||
| 162 | con.transaction(DEV_ADDR, &mut ops).await.unwrap(); | ||
| 163 | assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], "write_read failed"); | ||
| 164 | info!("write_read - OK"); | ||
| 165 | embassy_futures::yield_now().await; | ||
| 166 | } | ||
| 167 | |||
| 168 | { | ||
| 169 | let mut buf = [0u8; 32]; | ||
| 170 | let mut ops = [Operation::Write(&[0xC8]), Operation::Read(&mut buf)]; | ||
| 171 | con.transaction(DEV_ADDR, &mut ops).await.unwrap(); | ||
| 172 | assert_eq!( | ||
| 173 | buf, | ||
| 174 | [ | ||
| 175 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, | ||
| 176 | 28, 29, 30, 31 | ||
| 177 | ], | ||
| 178 | "write_read of 32 bytes failed" | ||
| 179 | ); | ||
| 180 | info!("large write_read - OK") | ||
| 181 | } | ||
| 182 | |||
| 183 | info!("Test OK"); | ||
| 184 | cortex_m::asm::bkpt(); | ||
| 185 | } | ||
| 186 | |||
| 187 | #[cortex_m_rt::entry] | ||
| 188 | fn main() -> ! { | ||
| 189 | let p = embassy_rp::init(Default::default()); | ||
| 190 | info!("Hello World!"); | ||
| 191 | |||
| 192 | let d_sda = p.PIN_19; | ||
| 193 | let d_scl = p.PIN_18; | ||
| 194 | let mut config = i2c_slave::Config::default(); | ||
| 195 | config.addr = DEV_ADDR as u16; | ||
| 196 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); | ||
| 197 | |||
| 198 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 199 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 200 | executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); | ||
| 201 | }); | ||
| 202 | |||
| 203 | let executor0 = EXECUTOR0.init(Executor::new()); | ||
| 204 | |||
| 205 | let c_sda = p.PIN_21; | ||
| 206 | let c_scl = p.PIN_20; | ||
| 207 | let mut config = i2c::Config::default(); | ||
| 208 | config.frequency = 5_000; | ||
| 209 | let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); | ||
| 210 | |||
| 211 | executor0.run(|spawner| unwrap!(spawner.spawn(controller_task(controller)))); | ||
| 212 | } | ||
