|
1 | 1 | //! I2C
|
2 | 2 |
|
| 3 | +use core::ops::Deref; |
| 4 | + |
| 5 | +#[cfg(feature = "stm32l0x2")] |
3 | 6 | use core::{
|
4 | 7 | marker::PhantomData,
|
5 |
| - ops::Deref, |
| 8 | + ops::DerefMut, |
| 9 | + pin::Pin, |
| 10 | +}; |
| 11 | + |
| 12 | +#[cfg(feature = "stm32l0x2")] |
| 13 | +use as_slice::{ |
| 14 | + AsSlice, |
| 15 | + AsMutSlice, |
6 | 16 | };
|
7 | 17 |
|
8 | 18 | use crate::hal::blocking::i2c::{Read, Write, WriteRead};
|
9 | 19 |
|
| 20 | +#[cfg(feature = "stm32l0x2")] |
| 21 | +use crate::dma; |
10 | 22 | use crate::gpio::gpioa::{PA10, PA9};
|
11 | 23 | use crate::gpio::gpiob::{PB6, PB7};
|
12 | 24 | use crate::gpio::{AltMode, OpenDrain, Output};
|
@@ -130,8 +142,15 @@ where
|
130 | 142 | .bits(scldel)
|
131 | 143 | });
|
132 | 144 |
|
133 |
| - // Enable the peripheral |
134 |
| - i2c.cr1.write(|w| w.pe().set_bit()); |
| 145 | + i2c.cr1.write(|w| |
| 146 | + w |
| 147 | + // Enable DMA reception |
| 148 | + .rxdmaen().set_bit() |
| 149 | + // Enable DMA transmission |
| 150 | + .txdmaen().set_bit() |
| 151 | + // Enable peripheral |
| 152 | + .pe().set_bit() |
| 153 | + ); |
135 | 154 |
|
136 | 155 | I2c { i2c, sda, scl }
|
137 | 156 | }
|
@@ -186,6 +205,100 @@ where
|
186 | 205 | let value = self.i2c.rxdr.read().rxdata().bits();
|
187 | 206 | Ok(value)
|
188 | 207 | }
|
| 208 | + |
| 209 | + #[cfg(feature = "stm32l0x2")] |
| 210 | + pub fn write_all<Channel, Buffer>(mut self, |
| 211 | + dma: &mut dma::Handle, |
| 212 | + channel: Channel, |
| 213 | + address: u8, |
| 214 | + buffer: Pin<Buffer>, |
| 215 | + ) |
| 216 | + -> Transfer<Self, Tx<I>, Channel, Buffer, dma::Ready> |
| 217 | + where |
| 218 | + Tx<I>: dma::Target<Channel>, |
| 219 | + Channel: dma::Channel, |
| 220 | + Buffer: Deref + 'static, |
| 221 | + Buffer::Target: AsSlice<Element=u8>, |
| 222 | + { |
| 223 | + self.start_transfer(address, buffer.as_slice().len(), RD_WRNW::WRITE); |
| 224 | + |
| 225 | + // This token represents the transmission capability of I2C and this is |
| 226 | + // what the `dma::Target` trait is implemented for. It can't be |
| 227 | + // implemented for `I2c` itself, as that would allow for the user to |
| 228 | + // pass, for example, a channel that can do I2C RX to `write_all`. |
| 229 | + // |
| 230 | + // Theoretically, one could create both `Rx` and `Tx` at the same time, |
| 231 | + // or create multiple tokens of the same type, and use that to create |
| 232 | + // multiple simultaneous DMA transfers, which would be wrong and is not |
| 233 | + // supported by the I2C peripheral. We prevent that by only ever |
| 234 | + // creating an `Rx` or `Tx` token while we have ownership of `I2c`, and |
| 235 | + // dropping the token before returning ownership of `I2c` ot the user. |
| 236 | + let token = Tx(PhantomData); |
| 237 | + |
| 238 | + // Safe, because we're only taking the address of a register. |
| 239 | + let address = &unsafe { &*I::ptr() }.txdr as *const _ as u32; |
| 240 | + |
| 241 | + // Safe, because the trait bounds of this method guarantee that the |
| 242 | + // buffer can be read from. |
| 243 | + let transfer = unsafe { |
| 244 | + dma::Transfer::new( |
| 245 | + dma, |
| 246 | + token, |
| 247 | + channel, |
| 248 | + buffer, |
| 249 | + address, |
| 250 | + dma::Priority::high(), |
| 251 | + dma::Direction::memory_to_peripheral(), |
| 252 | + ) |
| 253 | + }; |
| 254 | + |
| 255 | + Transfer { |
| 256 | + target: self, |
| 257 | + inner: transfer, |
| 258 | + } |
| 259 | + } |
| 260 | + |
| 261 | + #[cfg(feature = "stm32l0x2")] |
| 262 | + pub fn read_all<Channel, Buffer>(mut self, |
| 263 | + dma: &mut dma::Handle, |
| 264 | + channel: Channel, |
| 265 | + address: u8, |
| 266 | + buffer: Pin<Buffer>, |
| 267 | + ) |
| 268 | + -> Transfer<Self, Rx<I>, Channel, Buffer, dma::Ready> |
| 269 | + where |
| 270 | + Rx<I>: dma::Target<Channel>, |
| 271 | + Channel: dma::Channel, |
| 272 | + Buffer: DerefMut + 'static, |
| 273 | + Buffer::Target: AsMutSlice<Element=u8>, |
| 274 | + { |
| 275 | + self.start_transfer(address, buffer.as_slice().len(), RD_WRNW::READ); |
| 276 | + |
| 277 | + // See explanation of tokens in `write_all`. |
| 278 | + let token = Rx(PhantomData); |
| 279 | + |
| 280 | + // Safe, because we're only taking the address of a register. |
| 281 | + let address = &unsafe { &*I::ptr() }.rxdr as *const _ as u32; |
| 282 | + |
| 283 | + // Safe, because the trait bounds of this method guarantee that the |
| 284 | + // buffer can be written to. |
| 285 | + let transfer = unsafe { |
| 286 | + dma::Transfer::new( |
| 287 | + dma, |
| 288 | + token, |
| 289 | + channel, |
| 290 | + buffer, |
| 291 | + address, |
| 292 | + dma::Priority::high(), |
| 293 | + dma::Direction::peripheral_to_memory(), |
| 294 | + ) |
| 295 | + }; |
| 296 | + |
| 297 | + Transfer { |
| 298 | + target: self, |
| 299 | + inner: transfer, |
| 300 | + } |
| 301 | + } |
189 | 302 | }
|
190 | 303 |
|
191 | 304 | impl<I, SDA, SCL> WriteRead for I2c<I, SDA, SCL>
|
@@ -242,6 +355,7 @@ where
|
242 | 355 | }
|
243 | 356 |
|
244 | 357 | pub trait Instance: Deref<Target = RegisterBlock> {
|
| 358 | + fn ptr() -> *const RegisterBlock; |
245 | 359 | fn initialize(&self, rcc: &mut Rcc);
|
246 | 360 | }
|
247 | 361 |
|
@@ -310,6 +424,10 @@ macro_rules! i2c {
|
310 | 424 | }
|
311 | 425 |
|
312 | 426 | impl Instance for $I2CX {
|
| 427 | + fn ptr() -> *const RegisterBlock { |
| 428 | + $I2CX::ptr() |
| 429 | + } |
| 430 | + |
313 | 431 | fn initialize(&self, rcc: &mut Rcc) {
|
314 | 432 | // Enable clock for I2C
|
315 | 433 | rcc.rb.apb1enr.modify(|_, w| w.$i2cxen().set_bit());
|
@@ -391,10 +509,93 @@ i2c!(
|
391 | 509 | ///
|
392 | 510 | /// This is an implementation detail. The user doesn't have to deal with this
|
393 | 511 | /// directly.
|
| 512 | +#[cfg(feature = "stm32l0x2")] |
394 | 513 | pub struct Tx<I>(PhantomData<I>);
|
395 | 514 |
|
396 | 515 | /// Token used for DMA transfers
|
397 | 516 | ///
|
398 | 517 | /// This is an implementation detail. The user doesn't have to deal with this
|
399 | 518 | /// directly.
|
| 519 | +#[cfg(feature = "stm32l0x2")] |
400 | 520 | pub struct Rx<I>(PhantomData<I>);
|
| 521 | + |
| 522 | + |
| 523 | +/// I2C-specific wrapper around [`dma::Transfer`] |
| 524 | +#[cfg(feature = "stm32l0x2")] |
| 525 | +pub struct Transfer<Target, Token, Channel, Buffer, State> { |
| 526 | + target: Target, |
| 527 | + inner: dma::Transfer<Token, Channel, Buffer, State>, |
| 528 | +} |
| 529 | + |
| 530 | +#[cfg(feature = "stm32l0x2")] |
| 531 | +impl<Target, Token, Channel, Buffer> |
| 532 | + Transfer<Target, Token, Channel, Buffer, dma::Ready> |
| 533 | + where |
| 534 | + Token: dma::Target<Channel>, |
| 535 | + Channel: dma::Channel, |
| 536 | +{ |
| 537 | + /// Enables the provided interrupts |
| 538 | + /// |
| 539 | + /// This setting only affects this transfer. It doesn't affect transfer on |
| 540 | + /// other channels, or subsequent transfers on the same channel. |
| 541 | + pub fn enable_interrupts(&mut self, interrupts: dma::Interrupts) { |
| 542 | + self.inner.enable_interrupts(interrupts); |
| 543 | + } |
| 544 | + |
| 545 | + /// Start the DMA transfer |
| 546 | + /// |
| 547 | + /// Consumes this instance of `Transfer` and returns a new one, with its |
| 548 | + /// state changed to indicate that the transfer has been started. |
| 549 | + pub fn start(self) |
| 550 | + -> Transfer<Target, Token, Channel, Buffer, dma::Started> |
| 551 | + { |
| 552 | + Transfer { |
| 553 | + target: self.target, |
| 554 | + inner: self.inner.start(), |
| 555 | + } |
| 556 | + } |
| 557 | +} |
| 558 | + |
| 559 | +#[cfg(feature = "stm32l0x2")] |
| 560 | +impl<Target, Token, Channel, Buffer> |
| 561 | + Transfer<Target, Token, Channel, Buffer, dma::Started> |
| 562 | + where |
| 563 | + Channel: dma::Channel, |
| 564 | +{ |
| 565 | + /// Indicates whether the transfer is still ongoing |
| 566 | + pub fn is_active(&self) -> bool { |
| 567 | + self.inner.is_active() |
| 568 | + } |
| 569 | + |
| 570 | + /// Waits for the transfer to finish and returns the owned resources |
| 571 | + /// |
| 572 | + /// This function will busily wait until the transfer is finished. If you |
| 573 | + /// don't want this, please call this function only once you know that the |
| 574 | + /// transfer has finished. |
| 575 | + /// |
| 576 | + /// This function will return immediately, if [`Transfer::is_active`] |
| 577 | + /// returns `false`. |
| 578 | + pub fn wait(self) |
| 579 | + -> Result< |
| 580 | + dma::TransferResources<Target, Channel, Buffer>, |
| 581 | + (dma::TransferResources<Target, Channel, Buffer>, dma::Error) |
| 582 | + > |
| 583 | + { |
| 584 | + // Need to move `target` out of `self`, otherwise the closure captures |
| 585 | + // `self` completely. |
| 586 | + let target = self.target; |
| 587 | + |
| 588 | + let map_resources = |res: dma::TransferResources<_, _, _>| { |
| 589 | + dma::TransferResources { |
| 590 | + target: target, |
| 591 | + channel: res.channel, |
| 592 | + buffer: res.buffer, |
| 593 | + } |
| 594 | + }; |
| 595 | + |
| 596 | + match self.inner.wait() { |
| 597 | + Ok(res) => Ok(map_resources(res)), |
| 598 | + Err((res, err)) => Err((map_resources(res), err)), |
| 599 | + } |
| 600 | + } |
| 601 | +} |
0 commit comments