diff options
| -rw-r--r-- | embassy-stm32/src/i2c/v1.rs | 404 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/i2c_async.rs | 62 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/i2c_comparison.rs | 135 |
3 files changed, 578 insertions, 23 deletions
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 03f07c4fe..b62ee8246 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -1,10 +1,14 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 1 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::task::Poll; | ||
| 2 | 4 | ||
| 3 | use embassy_embedded_hal::SetConfig; | 5 | use embassy_embedded_hal::SetConfig; |
| 6 | use embassy_futures::select::{select, Either}; | ||
| 7 | use embassy_hal_internal::drop::OnDrop; | ||
| 4 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 5 | 9 | ||
| 6 | use super::*; | 10 | use super::*; |
| 7 | use crate::dma::NoDma; | 11 | use crate::dma::{NoDma, Transfer}; |
| 8 | use crate::gpio::sealed::AFType; | 12 | use crate::gpio::sealed::AFType; |
| 9 | use crate::gpio::Pull; | 13 | use crate::gpio::Pull; |
| 10 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| @@ -13,7 +17,17 @@ use crate::time::Hertz; | |||
| 13 | use crate::{interrupt, Peripheral}; | 17 | use crate::{interrupt, Peripheral}; |
| 14 | 18 | ||
| 15 | pub unsafe fn on_interrupt<T: Instance>() { | 19 | pub unsafe fn on_interrupt<T: Instance>() { |
| 16 | // todo | 20 | let regs = T::regs(); |
| 21 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of | ||
| 22 | // other stuff, so we wake the task on every interrupt. | ||
| 23 | T::state().waker.wake(); | ||
| 24 | critical_section::with(|_| { | ||
| 25 | // Clear event interrupt flag. | ||
| 26 | regs.cr2().modify(|w| { | ||
| 27 | w.set_itevten(false); | ||
| 28 | w.set_iterren(false); | ||
| 29 | }); | ||
| 30 | }); | ||
| 17 | } | 31 | } |
| 18 | 32 | ||
| 19 | #[non_exhaustive] | 33 | #[non_exhaustive] |
| @@ -98,40 +112,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 98 | } | 112 | } |
| 99 | } | 113 | } |
| 100 | 114 | ||
| 101 | fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> { | 115 | fn check_and_clear_error_flags() -> Result<i2c::regs::Sr1, Error> { |
| 102 | // Note that flags should only be cleared once they have been registered. If flags are | 116 | // Note that flags should only be cleared once they have been registered. If flags are |
| 103 | // cleared otherwise, there may be an inherent race condition and flags may be missed. | 117 | // cleared otherwise, there may be an inherent race condition and flags may be missed. |
| 104 | let sr1 = T::regs().sr1().read(); | 118 | let sr1 = T::regs().sr1().read(); |
| 105 | 119 | ||
| 106 | if sr1.timeout() { | 120 | if sr1.timeout() { |
| 107 | T::regs().sr1().modify(|reg| reg.set_timeout(false)); | 121 | T::regs().sr1().write(|reg| { |
| 122 | reg.0 = !0; | ||
| 123 | reg.set_timeout(false); | ||
| 124 | }); | ||
| 108 | return Err(Error::Timeout); | 125 | return Err(Error::Timeout); |
| 109 | } | 126 | } |
| 110 | 127 | ||
| 111 | if sr1.pecerr() { | 128 | if sr1.pecerr() { |
| 112 | T::regs().sr1().modify(|reg| reg.set_pecerr(false)); | 129 | T::regs().sr1().write(|reg| { |
| 130 | reg.0 = !0; | ||
| 131 | reg.set_pecerr(false); | ||
| 132 | }); | ||
| 113 | return Err(Error::Crc); | 133 | return Err(Error::Crc); |
| 114 | } | 134 | } |
| 115 | 135 | ||
| 116 | if sr1.ovr() { | 136 | if sr1.ovr() { |
| 117 | T::regs().sr1().modify(|reg| reg.set_ovr(false)); | 137 | T::regs().sr1().write(|reg| { |
| 138 | reg.0 = !0; | ||
| 139 | reg.set_ovr(false); | ||
| 140 | }); | ||
| 118 | return Err(Error::Overrun); | 141 | return Err(Error::Overrun); |
| 119 | } | 142 | } |
| 120 | 143 | ||
| 121 | if sr1.af() { | 144 | if sr1.af() { |
| 122 | T::regs().sr1().modify(|reg| reg.set_af(false)); | 145 | T::regs().sr1().write(|reg| { |
| 146 | reg.0 = !0; | ||
| 147 | reg.set_af(false); | ||
| 148 | }); | ||
| 123 | return Err(Error::Nack); | 149 | return Err(Error::Nack); |
| 124 | } | 150 | } |
| 125 | 151 | ||
| 126 | if sr1.arlo() { | 152 | if sr1.arlo() { |
| 127 | T::regs().sr1().modify(|reg| reg.set_arlo(false)); | 153 | T::regs().sr1().write(|reg| { |
| 154 | reg.0 = !0; | ||
| 155 | reg.set_arlo(false); | ||
| 156 | }); | ||
| 128 | return Err(Error::Arbitration); | 157 | return Err(Error::Arbitration); |
| 129 | } | 158 | } |
| 130 | 159 | ||
| 131 | // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and | 160 | // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and |
| 132 | // clearing the BERR bit instead. | 161 | // clearing the BERR bit instead. |
| 133 | if sr1.berr() { | 162 | if sr1.berr() { |
| 134 | T::regs().sr1().modify(|reg| reg.set_berr(false)); | 163 | T::regs().sr1().write(|reg| { |
| 164 | reg.0 = !0; | ||
| 165 | reg.set_berr(false); | ||
| 166 | }); | ||
| 135 | } | 167 | } |
| 136 | 168 | ||
| 137 | Ok(sr1) | 169 | Ok(sr1) |
| @@ -150,13 +182,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 150 | }); | 182 | }); |
| 151 | 183 | ||
| 152 | // Wait until START condition was generated | 184 | // Wait until START condition was generated |
| 153 | while !self.check_and_clear_error_flags()?.start() { | 185 | while !Self::check_and_clear_error_flags()?.start() { |
| 154 | check_timeout()?; | 186 | check_timeout()?; |
| 155 | } | 187 | } |
| 156 | 188 | ||
| 157 | // Also wait until signalled we're master and everything is waiting for us | 189 | // Also wait until signalled we're master and everything is waiting for us |
| 158 | while { | 190 | while { |
| 159 | self.check_and_clear_error_flags()?; | 191 | Self::check_and_clear_error_flags()?; |
| 160 | 192 | ||
| 161 | let sr2 = T::regs().sr2().read(); | 193 | let sr2 = T::regs().sr2().read(); |
| 162 | !sr2.msl() && !sr2.busy() | 194 | !sr2.msl() && !sr2.busy() |
| @@ -170,7 +202,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 170 | // Wait until address was sent | 202 | // Wait until address was sent |
| 171 | // Wait for the address to be acknowledged | 203 | // Wait for the address to be acknowledged |
| 172 | // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. | 204 | // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. |
| 173 | while !self.check_and_clear_error_flags()?.addr() { | 205 | while !Self::check_and_clear_error_flags()?.addr() { |
| 174 | check_timeout()?; | 206 | check_timeout()?; |
| 175 | } | 207 | } |
| 176 | 208 | ||
| @@ -190,7 +222,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 190 | // Wait until we're ready for sending | 222 | // Wait until we're ready for sending |
| 191 | while { | 223 | while { |
| 192 | // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. | 224 | // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. |
| 193 | !self.check_and_clear_error_flags()?.txe() | 225 | !Self::check_and_clear_error_flags()?.txe() |
| 194 | } { | 226 | } { |
| 195 | check_timeout()?; | 227 | check_timeout()?; |
| 196 | } | 228 | } |
| @@ -201,7 +233,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 201 | // Wait until byte is transferred | 233 | // Wait until byte is transferred |
| 202 | while { | 234 | while { |
| 203 | // Check for any potential error conditions. | 235 | // Check for any potential error conditions. |
| 204 | !self.check_and_clear_error_flags()?.btf() | 236 | !Self::check_and_clear_error_flags()?.btf() |
| 205 | } { | 237 | } { |
| 206 | check_timeout()?; | 238 | check_timeout()?; |
| 207 | } | 239 | } |
| @@ -212,7 +244,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 212 | fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> { | 244 | fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> { |
| 213 | while { | 245 | while { |
| 214 | // Check for any potential error conditions. | 246 | // Check for any potential error conditions. |
| 215 | self.check_and_clear_error_flags()?; | 247 | Self::check_and_clear_error_flags()?; |
| 216 | 248 | ||
| 217 | !T::regs().sr1().read().rxne() | 249 | !T::regs().sr1().read().rxne() |
| 218 | } { | 250 | } { |
| @@ -237,7 +269,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 237 | }); | 269 | }); |
| 238 | 270 | ||
| 239 | // Wait until START condition was generated | 271 | // Wait until START condition was generated |
| 240 | while !self.check_and_clear_error_flags()?.start() { | 272 | while !Self::check_and_clear_error_flags()?.start() { |
| 241 | check_timeout()?; | 273 | check_timeout()?; |
| 242 | } | 274 | } |
| 243 | 275 | ||
| @@ -254,7 +286,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 254 | 286 | ||
| 255 | // Wait until address was sent | 287 | // Wait until address was sent |
| 256 | // Wait for the address to be acknowledged | 288 | // Wait for the address to be acknowledged |
| 257 | while !self.check_and_clear_error_flags()?.addr() { | 289 | while !Self::check_and_clear_error_flags()?.addr() { |
| 258 | check_timeout()?; | 290 | check_timeout()?; |
| 259 | } | 291 | } |
| 260 | 292 | ||
| @@ -332,26 +364,352 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { | |||
| 332 | 364 | ||
| 333 | // Async | 365 | // Async |
| 334 | 366 | ||
| 335 | pub async fn write(&mut self, _address: u8, _write: &[u8]) -> Result<(), Error> | 367 | #[inline] // pretty sure this should always be inlined |
| 368 | fn enable_interrupts() -> () { | ||
| 369 | T::regs().cr2().modify(|w| { | ||
| 370 | w.set_iterren(true); | ||
| 371 | w.set_itevten(true); | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | |||
| 375 | async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error> | ||
| 336 | where | 376 | where |
| 337 | TXDMA: crate::i2c::TxDma<T>, | 377 | TXDMA: crate::i2c::TxDma<T>, |
| 338 | { | 378 | { |
| 339 | todo!() | 379 | let dma_transfer = unsafe { |
| 380 | let regs = T::regs(); | ||
| 381 | regs.cr2().modify(|w| { | ||
| 382 | // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. | ||
| 383 | w.set_dmaen(true); | ||
| 384 | w.set_itbufen(false); | ||
| 385 | }); | ||
| 386 | // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. | ||
| 387 | let dst = regs.dr().as_ptr() as *mut u8; | ||
| 388 | |||
| 389 | let ch = &mut self.tx_dma; | ||
| 390 | let request = ch.request(); | ||
| 391 | Transfer::new_write(ch, request, write, dst, Default::default()) | ||
| 392 | }; | ||
| 393 | |||
| 394 | let on_drop = OnDrop::new(|| { | ||
| 395 | let regs = T::regs(); | ||
| 396 | regs.cr2().modify(|w| { | ||
| 397 | w.set_dmaen(false); | ||
| 398 | w.set_iterren(false); | ||
| 399 | w.set_itevten(false); | ||
| 400 | }) | ||
| 401 | }); | ||
| 402 | |||
| 403 | Self::enable_interrupts(); | ||
| 404 | |||
| 405 | // Send a START condition | ||
| 406 | T::regs().cr1().modify(|reg| { | ||
| 407 | reg.set_start(true); | ||
| 408 | }); | ||
| 409 | |||
| 410 | let state = T::state(); | ||
| 411 | |||
| 412 | // Wait until START condition was generated | ||
| 413 | poll_fn(|cx| { | ||
| 414 | state.waker.register(cx.waker()); | ||
| 415 | |||
| 416 | match Self::check_and_clear_error_flags() { | ||
| 417 | Err(e) => Poll::Ready(Err(e)), | ||
| 418 | Ok(sr1) => { | ||
| 419 | if sr1.start() { | ||
| 420 | Poll::Ready(Ok(())) | ||
| 421 | } else { | ||
| 422 | Poll::Pending | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | }) | ||
| 427 | .await?; | ||
| 428 | |||
| 429 | // Also wait until signalled we're master and everything is waiting for us | ||
| 430 | Self::enable_interrupts(); | ||
| 431 | poll_fn(|cx| { | ||
| 432 | state.waker.register(cx.waker()); | ||
| 433 | |||
| 434 | match Self::check_and_clear_error_flags() { | ||
| 435 | Err(e) => Poll::Ready(Err(e)), | ||
| 436 | Ok(_) => { | ||
| 437 | let sr2 = T::regs().sr2().read(); | ||
| 438 | if !sr2.msl() && !sr2.busy() { | ||
| 439 | Poll::Pending | ||
| 440 | } else { | ||
| 441 | Poll::Ready(Ok(())) | ||
| 442 | } | ||
| 443 | } | ||
| 444 | } | ||
| 445 | }) | ||
| 446 | .await?; | ||
| 447 | |||
| 448 | // Set up current address, we're trying to talk to | ||
| 449 | Self::enable_interrupts(); | ||
| 450 | T::regs().dr().write(|reg| reg.set_dr(address << 1)); | ||
| 451 | |||
| 452 | poll_fn(|cx| { | ||
| 453 | state.waker.register(cx.waker()); | ||
| 454 | match Self::check_and_clear_error_flags() { | ||
| 455 | Err(e) => Poll::Ready(Err(e)), | ||
| 456 | Ok(sr1) => { | ||
| 457 | if sr1.addr() { | ||
| 458 | // Clear the ADDR condition by reading SR2. | ||
| 459 | T::regs().sr2().read(); | ||
| 460 | Poll::Ready(Ok(())) | ||
| 461 | } else { | ||
| 462 | Poll::Pending | ||
| 463 | } | ||
| 464 | } | ||
| 465 | } | ||
| 466 | }) | ||
| 467 | .await?; | ||
| 468 | Self::enable_interrupts(); | ||
| 469 | let poll_error = poll_fn(|cx| { | ||
| 470 | state.waker.register(cx.waker()); | ||
| 471 | |||
| 472 | match Self::check_and_clear_error_flags() { | ||
| 473 | // Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other | ||
| 474 | // identical poll_fn check_and_clear matches. | ||
| 475 | Err(e) => Poll::Ready(Err::<T, Error>(e)), | ||
| 476 | Ok(_) => Poll::Pending, | ||
| 477 | } | ||
| 478 | }); | ||
| 479 | |||
| 480 | // Wait for either the DMA transfer to successfully finish, or an I2C error to occur. | ||
| 481 | match select(dma_transfer, poll_error).await { | ||
| 482 | Either::Second(Err(e)) => Err(e), | ||
| 483 | _ => Ok(()), | ||
| 484 | }?; | ||
| 485 | |||
| 486 | // The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too. | ||
| 487 | |||
| 488 | // 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA | ||
| 489 | // requests then wait for a BTF event before programming the Stop condition.” | ||
| 490 | |||
| 491 | // TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it? | ||
| 492 | T::regs().cr2().modify(|w| { | ||
| 493 | w.set_dmaen(false); | ||
| 494 | }); | ||
| 495 | |||
| 496 | Self::enable_interrupts(); | ||
| 497 | poll_fn(|cx| { | ||
| 498 | state.waker.register(cx.waker()); | ||
| 499 | |||
| 500 | match Self::check_and_clear_error_flags() { | ||
| 501 | Err(e) => Poll::Ready(Err(e)), | ||
| 502 | Ok(sr1) => { | ||
| 503 | if sr1.btf() { | ||
| 504 | if send_stop { | ||
| 505 | T::regs().cr1().modify(|w| { | ||
| 506 | w.set_stop(true); | ||
| 507 | }); | ||
| 508 | } | ||
| 509 | |||
| 510 | Poll::Ready(Ok(())) | ||
| 511 | } else { | ||
| 512 | Poll::Pending | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | }) | ||
| 517 | .await?; | ||
| 518 | |||
| 519 | drop(on_drop); | ||
| 520 | |||
| 521 | // Fallthrough is success | ||
| 522 | Ok(()) | ||
| 340 | } | 523 | } |
| 341 | 524 | ||
| 342 | pub async fn read(&mut self, _address: u8, _buffer: &mut [u8]) -> Result<(), Error> | 525 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> |
| 526 | where | ||
| 527 | TXDMA: crate::i2c::TxDma<T>, | ||
| 528 | { | ||
| 529 | self.write_with_stop(address, write, true).await?; | ||
| 530 | |||
| 531 | // Wait for STOP condition to transmit. | ||
| 532 | Self::enable_interrupts(); | ||
| 533 | poll_fn(|cx| { | ||
| 534 | T::state().waker.register(cx.waker()); | ||
| 535 | // TODO: error interrupts are enabled here, should we additional check for and return errors? | ||
| 536 | if T::regs().cr1().read().stop() { | ||
| 537 | Poll::Pending | ||
| 538 | } else { | ||
| 539 | Poll::Ready(Ok(())) | ||
| 540 | } | ||
| 541 | }) | ||
| 542 | .await?; | ||
| 543 | |||
| 544 | Ok(()) | ||
| 545 | } | ||
| 546 | |||
| 547 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> | ||
| 343 | where | 548 | where |
| 344 | RXDMA: crate::i2c::RxDma<T>, | 549 | RXDMA: crate::i2c::RxDma<T>, |
| 345 | { | 550 | { |
| 346 | todo!() | 551 | let state = T::state(); |
| 552 | let buffer_len = buffer.len(); | ||
| 553 | |||
| 554 | let dma_transfer = unsafe { | ||
| 555 | let regs = T::regs(); | ||
| 556 | regs.cr2().modify(|w| { | ||
| 557 | // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. | ||
| 558 | w.set_itbufen(false); | ||
| 559 | w.set_dmaen(true); | ||
| 560 | }); | ||
| 561 | // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. | ||
| 562 | let src = regs.dr().as_ptr() as *mut u8; | ||
| 563 | |||
| 564 | let ch = &mut self.rx_dma; | ||
| 565 | let request = ch.request(); | ||
| 566 | Transfer::new_read(ch, request, src, buffer, Default::default()) | ||
| 567 | }; | ||
| 568 | |||
| 569 | let on_drop = OnDrop::new(|| { | ||
| 570 | let regs = T::regs(); | ||
| 571 | regs.cr2().modify(|w| { | ||
| 572 | w.set_dmaen(false); | ||
| 573 | w.set_iterren(false); | ||
| 574 | w.set_itevten(false); | ||
| 575 | }) | ||
| 576 | }); | ||
| 577 | |||
| 578 | Self::enable_interrupts(); | ||
| 579 | |||
| 580 | // Send a START condition and set ACK bit | ||
| 581 | T::regs().cr1().modify(|reg| { | ||
| 582 | reg.set_start(true); | ||
| 583 | reg.set_ack(true); | ||
| 584 | }); | ||
| 585 | |||
| 586 | // Wait until START condition was generated | ||
| 587 | poll_fn(|cx| { | ||
| 588 | state.waker.register(cx.waker()); | ||
| 589 | |||
| 590 | match Self::check_and_clear_error_flags() { | ||
| 591 | Err(e) => Poll::Ready(Err(e)), | ||
| 592 | Ok(sr1) => { | ||
| 593 | if sr1.start() { | ||
| 594 | Poll::Ready(Ok(())) | ||
| 595 | } else { | ||
| 596 | Poll::Pending | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | }) | ||
| 601 | .await?; | ||
| 602 | |||
| 603 | // Also wait until signalled we're master and everything is waiting for us | ||
| 604 | Self::enable_interrupts(); | ||
| 605 | poll_fn(|cx| { | ||
| 606 | state.waker.register(cx.waker()); | ||
| 607 | |||
| 608 | // blocking read didn’t have a check_and_clear call here, but blocking write did so | ||
| 609 | // I’m adding it here in case that was an oversight. | ||
| 610 | match Self::check_and_clear_error_flags() { | ||
| 611 | Err(e) => Poll::Ready(Err(e)), | ||
| 612 | Ok(_) => { | ||
| 613 | let sr2 = T::regs().sr2().read(); | ||
| 614 | if !sr2.msl() && !sr2.busy() { | ||
| 615 | Poll::Pending | ||
| 616 | } else { | ||
| 617 | Poll::Ready(Ok(())) | ||
| 618 | } | ||
| 619 | } | ||
| 620 | } | ||
| 621 | }) | ||
| 622 | .await?; | ||
| 623 | |||
| 624 | // Set up current address, we're trying to talk to | ||
| 625 | T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); | ||
| 626 | |||
| 627 | // Wait for the address to be acknowledged | ||
| 628 | |||
| 629 | Self::enable_interrupts(); | ||
| 630 | poll_fn(|cx| { | ||
| 631 | state.waker.register(cx.waker()); | ||
| 632 | |||
| 633 | match Self::check_and_clear_error_flags() { | ||
| 634 | Err(e) => Poll::Ready(Err(e)), | ||
| 635 | Ok(sr1) => { | ||
| 636 | if sr1.addr() { | ||
| 637 | // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 | ||
| 638 | // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. | ||
| 639 | if buffer_len == 1 { | ||
| 640 | T::regs().cr1().modify(|w| { | ||
| 641 | w.set_ack(false); | ||
| 642 | }); | ||
| 643 | } | ||
| 644 | Poll::Ready(Ok(())) | ||
| 645 | } else { | ||
| 646 | Poll::Pending | ||
| 647 | } | ||
| 648 | } | ||
| 649 | } | ||
| 650 | }) | ||
| 651 | .await?; | ||
| 652 | |||
| 653 | // Clear ADDR condition by reading SR2 | ||
| 654 | T::regs().sr2().read(); | ||
| 655 | |||
| 656 | // 18.3.8: When a single byte must be received: [snip] Then the | ||
| 657 | // user can program the STOP condition either after clearing ADDR flag, or in the | ||
| 658 | // DMA Transfer Complete interrupt routine. | ||
| 659 | if buffer_len == 1 { | ||
| 660 | T::regs().cr1().modify(|w| { | ||
| 661 | w.set_stop(true); | ||
| 662 | }); | ||
| 663 | } else { | ||
| 664 | // If, in the I2C_CR2 register, the LAST bit is set, I2C | ||
| 665 | // automatically sends a NACK after the next byte following EOT_1. The user can | ||
| 666 | // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. | ||
| 667 | T::regs().cr2().modify(|w| { | ||
| 668 | w.set_last(true); | ||
| 669 | }) | ||
| 670 | } | ||
| 671 | |||
| 672 | // Wait for bytes to be received, or an error to occur. | ||
| 673 | Self::enable_interrupts(); | ||
| 674 | let poll_error = poll_fn(|cx| { | ||
| 675 | state.waker.register(cx.waker()); | ||
| 676 | |||
| 677 | match Self::check_and_clear_error_flags() { | ||
| 678 | Err(e) => Poll::Ready(Err::<T, Error>(e)), | ||
| 679 | _ => Poll::Pending, | ||
| 680 | } | ||
| 681 | }); | ||
| 682 | |||
| 683 | match select(dma_transfer, poll_error).await { | ||
| 684 | Either::Second(Err(e)) => Err(e), | ||
| 685 | _ => Ok(()), | ||
| 686 | }?; | ||
| 687 | |||
| 688 | // Wait for the STOP to be sent (STOP bit cleared). | ||
| 689 | Self::enable_interrupts(); | ||
| 690 | poll_fn(|cx| { | ||
| 691 | state.waker.register(cx.waker()); | ||
| 692 | // TODO: error interrupts are enabled here, should we additional check for and return errors? | ||
| 693 | if T::regs().cr1().read().stop() { | ||
| 694 | Poll::Pending | ||
| 695 | } else { | ||
| 696 | Poll::Ready(Ok(())) | ||
| 697 | } | ||
| 698 | }) | ||
| 699 | .await?; | ||
| 700 | drop(on_drop); | ||
| 701 | |||
| 702 | // Fallthrough is success | ||
| 703 | Ok(()) | ||
| 347 | } | 704 | } |
| 348 | 705 | ||
| 349 | pub async fn write_read(&mut self, _address: u8, _write: &[u8], _read: &mut [u8]) -> Result<(), Error> | 706 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> |
| 350 | where | 707 | where |
| 351 | RXDMA: crate::i2c::RxDma<T>, | 708 | RXDMA: crate::i2c::RxDma<T>, |
| 352 | TXDMA: crate::i2c::TxDma<T>, | 709 | TXDMA: crate::i2c::TxDma<T>, |
| 353 | { | 710 | { |
| 354 | todo!() | 711 | self.write_with_stop(address, write, false).await?; |
| 712 | self.read(address, read).await | ||
| 355 | } | 713 | } |
| 356 | } | 714 | } |
| 357 | 715 | ||
diff --git a/examples/stm32f4/src/bin/i2c_async.rs b/examples/stm32f4/src/bin/i2c_async.rs new file mode 100644 index 000000000..9f59e4d41 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_async.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1 | ||
| 6 | // DMA peripherals changed to compile for stm32f429zi, for the CI. | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::i2c::I2c; | ||
| 11 | use embassy_stm32::time::Hertz; | ||
| 12 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | const ADDRESS: u8 = 96; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 19 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | info!("Hello world!"); | ||
| 25 | let p = embassy_stm32::init(Default::default()); | ||
| 26 | |||
| 27 | let mut i2c = I2c::new( | ||
| 28 | p.I2C1, | ||
| 29 | p.PB8, | ||
| 30 | p.PB7, | ||
| 31 | Irqs, | ||
| 32 | p.DMA1_CH6, | ||
| 33 | p.DMA1_CH0, | ||
| 34 | Hertz(100_000), | ||
| 35 | Default::default(), | ||
| 36 | ); | ||
| 37 | |||
| 38 | loop { | ||
| 39 | let a1454_read_sensor_command = [0x1F]; | ||
| 40 | let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 41 | |||
| 42 | match i2c | ||
| 43 | .write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer) | ||
| 44 | .await | ||
| 45 | { | ||
| 46 | Ok(()) => { | ||
| 47 | // Convert 12-bit signed integer into 16-bit signed integer. | ||
| 48 | // Is the 12 bit number negative? | ||
| 49 | if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 { | ||
| 50 | sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000; | ||
| 51 | } | ||
| 52 | |||
| 53 | let mut sensor_value_raw: u16 = sensor_data_buffer[3].into(); | ||
| 54 | sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8; | ||
| 55 | let sensor_value: u16 = sensor_value_raw.into(); | ||
| 56 | let sensor_value = sensor_value as i16; | ||
| 57 | info!("Data: {}", sensor_value); | ||
| 58 | } | ||
| 59 | Err(e) => error!("I2C Error during read: {:?}", e), | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/examples/stm32f4/src/bin/i2c_comparison.rs b/examples/stm32f4/src/bin/i2c_comparison.rs new file mode 100644 index 000000000..6d23c0ed8 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_comparison.rs | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3 | ||
| 6 | // on the pins referenced in the peripheral definitions. | ||
| 7 | // Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI. | ||
| 8 | // MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x | ||
| 9 | // as long to complete as the blocking ones! | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_stm32::i2c::I2c; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 16 | use embassy_time::Instant; | ||
| 17 | use futures::future::try_join3; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const ADDRESS: u8 = 96; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 24 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 25 | I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||
| 26 | I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||
| 27 | I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>; | ||
| 28 | I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>; | ||
| 29 | }); | ||
| 30 | |||
| 31 | /// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer. | ||
| 32 | fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 { | ||
| 33 | let lower = buffer[3]; | ||
| 34 | let mut upper = buffer[2]; | ||
| 35 | // Fill in additional 1s if the 12 bit number is negative. | ||
| 36 | if (upper & 0b00001000) == 0b0001000 { | ||
| 37 | upper = upper | 0b11110000; | ||
| 38 | } | ||
| 39 | |||
| 40 | let mut sensor_value_raw: u16 = lower.into(); | ||
| 41 | sensor_value_raw |= (upper as u16) << 8; | ||
| 42 | let sensor_value: u16 = sensor_value_raw.into(); | ||
| 43 | let sensor_value = sensor_value as i16; | ||
| 44 | sensor_value | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy_executor::main] | ||
| 48 | async fn main(_spawner: Spawner) { | ||
| 49 | info!("Setting up peripherals."); | ||
| 50 | let p = embassy_stm32::init(Default::default()); | ||
| 51 | |||
| 52 | let mut i2c1 = I2c::new( | ||
| 53 | p.I2C1, | ||
| 54 | p.PB8, | ||
| 55 | p.PB7, | ||
| 56 | Irqs, | ||
| 57 | p.DMA1_CH6, | ||
| 58 | p.DMA1_CH0, | ||
| 59 | Hertz(100_000), | ||
| 60 | Default::default(), | ||
| 61 | ); | ||
| 62 | |||
| 63 | let mut i2c2 = I2c::new( | ||
| 64 | p.I2C2, | ||
| 65 | p.PB10, | ||
| 66 | p.PB11, | ||
| 67 | Irqs, | ||
| 68 | p.DMA1_CH7, | ||
| 69 | p.DMA1_CH3, | ||
| 70 | Hertz(100_000), | ||
| 71 | Default::default(), | ||
| 72 | ); | ||
| 73 | |||
| 74 | let mut i2c3 = I2c::new( | ||
| 75 | p.I2C3, | ||
| 76 | p.PA8, | ||
| 77 | p.PC9, | ||
| 78 | Irqs, | ||
| 79 | p.DMA1_CH4, | ||
| 80 | p.DMA1_CH2, | ||
| 81 | Hertz(100_000), | ||
| 82 | Default::default(), | ||
| 83 | ); | ||
| 84 | |||
| 85 | let a1454_read_sensor_command = [0x1F]; | ||
| 86 | let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 87 | let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 88 | let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 89 | loop { | ||
| 90 | // Blocking reads one after the other. Completes in about 2000us. | ||
| 91 | let blocking_read_start_us = Instant::now().as_micros(); | ||
| 92 | match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) { | ||
| 93 | Ok(()) => {} | ||
| 94 | Err(e) => error!("I2C Error: {:?}", e), | ||
| 95 | } | ||
| 96 | match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) { | ||
| 97 | Ok(()) => {} | ||
| 98 | Err(e) => error!("I2C Error: {:?}", e), | ||
| 99 | } | ||
| 100 | match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) { | ||
| 101 | Ok(()) => {} | ||
| 102 | Err(e) => error!("I2C Error: {:?}", e), | ||
| 103 | } | ||
| 104 | let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us; | ||
| 105 | info!( | ||
| 106 | "Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", | ||
| 107 | blocking_read_total_us, | ||
| 108 | a1454_buf_to_i16(&i2c1_buffer), | ||
| 109 | a1454_buf_to_i16(&i2c2_buffer), | ||
| 110 | a1454_buf_to_i16(&i2c3_buffer) | ||
| 111 | ); | ||
| 112 | |||
| 113 | // Async reads overlapping. Completes in about 1000us. | ||
| 114 | let async_read_start_us = Instant::now().as_micros(); | ||
| 115 | |||
| 116 | let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer); | ||
| 117 | let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer); | ||
| 118 | let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer); | ||
| 119 | |||
| 120 | // Wait for all three transactions to finish, or any one of them to fail. | ||
| 121 | match try_join3(i2c1_result, i2c2_result, i2c3_result).await { | ||
| 122 | Ok(_) => { | ||
| 123 | let async_read_total_us = Instant::now().as_micros() - async_read_start_us; | ||
| 124 | info!( | ||
| 125 | "Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", | ||
| 126 | async_read_total_us, | ||
| 127 | a1454_buf_to_i16(&i2c1_buffer), | ||
| 128 | a1454_buf_to_i16(&i2c2_buffer), | ||
| 129 | a1454_buf_to_i16(&i2c3_buffer) | ||
| 130 | ); | ||
| 131 | } | ||
| 132 | Err(e) => error!("I2C Error during async write-read: {}", e), | ||
| 133 | }; | ||
| 134 | } | ||
| 135 | } | ||
