diff options
| -rw-r--r-- | embassy-rp/src/i2c.rs | 86 | ||||
| -rw-r--r-- | tests/rp/Cargo.toml | 1 | ||||
| -rw-r--r-- | tests/rp/src/bin/i2c.rs | 94 |
3 files changed, 114 insertions, 67 deletions
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 74d015792..85636f5fe 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 |
| @@ -407,11 +414,14 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 407 | let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low | 414 | let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low |
| 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 | ||
| 417 | warn!("cb:{} h:{:x} l:{:x}", clk_base, hcnt, lcnt); | ||
| 410 | // Check for out-of-range divisors: | 418 | // Check for out-of-range divisors: |
| 411 | assert!(hcnt <= 0xffff); | 419 | if hcnt > 0xffff || lcnt > 0xffff { |
| 412 | assert!(lcnt <= 0xffff); | 420 | return Err(ConfigError::ClockTooFast); |
| 413 | assert!(hcnt >= 8); | 421 | } |
| 414 | assert!(lcnt >= 8); | 422 | if hcnt < 8 || lcnt < 8 { |
| 423 | return Err(ConfigError::ClockTooSlow); | ||
| 424 | } | ||
| 415 | 425 | ||
| 416 | // Per I2C-bus specification a device in standard or fast mode must | 426 | // 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 | 427 | // internally provide a hold time of at least 300ns for the SDA |
| @@ -424,14 +434,20 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 424 | ((clk_base * 3) / 10_000_000) + 1 | 434 | ((clk_base * 3) / 10_000_000) + 1 |
| 425 | } else { | 435 | } else { |
| 426 | // fast mode plus requires a clk_base > 32MHz | 436 | // fast mode plus requires a clk_base > 32MHz |
| 427 | assert!(clk_base >= 32_000_000); | 437 | if clk_base <= 32_000_000 { |
| 438 | return Err(ConfigError::ClockTooSlow); | ||
| 439 | } | ||
| 428 | 440 | ||
| 429 | // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / | 441 | // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / |
| 430 | // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't | 442 | // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't |
| 431 | // fit in uint. Add 1 to avoid division truncation. | 443 | // fit in uint. Add 1 to avoid division truncation. |
| 432 | ((clk_base * 3) / 25_000_000) + 1 | 444 | ((clk_base * 3) / 25_000_000) + 1 |
| 433 | }; | 445 | }; |
| 434 | assert!(sda_tx_hold_count <= lcnt - 2); | 446 | /* |
| 447 | if sda_tx_hold_count <= lcnt - 2 { | ||
| 448 | return Err(ConfigError::HoldCountOutOfRange); | ||
| 449 | } | ||
| 450 | */ | ||
| 435 | 451 | ||
| 436 | p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); | 452 | 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)); | 453 | p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); |
| @@ -440,10 +456,9 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { | |||
| 440 | p.ic_sda_hold() | 456 | p.ic_sda_hold() |
| 441 | .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); | 457 | .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); |
| 442 | 458 | ||
| 443 | // Enable I2C block | ||
| 444 | p.ic_enable().write(|w| w.set_enable(true)); | 459 | p.ic_enable().write(|w| w.set_enable(true)); |
| 445 | 460 | ||
| 446 | Self { phantom: PhantomData } | 461 | Ok(()) |
| 447 | } | 462 | } |
| 448 | 463 | ||
| 449 | fn setup(addr: u16) -> Result<(), Error> { | 464 | fn setup(addr: u16) -> Result<(), Error> { |
| @@ -757,6 +772,15 @@ where | |||
| 757 | } | 772 | } |
| 758 | } | 773 | } |
| 759 | 774 | ||
| 775 | impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> { | ||
| 776 | type Config = Config; | ||
| 777 | type ConfigError = ConfigError; | ||
| 778 | |||
| 779 | fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { | ||
| 780 | self.set_config_inner(config) | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 760 | /// Check if address is reserved. | 784 | /// Check if address is reserved. |
| 761 | pub fn i2c_reserved_addr(addr: u16) -> bool { | 785 | pub fn i2c_reserved_addr(addr: u16) -> bool { |
| 762 | ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 | 786 | ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 |
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 | } |
