diff options
| -rw-r--r-- | embassy-rp/src/i2c.rs | 84 | ||||
| -rw-r--r-- | embassy-rp/src/i2c_slave.rs | 115 | ||||
| -rw-r--r-- | tests/rp/Cargo.toml | 1 | ||||
| -rw-r--r-- | tests/rp/src/bin/i2c.rs | 94 |
4 files changed, 177 insertions, 117 deletions
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 74d015792..ac0eac96d 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs | |||
| @@ -43,6 +43,18 @@ pub enum Error { | |||
| 43 | AddressReserved(u16), | 43 | AddressReserved(u16), |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | /// I2C Config error | ||
| 47 | #[derive(Debug, PartialEq, Eq)] | ||
| 48 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 49 | pub enum ConfigError { | ||
| 50 | /// Max i2c speed is 1MHz | ||
| 51 | FrequencyTooHigh, | ||
| 52 | /// The sys clock is too slow to support given frequency | ||
| 53 | ClockTooSlow, | ||
| 54 | /// The sys clock is too fast to support given frequency | ||
| 55 | ClockTooFast, | ||
| 56 | } | ||
| 57 | |||
| 46 | /// I2C config. | 58 | /// I2C config. |
| 47 | #[non_exhaustive] | 59 | #[non_exhaustive] |
| 48 | #[derive(Copy, Clone)] | 60 | #[derive(Copy, Clone)] |
| @@ -365,37 +377,32 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 365 | ) -> Self { | 377 | ) -> Self { |
| 366 | into_ref!(_peri); | 378 | into_ref!(_peri); |
| 367 | 379 | ||
| 368 | assert!(config.frequency <= 1_000_000); | ||
| 369 | assert!(config.frequency > 0); | ||
| 370 | |||
| 371 | let p = T::regs(); | ||
| 372 | |||
| 373 | let reset = T::reset(); | 380 | let reset = T::reset(); |
| 374 | crate::reset::reset(reset); | 381 | crate::reset::reset(reset); |
| 375 | crate::reset::unreset_wait(reset); | 382 | crate::reset::unreset_wait(reset); |
| 376 | 383 | ||
| 377 | p.ic_enable().write(|w| w.set_enable(false)); | ||
| 378 | |||
| 379 | // Select controller mode & speed | ||
| 380 | p.ic_con().modify(|w| { | ||
| 381 | // Always use "fast" mode (<= 400 kHz, works fine for standard | ||
| 382 | // mode too) | ||
| 383 | w.set_speed(i2c::vals::Speed::FAST); | ||
| 384 | w.set_master_mode(true); | ||
| 385 | w.set_ic_slave_disable(true); | ||
| 386 | w.set_ic_restart_en(true); | ||
| 387 | w.set_tx_empty_ctrl(true); | ||
| 388 | }); | ||
| 389 | |||
| 390 | // Set FIFO watermarks to 1 to make things simpler. This is encoded | ||
| 391 | // by a register value of 0. | ||
| 392 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); | ||
| 393 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | ||
| 394 | |||
| 395 | // Configure SCL & SDA pins | 384 | // Configure SCL & SDA pins |
| 396 | set_up_i2c_pin(&scl); | 385 | set_up_i2c_pin(&scl); |
| 397 | set_up_i2c_pin(&sda); | 386 | set_up_i2c_pin(&sda); |
| 398 | 387 | ||
| 388 | let mut me = Self { phantom: PhantomData }; | ||
| 389 | |||
| 390 | if let Err(e) = me.set_config_inner(&config) { | ||
| 391 | panic!("Error configuring i2c: {:?}", e); | ||
| 392 | } | ||
| 393 | |||
| 394 | me | ||
| 395 | } | ||
| 396 | |||
| 397 | fn set_config_inner(&mut self, config: &Config) -> Result<(), ConfigError> { | ||
| 398 | if config.frequency > 1_000_000 { | ||
| 399 | return Err(ConfigError::FrequencyTooHigh); | ||
| 400 | } | ||
| 401 | |||
| 402 | let p = T::regs(); | ||
| 403 | |||
| 404 | p.ic_enable().write(|w| w.set_enable(false)); | ||
| 405 | |||
| 399 | // Configure baudrate | 406 | // Configure baudrate |
| 400 | 407 | ||
| 401 | // There are some subtleties to I2C timing which we are completely | 408 | // There are some subtleties to I2C timing which we are completely |
| @@ -408,10 +415,12 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 408 | let hcnt = period - lcnt; // and 2/5 (40%) of the period high | 415 | let hcnt = period - lcnt; // and 2/5 (40%) of the period high |
| 409 | 416 | ||
| 410 | // Check for out-of-range divisors: | 417 | // Check for out-of-range divisors: |
| 411 | assert!(hcnt <= 0xffff); | 418 | if hcnt > 0xffff || lcnt > 0xffff { |
| 412 | assert!(lcnt <= 0xffff); | 419 | return Err(ConfigError::ClockTooFast); |
| 413 | assert!(hcnt >= 8); | 420 | } |
| 414 | assert!(lcnt >= 8); | 421 | if hcnt < 8 || lcnt < 8 { |
| 422 | return Err(ConfigError::ClockTooSlow); | ||
| 423 | } | ||
| 415 | 424 | ||
| 416 | // Per I2C-bus specification a device in standard or fast mode must | 425 | // Per I2C-bus specification a device in standard or fast mode must |
| 417 | // internally provide a hold time of at least 300ns for the SDA | 426 | // internally provide a hold time of at least 300ns for the SDA |
| @@ -424,14 +433,19 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 424 | ((clk_base * 3) / 10_000_000) + 1 | 433 | ((clk_base * 3) / 10_000_000) + 1 |
| 425 | } else { | 434 | } else { |
| 426 | // fast mode plus requires a clk_base > 32MHz | 435 | // fast mode plus requires a clk_base > 32MHz |
| 427 | assert!(clk_base >= 32_000_000); | 436 | if clk_base <= 32_000_000 { |
| 437 | return Err(ConfigError::ClockTooSlow); | ||
| 438 | } | ||
| 428 | 439 | ||
| 429 | // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / | 440 | // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / |
| 430 | // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't | 441 | // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't |
| 431 | // fit in uint. Add 1 to avoid division truncation. | 442 | // fit in uint. Add 1 to avoid division truncation. |
| 432 | ((clk_base * 3) / 25_000_000) + 1 | 443 | ((clk_base * 3) / 25_000_000) + 1 |
| 433 | }; | 444 | }; |
| 434 | assert!(sda_tx_hold_count <= lcnt - 2); | 445 | |
| 446 | if sda_tx_hold_count > lcnt - 2 { | ||
| 447 | return Err(ConfigError::ClockTooSlow); | ||
| 448 | } | ||
| 435 | 449 | ||
| 436 | p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); | 450 | p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); |
| 437 | p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); | 451 | p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); |
| @@ -440,10 +454,9 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 440 | p.ic_sda_hold() | 454 | p.ic_sda_hold() |
| 441 | .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); | 455 | .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); |
| 442 | 456 | ||
| 443 | // Enable I2C block | ||
| 444 | p.ic_enable().write(|w| w.set_enable(true)); | 457 | p.ic_enable().write(|w| w.set_enable(true)); |
| 445 | 458 | ||
| 446 | Self { phantom: PhantomData } | 459 | Ok(()) |
| 447 | } | 460 | } |
| 448 | 461 | ||
| 449 | fn setup(addr: u16) -> Result<(), Error> { | 462 | fn setup(addr: u16) -> Result<(), Error> { |
| @@ -757,6 +770,15 @@ where | |||
| 757 | } | 770 | } |
| 758 | } | 771 | } |
| 759 | 772 | ||
| 773 | impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> { | ||
| 774 | type Config = Config; | ||
| 775 | type ConfigError = ConfigError; | ||
| 776 | |||
| 777 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | ||
| 778 | self.set_config_inner(config) | ||
| 779 | } | ||
| 780 | } | ||
| 781 | |||
| 760 | /// Check if address is reserved. | 782 | /// Check if address is reserved. |
| 761 | pub fn i2c_reserved_addr(addr: u16) -> bool { | 783 | pub fn i2c_reserved_addr(addr: u16) -> bool { |
| 762 | ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 | 784 | ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 |
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index 979686627..97ca17295 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs | |||
| @@ -83,6 +83,7 @@ impl Default for Config { | |||
| 83 | pub struct I2cSlave<'d, T: Instance> { | 83 | pub struct I2cSlave<'d, T: Instance> { |
| 84 | phantom: PhantomData<&'d mut T>, | 84 | phantom: PhantomData<&'d mut T>, |
| 85 | pending_byte: Option<u8>, | 85 | pending_byte: Option<u8>, |
| 86 | config: Config, | ||
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | impl<'d, T: Instance> I2cSlave<'d, T> { | 89 | impl<'d, T: Instance> I2cSlave<'d, T> { |
| @@ -99,6 +100,25 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 99 | assert!(!i2c_reserved_addr(config.addr)); | 100 | assert!(!i2c_reserved_addr(config.addr)); |
| 100 | assert!(config.addr != 0); | 101 | assert!(config.addr != 0); |
| 101 | 102 | ||
| 103 | // Configure SCL & SDA pins | ||
| 104 | set_up_i2c_pin(&scl); | ||
| 105 | set_up_i2c_pin(&sda); | ||
| 106 | |||
| 107 | let mut ret = Self { | ||
| 108 | phantom: PhantomData, | ||
| 109 | pending_byte: None, | ||
| 110 | config, | ||
| 111 | }; | ||
| 112 | |||
| 113 | ret.reset(); | ||
| 114 | |||
| 115 | ret | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus. | ||
| 119 | /// You can recover the bus by calling this function, but doing so will almost certainly cause | ||
| 120 | /// an i/o error in the master. | ||
| 121 | pub fn reset(&mut self) { | ||
| 102 | let p = T::regs(); | 122 | let p = T::regs(); |
| 103 | 123 | ||
| 104 | let reset = T::reset(); | 124 | let reset = T::reset(); |
| @@ -107,7 +127,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 107 | 127 | ||
| 108 | p.ic_enable().write(|w| w.set_enable(false)); | 128 | p.ic_enable().write(|w| w.set_enable(false)); |
| 109 | 129 | ||
| 110 | p.ic_sar().write(|w| w.set_ic_sar(config.addr)); | 130 | p.ic_sar().write(|w| w.set_ic_sar(self.config.addr)); |
| 111 | p.ic_con().modify(|w| { | 131 | p.ic_con().modify(|w| { |
| 112 | w.set_master_mode(false); | 132 | w.set_master_mode(false); |
| 113 | w.set_ic_slave_disable(false); | 133 | w.set_ic_slave_disable(false); |
| @@ -121,10 +141,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 121 | // Generate stop interrupts for general calls | 141 | // Generate stop interrupts for general calls |
| 122 | // This also causes stop interrupts for other devices on the bus but those will not be | 142 | // This also causes stop interrupts for other devices on the bus but those will not be |
| 123 | // propagated up to the application. | 143 | // propagated up to the application. |
| 124 | w.set_stop_det_ifaddressed(!config.general_call); | 144 | w.set_stop_det_ifaddressed(!self.config.general_call); |
| 125 | }); | 145 | }); |
| 126 | p.ic_ack_general_call() | 146 | p.ic_ack_general_call() |
| 127 | .write(|w| w.set_ack_gen_call(config.general_call)); | 147 | .write(|w| w.set_ack_gen_call(self.config.general_call)); |
| 128 | 148 | ||
| 129 | // Set FIFO watermarks to 1 to make things simpler. This is encoded | 149 | // Set FIFO watermarks to 1 to make things simpler. This is encoded |
| 130 | // by a register value of 0. Rx watermark should never change, but Tx watermark will be | 150 | // by a register value of 0. Rx watermark should never change, but Tx watermark will be |
| @@ -132,10 +152,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 132 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); | 152 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); |
| 133 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | 153 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); |
| 134 | 154 | ||
| 135 | // Configure SCL & SDA pins | ||
| 136 | set_up_i2c_pin(&scl); | ||
| 137 | set_up_i2c_pin(&sda); | ||
| 138 | |||
| 139 | // Clear interrupts | 155 | // Clear interrupts |
| 140 | p.ic_clr_intr().read(); | 156 | p.ic_clr_intr().read(); |
| 141 | 157 | ||
| @@ -146,11 +162,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 146 | p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); | 162 | p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); |
| 147 | T::Interrupt::unpend(); | 163 | T::Interrupt::unpend(); |
| 148 | unsafe { T::Interrupt::enable() }; | 164 | unsafe { T::Interrupt::enable() }; |
| 149 | |||
| 150 | Self { | ||
| 151 | phantom: PhantomData, | ||
| 152 | pending_byte: None, | ||
| 153 | } | ||
| 154 | } | 165 | } |
| 155 | 166 | ||
| 156 | /// Calls `f` to check if we are ready or not. | 167 | /// Calls `f` to check if we are ready or not. |
| @@ -178,15 +189,13 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 178 | fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) { | 189 | fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) { |
| 179 | let p = T::regs(); | 190 | let p = T::regs(); |
| 180 | 191 | ||
| 181 | for b in &mut buffer[*offset..] { | 192 | if let Some(pending) = self.pending_byte.take() { |
| 182 | if let Some(pending) = self.pending_byte.take() { | 193 | buffer[*offset] = pending; |
| 183 | *b = pending; | 194 | *offset += 1; |
| 184 | *offset += 1; | 195 | } |
| 185 | continue; | ||
| 186 | } | ||
| 187 | 196 | ||
| 188 | let status = p.ic_status().read(); | 197 | for b in &mut buffer[*offset..] { |
| 189 | if !status.rfne() { | 198 | if !p.ic_status().read().rfne() { |
| 190 | break; | 199 | break; |
| 191 | } | 200 | } |
| 192 | 201 | ||
| @@ -207,14 +216,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 207 | } | 216 | } |
| 208 | } | 217 | } |
| 209 | 218 | ||
| 210 | #[inline(always)] | ||
| 211 | fn write_to_fifo(&mut self, buffer: &[u8]) { | ||
| 212 | let p = T::regs(); | ||
| 213 | for byte in buffer { | ||
| 214 | p.ic_data_cmd().write(|w| w.set_dat(*byte)); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | /// Wait asynchronously for commands from an I2C master. | 219 | /// Wait asynchronously for commands from an I2C master. |
| 219 | /// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'. | 220 | /// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'. |
| 220 | pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { | 221 | pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { |
| @@ -227,8 +228,9 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 227 | self.wait_on( | 228 | self.wait_on( |
| 228 | |me| { | 229 | |me| { |
| 229 | let stat = p.ic_raw_intr_stat().read(); | 230 | let stat = p.ic_raw_intr_stat().read(); |
| 231 | trace!("ls:{:013b} len:{}", stat.0, len); | ||
| 230 | 232 | ||
| 231 | if p.ic_rxflr().read().rxflr() > 0 { | 233 | if p.ic_rxflr().read().rxflr() > 0 || me.pending_byte.is_some() { |
| 232 | me.drain_fifo(buffer, &mut len); | 234 | me.drain_fifo(buffer, &mut len); |
| 233 | // we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise | 235 | // we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise |
| 234 | p.ic_rx_tl().write(|w| w.set_rx_tl(11)); | 236 | p.ic_rx_tl().write(|w| w.set_rx_tl(11)); |
| @@ -241,6 +243,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 241 | return Poll::Ready(Err(Error::PartialWrite(buffer.len()))); | 243 | return Poll::Ready(Err(Error::PartialWrite(buffer.len()))); |
| 242 | } | 244 | } |
| 243 | } | 245 | } |
| 246 | trace!("len:{}, pend:{:?}", len, me.pending_byte); | ||
| 247 | if me.pending_byte.is_some() { | ||
| 248 | warn!("pending") | ||
| 249 | } | ||
| 244 | 250 | ||
| 245 | if stat.restart_det() && stat.rd_req() { | 251 | if stat.restart_det() && stat.rd_req() { |
| 246 | p.ic_clr_restart_det().read(); | 252 | p.ic_clr_restart_det().read(); |
| @@ -257,12 +263,17 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 257 | p.ic_clr_restart_det().read(); | 263 | p.ic_clr_restart_det().read(); |
| 258 | p.ic_clr_gen_call().read(); | 264 | p.ic_clr_gen_call().read(); |
| 259 | Poll::Ready(Ok(Command::Read)) | 265 | Poll::Ready(Ok(Command::Read)) |
| 266 | } else if stat.stop_det() { | ||
| 267 | // clear stuck stop bit | ||
| 268 | // This can happen if the SDA/SCL pullups are enabled after calling this func | ||
| 269 | p.ic_clr_stop_det().read(); | ||
| 270 | Poll::Pending | ||
| 260 | } else { | 271 | } else { |
| 261 | Poll::Pending | 272 | Poll::Pending |
| 262 | } | 273 | } |
| 263 | }, | 274 | }, |
| 264 | |_me| { | 275 | |_me| { |
| 265 | p.ic_intr_mask().modify(|w| { | 276 | p.ic_intr_mask().write(|w| { |
| 266 | w.set_m_stop_det(true); | 277 | w.set_m_stop_det(true); |
| 267 | w.set_m_restart_det(true); | 278 | w.set_m_restart_det(true); |
| 268 | w.set_m_gen_call(true); | 279 | w.set_m_gen_call(true); |
| @@ -286,27 +297,30 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 286 | 297 | ||
| 287 | self.wait_on( | 298 | self.wait_on( |
| 288 | |me| { | 299 | |me| { |
| 289 | if let Err(abort_reason) = me.read_and_clear_abort_reason() { | 300 | let stat = p.ic_raw_intr_stat().read(); |
| 290 | if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { | 301 | trace!("rs:{:013b}", stat.0); |
| 291 | p.ic_clr_intr().read(); | 302 | |
| 292 | return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); | 303 | if stat.tx_abrt() { |
| 293 | } else { | 304 | if let Err(abort_reason) = me.read_and_clear_abort_reason() { |
| 294 | return Poll::Ready(Err(abort_reason)); | 305 | if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { |
| 306 | p.ic_clr_intr().read(); | ||
| 307 | return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); | ||
| 308 | } else { | ||
| 309 | return Poll::Ready(Err(abort_reason)); | ||
| 310 | } | ||
| 295 | } | 311 | } |
| 296 | } | 312 | } |
| 297 | 313 | ||
| 298 | if let Some(chunk) = chunks.next() { | 314 | if let Some(chunk) = chunks.next() { |
| 299 | me.write_to_fifo(chunk); | 315 | for byte in chunk { |
| 300 | 316 | p.ic_clr_rd_req().read(); | |
| 301 | p.ic_clr_rd_req().read(); | 317 | p.ic_data_cmd().write(|w| w.set_dat(*byte)); |
| 318 | } | ||
| 302 | 319 | ||
| 303 | Poll::Pending | 320 | Poll::Pending |
| 304 | } else { | 321 | } else { |
| 305 | let stat = p.ic_raw_intr_stat().read(); | 322 | if stat.rx_done() { |
| 306 | |||
| 307 | if stat.rx_done() && stat.stop_det() { | ||
| 308 | p.ic_clr_rx_done().read(); | 323 | p.ic_clr_rx_done().read(); |
| 309 | p.ic_clr_stop_det().read(); | ||
| 310 | Poll::Ready(Ok(ReadStatus::Done)) | 324 | Poll::Ready(Ok(ReadStatus::Done)) |
| 311 | } else if stat.rd_req() && stat.tx_empty() { | 325 | } else if stat.rd_req() && stat.tx_empty() { |
| 312 | Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) | 326 | Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) |
| @@ -316,8 +330,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 316 | } | 330 | } |
| 317 | }, | 331 | }, |
| 318 | |_me| { | 332 | |_me| { |
| 319 | p.ic_intr_mask().modify(|w| { | 333 | p.ic_intr_mask().write(|w| { |
| 320 | w.set_m_stop_det(true); | ||
| 321 | w.set_m_rx_done(true); | 334 | w.set_m_rx_done(true); |
| 322 | w.set_m_tx_empty(true); | 335 | w.set_m_tx_empty(true); |
| 323 | w.set_m_tx_abrt(true); | 336 | w.set_m_tx_abrt(true); |
| @@ -329,9 +342,14 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 329 | 342 | ||
| 330 | /// Respond to reads with the fill byte until the controller stops asking | 343 | /// Respond to reads with the fill byte until the controller stops asking |
| 331 | pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { | 344 | pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { |
| 345 | // Send fill bytes a full fifo at a time, to reduce interrupt noise. | ||
| 346 | // This does mean we'll almost certainly abort the write, but since these are fill bytes, | ||
| 347 | // we don't care. | ||
| 348 | let buff = [fill; FIFO_SIZE as usize]; | ||
| 332 | loop { | 349 | loop { |
| 333 | match self.respond_to_read(&[fill]).await { | 350 | match self.respond_to_read(&buff).await { |
| 334 | Ok(ReadStatus::NeedMoreBytes) => (), | 351 | Ok(ReadStatus::NeedMoreBytes) => (), |
| 352 | Ok(ReadStatus::LeftoverBytes(_)) => break Ok(()), | ||
| 335 | Ok(_) => break Ok(()), | 353 | Ok(_) => break Ok(()), |
| 336 | Err(e) => break Err(e), | 354 | Err(e) => break Err(e), |
| 337 | } | 355 | } |
| @@ -353,10 +371,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> { | |||
| 353 | #[inline(always)] | 371 | #[inline(always)] |
| 354 | fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { | 372 | fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { |
| 355 | let p = T::regs(); | 373 | let p = T::regs(); |
| 356 | let mut abort_reason = p.ic_tx_abrt_source().read(); | 374 | let abort_reason = p.ic_tx_abrt_source().read(); |
| 357 | |||
| 358 | // Mask off master_dis | ||
| 359 | abort_reason.set_abrt_master_dis(false); | ||
| 360 | 375 | ||
| 361 | if abort_reason.0 != 0 { | 376 | if abort_reason.0 != 0 { |
| 362 | // Note clearing the abort flag also clears the reason, and this | 377 | // Note clearing the abort flag also clears the reason, and this |
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 46e1e9a5f..e67f2117d 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml | |||
| @@ -14,6 +14,7 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = [ "defmt | |||
| 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 14 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 15 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } | 15 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } |
| 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } | 16 | embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } |
| 17 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal/"} | ||
| 17 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } | 18 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } |
| 18 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } | 19 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } |
| 19 | perf-client = { path = "../perf-client" } | 20 | perf-client = { path = "../perf-client" } |
diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs index a0aed1a42..153b37999 100644 --- a/tests/rp/src/bin/i2c.rs +++ b/tests/rp/src/bin/i2c.rs | |||
| @@ -3,7 +3,10 @@ | |||
| 3 | teleprobe_meta::target!(b"rpi-pico"); | 3 | teleprobe_meta::target!(b"rpi-pico"); |
| 4 | 4 | ||
| 5 | use defmt::{assert_eq, info, panic, unwrap}; | 5 | use defmt::{assert_eq, info, panic, unwrap}; |
| 6 | use embassy_executor::Executor; | 6 | use embassy_embedded_hal::SetConfig; |
| 7 | use embassy_executor::{Executor, Spawner}; | ||
| 8 | use embassy_rp::clocks::{PllConfig, XoscConfig}; | ||
| 9 | use embassy_rp::config::Config as rpConfig; | ||
| 7 | use embassy_rp::multicore::{spawn_core1, Stack}; | 10 | use embassy_rp::multicore::{spawn_core1, Stack}; |
| 8 | use embassy_rp::peripherals::{I2C0, I2C1}; | 11 | use embassy_rp::peripherals::{I2C0, I2C1}; |
| 9 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; | 12 | use embassy_rp::{bind_interrupts, i2c, i2c_slave}; |
| @@ -13,7 +16,6 @@ use static_cell::StaticCell; | |||
| 13 | use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _}; |
| 14 | 17 | ||
| 15 | static mut CORE1_STACK: Stack<1024> = Stack::new(); | 18 | static mut CORE1_STACK: Stack<1024> = Stack::new(); |
| 16 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 17 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | 19 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); |
| 18 | 20 | ||
| 19 | use crate::i2c::AbortReason; | 21 | use crate::i2c::AbortReason; |
| @@ -44,10 +46,7 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { | |||
| 44 | Ok(x) => match x { | 46 | Ok(x) => match x { |
| 45 | i2c_slave::ReadStatus::Done => break, | 47 | i2c_slave::ReadStatus::Done => break, |
| 46 | i2c_slave::ReadStatus::NeedMoreBytes => count += 1, | 48 | i2c_slave::ReadStatus::NeedMoreBytes => count += 1, |
| 47 | i2c_slave::ReadStatus::LeftoverBytes(x) => { | 49 | i2c_slave::ReadStatus::LeftoverBytes(x) => panic!("tried to write {} extra bytes", x), |
| 48 | info!("tried to write {} extra bytes", x); | ||
| 49 | break; | ||
| 50 | } | ||
| 51 | }, | 50 | }, |
| 52 | Err(e) => match e { | 51 | Err(e) => match e { |
| 53 | embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), | 52 | embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n), |
| @@ -92,6 +91,8 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { | |||
| 92 | resp_buff[i] = i as u8; | 91 | resp_buff[i] = i as u8; |
| 93 | } | 92 | } |
| 94 | dev.respond_to_read(&resp_buff).await.unwrap(); | 93 | dev.respond_to_read(&resp_buff).await.unwrap(); |
| 94 | // reset count for next round of tests | ||
| 95 | count = 0xD0; | ||
| 95 | } | 96 | } |
| 96 | x => panic!("Invalid Write Read {:x}", x), | 97 | x => panic!("Invalid Write Read {:x}", x), |
| 97 | } | 98 | } |
| @@ -104,8 +105,7 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! { | |||
| 104 | } | 105 | } |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | #[embassy_executor::task] | 108 | async fn controller_task(con: &mut i2c::I2c<'static, I2C0, i2c::Async>) { |
| 108 | async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { | ||
| 109 | info!("Device start"); | 109 | info!("Device start"); |
| 110 | 110 | ||
| 111 | { | 111 | { |
| @@ -179,33 +179,55 @@ async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) { | |||
| 179 | info!("large write_read - OK") | 179 | info!("large write_read - OK") |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | info!("Test OK"); | 182 | #[embassy_executor::main] |
| 183 | cortex_m::asm::bkpt(); | 183 | async fn main(_core0_spawner: Spawner) { |
| 184 | } | 184 | let mut config = rpConfig::default(); |
| 185 | 185 | // Configure clk_sys to 48MHz to support 1kHz scl. | |
| 186 | #[cortex_m_rt::entry] | 186 | // In theory it can go lower, but we won't bother to test below 1kHz. |
| 187 | fn main() -> ! { | 187 | config.clocks.xosc = Some(XoscConfig { |
| 188 | let p = embassy_rp::init(Default::default()); | 188 | hz: 12_000_000, |
| 189 | info!("Hello World!"); | 189 | delay_multiplier: 128, |
| 190 | 190 | sys_pll: Some(PllConfig { | |
| 191 | let d_sda = p.PIN_19; | 191 | refdiv: 1, |
| 192 | let d_scl = p.PIN_18; | 192 | fbdiv: 120, |
| 193 | let mut config = i2c_slave::Config::default(); | 193 | post_div1: 6, |
| 194 | config.addr = DEV_ADDR as u16; | 194 | post_div2: 5, |
| 195 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); | 195 | }), |
| 196 | 196 | usb_pll: Some(PllConfig { | |
| 197 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | 197 | refdiv: 1, |
| 198 | let executor1 = EXECUTOR1.init(Executor::new()); | 198 | fbdiv: 120, |
| 199 | executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); | 199 | post_div1: 6, |
| 200 | }); | 200 | post_div2: 5, |
| 201 | 201 | }), | |
| 202 | let executor0 = EXECUTOR0.init(Executor::new()); | 202 | }); |
| 203 | 203 | ||
| 204 | let c_sda = p.PIN_21; | 204 | let p = embassy_rp::init(config); |
| 205 | let c_scl = p.PIN_20; | 205 | info!("Hello World!"); |
| 206 | let mut config = i2c::Config::default(); | 206 | |
| 207 | config.frequency = 5_000; | 207 | let d_sda = p.PIN_19; |
| 208 | let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); | 208 | let d_scl = p.PIN_18; |
| 209 | let mut config = i2c_slave::Config::default(); | ||
| 210 | config.addr = DEV_ADDR as u16; | ||
| 211 | let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); | ||
| 212 | |||
| 213 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 214 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 215 | executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); | ||
| 216 | }); | ||
| 217 | |||
| 218 | let c_sda = p.PIN_21; | ||
| 219 | let c_scl = p.PIN_20; | ||
| 220 | let mut controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, Default::default()); | ||
| 221 | |||
| 222 | for freq in [1000, 100_000, 400_000, 1_000_000] { | ||
| 223 | info!("testing at {}hz", freq); | ||
| 224 | let mut config = i2c::Config::default(); | ||
| 225 | config.frequency = freq; | ||
| 226 | controller.set_config(&config).unwrap(); | ||
| 227 | controller_task(&mut controller).await; | ||
| 228 | } | ||
| 209 | 229 | ||
| 210 | executor0.run(|spawner| unwrap!(spawner.spawn(controller_task(controller)))); | 230 | info!("Test OK"); |
| 231 | cortex_m::asm::bkpt(); | ||
| 232 | } | ||
| 211 | } | 233 | } |
