diff options
| -rw-r--r-- | embassy-rp/src/i2c.rs | 306 |
1 files changed, 286 insertions, 20 deletions
diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 4a27ee8df..f7e6a6f6b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs | |||
| @@ -39,12 +39,15 @@ impl Default for Config { | |||
| 39 | } | 39 | } |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | const TX_FIFO_SIZE: u8 = 16; | ||
| 43 | const RX_FIFO_SIZE: u8 = 16; | ||
| 44 | |||
| 42 | pub struct I2c<'d, T: Instance, M: Mode> { | 45 | pub struct I2c<'d, T: Instance, M: Mode> { |
| 43 | phantom: PhantomData<(&'d mut T, M)>, | 46 | phantom: PhantomData<(&'d mut T, M)>, |
| 44 | } | 47 | } |
| 45 | 48 | ||
| 46 | impl<'d, T: Instance> I2c<'d, T, Master> { | 49 | impl<'d, T: Instance> I2c<'d, T, Blocking> { |
| 47 | pub fn new_master( | 50 | pub fn new_blocking( |
| 48 | _peri: impl Peripheral<P = T> + 'd, | 51 | _peri: impl Peripheral<P = T> + 'd, |
| 49 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, | 52 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, |
| 50 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | 53 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, |
| @@ -60,9 +63,10 @@ impl<'d, T: Instance> I2c<'d, T, Master> { | |||
| 60 | unsafe { | 63 | unsafe { |
| 61 | p.ic_enable().write(|w| w.set_enable(false)); | 64 | p.ic_enable().write(|w| w.set_enable(false)); |
| 62 | 65 | ||
| 63 | // select controller mode & speed | 66 | // Select controller mode & speed |
| 64 | p.ic_con().write(|w| { | 67 | p.ic_con().write(|w| { |
| 65 | // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) | 68 | // Always use "fast" mode (<= 400 kHz, works fine for standard |
| 69 | // mode too) | ||
| 66 | w.set_speed(i2c::vals::Speed::FAST); | 70 | w.set_speed(i2c::vals::Speed::FAST); |
| 67 | w.set_master_mode(true); | 71 | w.set_master_mode(true); |
| 68 | w.set_ic_slave_disable(true); | 72 | w.set_ic_slave_disable(true); |
| @@ -70,7 +74,8 @@ impl<'d, T: Instance> I2c<'d, T, Master> { | |||
| 70 | w.set_tx_empty_ctrl(true); | 74 | w.set_tx_empty_ctrl(true); |
| 71 | }); | 75 | }); |
| 72 | 76 | ||
| 73 | // Clear FIFO threshold | 77 | // Set FIFO watermarks to 1 to make things simpler. This is encoded |
| 78 | // by a register value of 0. | ||
| 74 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); | 79 | p.ic_tx_tl().write(|w| w.set_tx_tl(0)); |
| 75 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); | 80 | p.ic_rx_tl().write(|w| w.set_rx_tl(0)); |
| 76 | 81 | ||
| @@ -89,8 +94,9 @@ impl<'d, T: Instance> I2c<'d, T, Master> { | |||
| 89 | 94 | ||
| 90 | // Configure baudrate | 95 | // Configure baudrate |
| 91 | 96 | ||
| 92 | // There are some subtleties to I2C timing which we are completely ignoring here | 97 | // There are some subtleties to I2C timing which we are completely |
| 93 | // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 | 98 | // ignoring here See: |
| 99 | // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 | ||
| 94 | let clk_base = crate::clocks::clk_sys_freq(); | 100 | let clk_base = crate::clocks::clk_sys_freq(); |
| 95 | 101 | ||
| 96 | let period = (clk_base + config.frequency / 2) / config.frequency; | 102 | let period = (clk_base + config.frequency / 2) / config.frequency; |
| @@ -104,21 +110,21 @@ impl<'d, T: Instance> I2c<'d, T, Master> { | |||
| 104 | assert!(lcnt >= 8); | 110 | assert!(lcnt >= 8); |
| 105 | 111 | ||
| 106 | // Per I2C-bus specification a device in standard or fast mode must | 112 | // Per I2C-bus specification a device in standard or fast mode must |
| 107 | // internally provide a hold time of at least 300ns for the SDA signal to | 113 | // internally provide a hold time of at least 300ns for the SDA |
| 108 | // bridge the undefined region of the falling edge of SCL. A smaller hold | 114 | // signal to bridge the undefined region of the falling edge of SCL. |
| 109 | // time of 120ns is used for fast mode plus. | 115 | // A smaller hold time of 120ns is used for fast mode plus. |
| 110 | let sda_tx_hold_count = if config.frequency < 1_000_000 { | 116 | let sda_tx_hold_count = if config.frequency < 1_000_000 { |
| 111 | // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) | 117 | // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / |
| 112 | // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. | 118 | // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't |
| 113 | // Add 1 to avoid division truncation. | 119 | // fit in uint. Add 1 to avoid division truncation. |
| 114 | ((clk_base * 3) / 10_000_000) + 1 | 120 | ((clk_base * 3) / 10_000_000) + 1 |
| 115 | } else { | 121 | } else { |
| 116 | // fast mode plus requires a clk_base > 32MHz | 122 | // fast mode plus requires a clk_base > 32MHz |
| 117 | assert!(clk_base >= 32_000_000); | 123 | assert!(clk_base >= 32_000_000); |
| 118 | 124 | ||
| 119 | // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) | 125 | // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / |
| 120 | // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. | 126 | // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't |
| 121 | // Add 1 to avoid division truncation. | 127 | // fit in uint. Add 1 to avoid division truncation. |
| 122 | ((clk_base * 3) / 25_000_000) + 1 | 128 | ((clk_base * 3) / 25_000_000) + 1 |
| 123 | }; | 129 | }; |
| 124 | assert!(sda_tx_hold_count <= lcnt - 2); | 130 | assert!(sda_tx_hold_count <= lcnt - 2); |
| @@ -138,6 +144,266 @@ impl<'d, T: Instance> I2c<'d, T, Master> { | |||
| 138 | } | 144 | } |
| 139 | } | 145 | } |
| 140 | 146 | ||
| 147 | impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | ||
| 148 | /// Number of bytes currently in the RX FIFO | ||
| 149 | #[inline] | ||
| 150 | pub fn rx_fifo_used(&self) -> u8 { | ||
| 151 | unsafe { T::regs().ic_rxflr().read().rxflr() } | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Remaining capacity in the RX FIFO | ||
| 155 | #[inline] | ||
| 156 | pub fn rx_fifo_free(&self) -> u8 { | ||
| 157 | RX_FIFO_SIZE - self.rx_fifo_used() | ||
| 158 | } | ||
| 159 | |||
| 160 | /// RX FIFO is empty | ||
| 161 | #[inline] | ||
| 162 | pub fn rx_fifo_empty(&self) -> bool { | ||
| 163 | self.rx_fifo_used() == 0 | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Number of bytes currently in the TX FIFO | ||
| 167 | #[inline] | ||
| 168 | pub fn tx_fifo_used(&self) -> u8 { | ||
| 169 | unsafe { T::regs().ic_txflr().read().txflr() } | ||
| 170 | } | ||
| 171 | |||
| 172 | /// Remaining capacity in the TX FIFO | ||
| 173 | #[inline] | ||
| 174 | pub fn tx_fifo_free(&self) -> u8 { | ||
| 175 | TX_FIFO_SIZE - self.tx_fifo_used() | ||
| 176 | } | ||
| 177 | |||
| 178 | /// TX FIFO is at capacity | ||
| 179 | #[inline] | ||
| 180 | pub fn tx_fifo_full(&self) -> bool { | ||
| 181 | self.tx_fifo_free() == 0 | ||
| 182 | } | ||
| 183 | |||
| 184 | fn setup(addr: u16) -> Result<(), Error> { | ||
| 185 | if addr >= 0x80 { | ||
| 186 | return Err(Error::AddressOutOfRange(addr)); | ||
| 187 | } | ||
| 188 | |||
| 189 | if i2c_reserved_addr(addr) { | ||
| 190 | return Err(Error::AddressReserved(addr)); | ||
| 191 | } | ||
| 192 | |||
| 193 | let p = T::regs(); | ||
| 194 | unsafe { | ||
| 195 | p.ic_enable().write(|w| w.set_enable(false)); | ||
| 196 | p.ic_tar().write(|w| w.set_ic_tar(addr)); | ||
| 197 | p.ic_enable().write(|w| w.set_enable(true)); | ||
| 198 | } | ||
| 199 | Ok(()) | ||
| 200 | } | ||
| 201 | |||
| 202 | fn read_and_clear_abort_reason(&mut self) -> Option<u32> { | ||
| 203 | let p = T::regs(); | ||
| 204 | unsafe { | ||
| 205 | let abort_reason = p.ic_tx_abrt_source().read().0; | ||
| 206 | if abort_reason != 0 { | ||
| 207 | // Note clearing the abort flag also clears the reason, and this | ||
| 208 | // instance of flag is clear-on-read! Note also the | ||
| 209 | // IC_CLR_TX_ABRT register always reads as 0. | ||
| 210 | p.ic_clr_tx_abrt().read(); | ||
| 211 | Some(abort_reason) | ||
| 212 | } else { | ||
| 213 | None | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { | ||
| 219 | if buffer.is_empty() { | ||
| 220 | return Err(Error::InvalidReadBufferLength); | ||
| 221 | } | ||
| 222 | |||
| 223 | let p = T::regs(); | ||
| 224 | let lastindex = buffer.len() - 1; | ||
| 225 | for (i, byte) in buffer.iter_mut().enumerate() { | ||
| 226 | let first = i == 0; | ||
| 227 | let last = i == lastindex; | ||
| 228 | |||
| 229 | // NOTE(unsafe) We have &mut self | ||
| 230 | unsafe { | ||
| 231 | // wait until there is space in the FIFO to write the next byte | ||
| 232 | while self.tx_fifo_full() {} | ||
| 233 | |||
| 234 | p.ic_data_cmd().write(|w| { | ||
| 235 | if restart && first { | ||
| 236 | w.set_restart(true); | ||
| 237 | } else { | ||
| 238 | w.set_restart(false); | ||
| 239 | } | ||
| 240 | |||
| 241 | if send_stop && last { | ||
| 242 | w.set_stop(true); | ||
| 243 | } else { | ||
| 244 | w.set_stop(false); | ||
| 245 | } | ||
| 246 | |||
| 247 | w.cmd() | ||
| 248 | }); | ||
| 249 | |||
| 250 | while p.ic_rxflr().read().rxflr() == 0 { | ||
| 251 | if let Some(abort_reason) = self.read_and_clear_abort_reason() { | ||
| 252 | return Err(Error::Abort(abort_reason)); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | *byte = p.ic_data_cmd().read().dat(); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | Ok(()) | ||
| 261 | } | ||
| 262 | |||
| 263 | fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { | ||
| 264 | if bytes.is_empty() { | ||
| 265 | return Err(Error::InvalidWriteBufferLength); | ||
| 266 | } | ||
| 267 | |||
| 268 | let p = T::regs(); | ||
| 269 | |||
| 270 | for (i, byte) in bytes.iter().enumerate() { | ||
| 271 | let last = i == bytes.len() - 1; | ||
| 272 | |||
| 273 | // NOTE(unsafe) We have &mut self | ||
| 274 | unsafe { | ||
| 275 | p.ic_data_cmd().write(|w| { | ||
| 276 | if send_stop && last { | ||
| 277 | w.set_stop(true); | ||
| 278 | } else { | ||
| 279 | w.set_stop(false); | ||
| 280 | } | ||
| 281 | w.set_dat(*byte); | ||
| 282 | }); | ||
| 283 | |||
| 284 | // Wait until the transmission of the address/data from the | ||
| 285 | // internal shift register has completed. For this to function | ||
| 286 | // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The | ||
| 287 | // TX_EMPTY_CTRL flag was set in i2c_init. | ||
| 288 | while !p.ic_raw_intr_stat().read().tx_empty() {} | ||
| 289 | |||
| 290 | let abort_reason = self.read_and_clear_abort_reason(); | ||
| 291 | |||
| 292 | if abort_reason.is_some() || (send_stop && last) { | ||
| 293 | // If the transaction was aborted or if it completed | ||
| 294 | // successfully wait until the STOP condition has occured. | ||
| 295 | |||
| 296 | while !p.ic_raw_intr_stat().read().stop_det() {} | ||
| 297 | |||
| 298 | p.ic_clr_stop_det().read().clr_stop_det(); | ||
| 299 | } | ||
| 300 | |||
| 301 | // Note the hardware issues a STOP automatically on an abort | ||
| 302 | // condition. Note also the hardware clears RX FIFO as well as | ||
| 303 | // TX on abort, ecause we set hwparam | ||
| 304 | // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. | ||
| 305 | if let Some(abort_reason) = abort_reason { | ||
| 306 | return Err(Error::Abort(abort_reason)); | ||
| 307 | } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | Ok(()) | ||
| 311 | } | ||
| 312 | |||
| 313 | // ========================= Blocking public API | ||
| 314 | // ========================= | ||
| 315 | |||
| 316 | pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | ||
| 317 | Self::setup(address.into())?; | ||
| 318 | self.read_blocking_internal(buffer, false, true) | ||
| 319 | // Automatic Stop | ||
| 320 | } | ||
| 321 | |||
| 322 | pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { | ||
| 323 | Self::setup(address.into())?; | ||
| 324 | self.write_blocking_internal(bytes, true) | ||
| 325 | } | ||
| 326 | |||
| 327 | pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { | ||
| 328 | Self::setup(address.into())?; | ||
| 329 | self.write_blocking_internal(bytes, false)?; | ||
| 330 | self.read_blocking_internal(buffer, true, true) | ||
| 331 | // Automatic Stop | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | // impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // | ||
| 336 | // Async public API // ========================= | ||
| 337 | |||
| 338 | // pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), | ||
| 339 | // Error> { if bytes.is_empty() { self.write_blocking_internal(address, | ||
| 340 | // bytes, true) } else { self.write_dma_internal(address, bytes, | ||
| 341 | // true, true).await } } | ||
| 342 | |||
| 343 | // pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> | ||
| 344 | // Result<(), Error> { if bytes.is_empty() { return | ||
| 345 | // Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); | ||
| 346 | |||
| 347 | // let mut first = true; let mut current = iter.next(); while let | ||
| 348 | // Some(c) = current { let next = iter.next(); let is_last = | ||
| 349 | // next.is_none(); | ||
| 350 | |||
| 351 | // self.write_dma_internal(address, c, first, is_last).await?; | ||
| 352 | // first = false; | ||
| 353 | // current = next; | ||
| 354 | // } Ok(()) | ||
| 355 | // } | ||
| 356 | |||
| 357 | // pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> | ||
| 358 | // Result<(), Error> { if buffer.is_empty() { | ||
| 359 | // self.read_blocking_internal(address, buffer, false) } else { | ||
| 360 | // self.read_dma_internal(address, buffer, false).await } } | ||
| 361 | |||
| 362 | // pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: | ||
| 363 | // &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { | ||
| 364 | // self.write_blocking_internal(address, bytes, false)?; } else { | ||
| 365 | // self.write_dma_internal(address, bytes, true, true).await?; } | ||
| 366 | |||
| 367 | // if buffer.is_empty() { self.read_blocking_internal(address, buffer, | ||
| 368 | // true)?; } else { self.read_dma_internal(address, buffer, | ||
| 369 | // true).await?; } | ||
| 370 | |||
| 371 | // Ok(()) | ||
| 372 | // } | ||
| 373 | // } | ||
| 374 | |||
| 375 | mod eh02 { | ||
| 376 | use super::*; | ||
| 377 | |||
| 378 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { | ||
| 379 | type Error = Error; | ||
| 380 | |||
| 381 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { | ||
| 382 | self.blocking_read(address, buffer) | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { | ||
| 387 | type Error = Error; | ||
| 388 | |||
| 389 | fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 390 | self.blocking_write(address, bytes) | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { | ||
| 395 | type Error = Error; | ||
| 396 | |||
| 397 | fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { | ||
| 398 | self.blocking_write_read(address, bytes, buffer) | ||
| 399 | } | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | fn i2c_reserved_addr(addr: u16) -> bool { | ||
| 404 | (addr & 0x78) == 0 || (addr & 0x78) == 0x78 | ||
| 405 | } | ||
| 406 | |||
| 141 | mod sealed { | 407 | mod sealed { |
| 142 | pub trait Instance {} | 408 | pub trait Instance {} |
| 143 | pub trait Mode {} | 409 | pub trait Mode {} |
| @@ -155,11 +421,11 @@ macro_rules! impl_mode { | |||
| 155 | }; | 421 | }; |
| 156 | } | 422 | } |
| 157 | 423 | ||
| 158 | pub struct Master; | 424 | pub struct Blocking; |
| 159 | pub struct Slave; | 425 | pub struct Async; |
| 160 | 426 | ||
| 161 | impl_mode!(Master); | 427 | impl_mode!(Blocking); |
| 162 | impl_mode!(Slave); | 428 | impl_mode!(Async); |
| 163 | 429 | ||
| 164 | pub trait Instance: sealed::Instance { | 430 | pub trait Instance: sealed::Instance { |
| 165 | fn regs() -> pac::i2c::I2c; | 431 | fn regs() -> pac::i2c::I2c; |
