diff options
| -rw-r--r-- | embassy-stm32/build.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/dac/mod.rs | 267 | ||||
| -rw-r--r-- | embassy-stm32/src/dma/dma.rs | 26 |
3 files changed, 178 insertions, 119 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f7a25743c..7fa4fae45 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -699,8 +699,8 @@ fn main() { | |||
| 699 | // SDMMCv1 uses the same channel for both directions, so just implement for RX | 699 | // SDMMCv1 uses the same channel for both directions, so just implement for RX |
| 700 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), | 700 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), |
| 701 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), | 701 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), |
| 702 | (("dac", "CH1"), quote!(crate::dac::Dma)), | 702 | (("dac", "CH1"), quote!(crate::dac::DmaCh1)), |
| 703 | (("dac", "CH2"), quote!(crate::dac::Dma)), | 703 | (("dac", "CH2"), quote!(crate::dac::DmaCh2)), |
| 704 | ] | 704 | ] |
| 705 | .into(); | 705 | .into(); |
| 706 | 706 | ||
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index d95674ff0..6ead00e15 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -171,13 +171,6 @@ pub trait DacChannel<T: Instance, Tx> { | |||
| 171 | } | 171 | } |
| 172 | Ok(()) | 172 | Ok(()) |
| 173 | } | 173 | } |
| 174 | |||
| 175 | /// Write `data` to the DAC channel via DMA. | ||
| 176 | /// | ||
| 177 | /// `circular` sets the DMA to circular mode. | ||
| 178 | async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 179 | where | ||
| 180 | Tx: Dma<T>; | ||
| 181 | } | 174 | } |
| 182 | 175 | ||
| 183 | /// Hold two DAC channels | 176 | /// Hold two DAC channels |
| @@ -244,6 +237,81 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { | |||
| 244 | }); | 237 | }); |
| 245 | Ok(()) | 238 | Ok(()) |
| 246 | } | 239 | } |
| 240 | |||
| 241 | /// Write `data` to the DAC CH1 via DMA. | ||
| 242 | /// | ||
| 243 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 244 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 245 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 246 | /// | ||
| 247 | /// **Important:** Channel 1 has to be configured for the DAC instance! | ||
| 248 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 249 | where | ||
| 250 | Tx: DmaCh1<T>, | ||
| 251 | { | ||
| 252 | let channel = Channel::Ch1.index(); | ||
| 253 | debug!("Writing to channel {}", channel); | ||
| 254 | |||
| 255 | // Enable DAC and DMA | ||
| 256 | T::regs().cr().modify(|w| { | ||
| 257 | w.set_en(channel, true); | ||
| 258 | w.set_dmaen(channel, true); | ||
| 259 | }); | ||
| 260 | |||
| 261 | let tx_request = self.dma.request(); | ||
| 262 | let dma_channel = &self.dma; | ||
| 263 | |||
| 264 | let tx_options = TransferOptions { | ||
| 265 | circular, | ||
| 266 | half_transfer_ir: false, | ||
| 267 | complete_transfer_ir: !circular, | ||
| 268 | ..Default::default() | ||
| 269 | }; | ||
| 270 | |||
| 271 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 272 | let tx_f = match data { | ||
| 273 | ValueArray::Bit8(buf) => unsafe { | ||
| 274 | Transfer::new_write( | ||
| 275 | dma_channel, | ||
| 276 | tx_request, | ||
| 277 | buf, | ||
| 278 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 279 | tx_options, | ||
| 280 | ) | ||
| 281 | }, | ||
| 282 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 283 | Transfer::new_write( | ||
| 284 | dma_channel, | ||
| 285 | tx_request, | ||
| 286 | buf, | ||
| 287 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 288 | tx_options, | ||
| 289 | ) | ||
| 290 | }, | ||
| 291 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 292 | Transfer::new_write( | ||
| 293 | dma_channel, | ||
| 294 | tx_request, | ||
| 295 | buf, | ||
| 296 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 297 | tx_options, | ||
| 298 | ) | ||
| 299 | }, | ||
| 300 | }; | ||
| 301 | |||
| 302 | tx_f.await; | ||
| 303 | |||
| 304 | // finish dma | ||
| 305 | // TODO: Do we need to check any status registers here? | ||
| 306 | T::regs().cr().modify(|w| { | ||
| 307 | // Disable the DAC peripheral | ||
| 308 | w.set_en(channel, false); | ||
| 309 | // Disable the DMA. TODO: Is this necessary? | ||
| 310 | w.set_dmaen(channel, false); | ||
| 311 | }); | ||
| 312 | |||
| 313 | Ok(()) | ||
| 314 | } | ||
| 247 | } | 315 | } |
| 248 | 316 | ||
| 249 | impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | 317 | impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { |
| @@ -279,6 +347,81 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { | |||
| 279 | }); | 347 | }); |
| 280 | Ok(()) | 348 | Ok(()) |
| 281 | } | 349 | } |
| 350 | |||
| 351 | /// Write `data` to the DAC CH2 via DMA. | ||
| 352 | /// | ||
| 353 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 354 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 355 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 356 | /// | ||
| 357 | /// **Important:** Channel 2 has to be configured for the DAC instance! | ||
| 358 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 359 | where | ||
| 360 | Tx: DmaCh2<T>, | ||
| 361 | { | ||
| 362 | let channel = Channel::Ch2.index(); | ||
| 363 | debug!("Writing to channel {}", channel); | ||
| 364 | |||
| 365 | // Enable DAC and DMA | ||
| 366 | T::regs().cr().modify(|w| { | ||
| 367 | w.set_en(channel, true); | ||
| 368 | w.set_dmaen(channel, true); | ||
| 369 | }); | ||
| 370 | |||
| 371 | let tx_request = self.dma.request(); | ||
| 372 | let dma_channel = &self.dma; | ||
| 373 | |||
| 374 | let tx_options = TransferOptions { | ||
| 375 | circular, | ||
| 376 | half_transfer_ir: false, | ||
| 377 | complete_transfer_ir: !circular, | ||
| 378 | ..Default::default() | ||
| 379 | }; | ||
| 380 | |||
| 381 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 382 | let tx_f = match data { | ||
| 383 | ValueArray::Bit8(buf) => unsafe { | ||
| 384 | Transfer::new_write( | ||
| 385 | dma_channel, | ||
| 386 | tx_request, | ||
| 387 | buf, | ||
| 388 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 389 | tx_options, | ||
| 390 | ) | ||
| 391 | }, | ||
| 392 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 393 | Transfer::new_write( | ||
| 394 | dma_channel, | ||
| 395 | tx_request, | ||
| 396 | buf, | ||
| 397 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 398 | tx_options, | ||
| 399 | ) | ||
| 400 | }, | ||
| 401 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 402 | Transfer::new_write( | ||
| 403 | dma_channel, | ||
| 404 | tx_request, | ||
| 405 | buf, | ||
| 406 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 407 | tx_options, | ||
| 408 | ) | ||
| 409 | }, | ||
| 410 | }; | ||
| 411 | |||
| 412 | tx_f.await; | ||
| 413 | |||
| 414 | // finish dma | ||
| 415 | // TODO: Do we need to check any status registers here? | ||
| 416 | T::regs().cr().modify(|w| { | ||
| 417 | // Disable the DAC peripheral | ||
| 418 | w.set_en(channel, false); | ||
| 419 | // Disable the DMA. TODO: Is this necessary? | ||
| 420 | w.set_dmaen(channel, false); | ||
| 421 | }); | ||
| 422 | |||
| 423 | Ok(()) | ||
| 424 | } | ||
| 282 | } | 425 | } |
| 283 | 426 | ||
| 284 | impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | 427 | impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { |
| @@ -350,117 +493,10 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { | |||
| 350 | 493 | ||
| 351 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { | 494 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> { |
| 352 | const CHANNEL: Channel = Channel::Ch1; | 495 | const CHANNEL: Channel = Channel::Ch1; |
| 353 | |||
| 354 | /// Write `data` to the DAC CH1 via DMA. | ||
| 355 | /// | ||
| 356 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 357 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 358 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 359 | /// | ||
| 360 | /// **Important:** Channel 1 has to be configured for the DAC instance! | ||
| 361 | async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 362 | where | ||
| 363 | Tx: Dma<T>, | ||
| 364 | { | ||
| 365 | write_inner(Self::CHANNEL, &self.dma, data, circular).await | ||
| 366 | } | ||
| 367 | } | 496 | } |
| 368 | 497 | ||
| 369 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { | 498 | impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> { |
| 370 | const CHANNEL: Channel = Channel::Ch2; | 499 | const CHANNEL: Channel = Channel::Ch2; |
| 371 | |||
| 372 | /// Write `data` to the DAC CH2 via DMA. | ||
| 373 | /// | ||
| 374 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 375 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 376 | /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. | ||
| 377 | /// | ||
| 378 | /// **Important:** Channel 2 has to be configured for the DAC instance! | ||
| 379 | async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 380 | where | ||
| 381 | Tx: Dma<T>, | ||
| 382 | { | ||
| 383 | write_inner(Self::CHANNEL, &self.dma, data, circular).await | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | /// Shared utility function to perform the actual DMA config and write. | ||
| 388 | async fn write_inner<T: Instance, Tx>( | ||
| 389 | ch: Channel, | ||
| 390 | dma: &PeripheralRef<'_, Tx>, | ||
| 391 | data: ValueArray<'_>, | ||
| 392 | circular: bool, | ||
| 393 | ) -> Result<(), Error> | ||
| 394 | where | ||
| 395 | Tx: Dma<T>, | ||
| 396 | { | ||
| 397 | let channel = ch.index(); | ||
| 398 | debug!("Writing to channel {}", channel); | ||
| 399 | |||
| 400 | // Enable DAC and DMA | ||
| 401 | T::regs().cr().modify(|w| { | ||
| 402 | w.set_en(channel, true); | ||
| 403 | w.set_dmaen(channel, true); | ||
| 404 | }); | ||
| 405 | |||
| 406 | let tx_request = dma.request(); | ||
| 407 | let dma_channel = dma; | ||
| 408 | |||
| 409 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 410 | let tx_f = match data { | ||
| 411 | ValueArray::Bit8(buf) => unsafe { | ||
| 412 | Transfer::new_write( | ||
| 413 | dma_channel, | ||
| 414 | tx_request, | ||
| 415 | buf, | ||
| 416 | T::regs().dhr8r(channel).as_ptr() as *mut u8, | ||
| 417 | TransferOptions { | ||
| 418 | circular, | ||
| 419 | half_transfer_ir: false, | ||
| 420 | complete_transfer_ir: !circular, | ||
| 421 | }, | ||
| 422 | ) | ||
| 423 | }, | ||
| 424 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 425 | Transfer::new_write( | ||
| 426 | dma_channel, | ||
| 427 | tx_request, | ||
| 428 | buf, | ||
| 429 | T::regs().dhr12l(channel).as_ptr() as *mut u16, | ||
| 430 | TransferOptions { | ||
| 431 | circular, | ||
| 432 | half_transfer_ir: false, | ||
| 433 | complete_transfer_ir: !circular, | ||
| 434 | }, | ||
| 435 | ) | ||
| 436 | }, | ||
| 437 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 438 | Transfer::new_write( | ||
| 439 | dma_channel, | ||
| 440 | tx_request, | ||
| 441 | buf, | ||
| 442 | T::regs().dhr12r(channel).as_ptr() as *mut u16, | ||
| 443 | TransferOptions { | ||
| 444 | circular, | ||
| 445 | half_transfer_ir: false, | ||
| 446 | complete_transfer_ir: !circular, | ||
| 447 | }, | ||
| 448 | ) | ||
| 449 | }, | ||
| 450 | }; | ||
| 451 | |||
| 452 | tx_f.await; | ||
| 453 | |||
| 454 | // finish dma | ||
| 455 | // TODO: Do we need to check any status registers here? | ||
| 456 | T::regs().cr().modify(|w| { | ||
| 457 | // Disable the DAC peripheral | ||
| 458 | w.set_en(channel, false); | ||
| 459 | // Disable the DMA. TODO: Is this necessary? | ||
| 460 | w.set_dmaen(channel, false); | ||
| 461 | }); | ||
| 462 | |||
| 463 | Ok(()) | ||
| 464 | } | 500 | } |
| 465 | 501 | ||
| 466 | pub(crate) mod sealed { | 502 | pub(crate) mod sealed { |
| @@ -470,7 +506,8 @@ pub(crate) mod sealed { | |||
| 470 | } | 506 | } |
| 471 | 507 | ||
| 472 | pub trait Instance: sealed::Instance + RccPeripheral + 'static {} | 508 | pub trait Instance: sealed::Instance + RccPeripheral + 'static {} |
| 473 | dma_trait!(Dma, Instance); | 509 | dma_trait!(DmaCh1, Instance); |
| 510 | dma_trait!(DmaCh2, Instance); | ||
| 474 | 511 | ||
| 475 | /// Marks a pin that can be used with the DAC | 512 | /// Marks a pin that can be used with the DAC |
| 476 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} | 513 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} |
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 8abe541d3..a5f828948 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs | |||
| @@ -29,6 +29,12 @@ pub struct TransferOptions { | |||
| 29 | pub flow_ctrl: FlowControl, | 29 | pub flow_ctrl: FlowControl, |
| 30 | /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. | 30 | /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. |
| 31 | pub fifo_threshold: Option<FifoThreshold>, | 31 | pub fifo_threshold: Option<FifoThreshold>, |
| 32 | /// Enable circular DMA | ||
| 33 | pub circular: bool, | ||
| 34 | /// Enable half transfer interrupt | ||
| 35 | pub half_transfer_ir: bool, | ||
| 36 | /// Enable transfer complete interrupt | ||
| 37 | pub complete_transfer_ir: bool, | ||
| 32 | } | 38 | } |
| 33 | 39 | ||
| 34 | impl Default for TransferOptions { | 40 | impl Default for TransferOptions { |
| @@ -38,6 +44,9 @@ impl Default for TransferOptions { | |||
| 38 | mburst: Burst::Single, | 44 | mburst: Burst::Single, |
| 39 | flow_ctrl: FlowControl::Dma, | 45 | flow_ctrl: FlowControl::Dma, |
| 40 | fifo_threshold: None, | 46 | fifo_threshold: None, |
| 47 | circular: false, | ||
| 48 | half_transfer_ir: false, | ||
| 49 | complete_transfer_ir: true, | ||
| 41 | } | 50 | } |
| 42 | } | 51 | } |
| 43 | } | 52 | } |
| @@ -366,13 +375,20 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 366 | }); | 375 | }); |
| 367 | w.set_pinc(vals::Inc::FIXED); | 376 | w.set_pinc(vals::Inc::FIXED); |
| 368 | w.set_teie(true); | 377 | w.set_teie(true); |
| 369 | w.set_tcie(true); | 378 | w.set_tcie(options.complete_transfer_ir); |
| 379 | w.set_htie(options.half_transfer_ir); | ||
| 370 | #[cfg(dma_v1)] | 380 | #[cfg(dma_v1)] |
| 371 | w.set_trbuff(true); | 381 | w.set_trbuff(true); |
| 372 | 382 | ||
| 373 | #[cfg(dma_v2)] | 383 | #[cfg(dma_v2)] |
| 374 | w.set_chsel(_request); | 384 | w.set_chsel(_request); |
| 375 | 385 | ||
| 386 | if options.circular { | ||
| 387 | w.set_circ(vals::Circ::ENABLED); | ||
| 388 | debug!("Setting circular mode"); | ||
| 389 | } else { | ||
| 390 | w.set_circ(vals::Circ::DISABLED); | ||
| 391 | } | ||
| 376 | w.set_pburst(options.pburst.into()); | 392 | w.set_pburst(options.pburst.into()); |
| 377 | w.set_mburst(options.mburst.into()); | 393 | w.set_mburst(options.mburst.into()); |
| 378 | w.set_pfctrl(options.flow_ctrl.into()); | 394 | w.set_pfctrl(options.flow_ctrl.into()); |
| @@ -404,8 +420,14 @@ impl<'a, C: Channel> Transfer<'a, C> { | |||
| 404 | } | 420 | } |
| 405 | 421 | ||
| 406 | pub fn is_running(&mut self) -> bool { | 422 | pub fn is_running(&mut self) -> bool { |
| 423 | //let ch = self.channel.regs().st(self.channel.num()); | ||
| 424 | //ch.cr().read().en() | ||
| 425 | |||
| 407 | let ch = self.channel.regs().st(self.channel.num()); | 426 | let ch = self.channel.regs().st(self.channel.num()); |
| 408 | ch.cr().read().en() | 427 | let en = ch.cr().read().en(); |
| 428 | let circular = ch.cr().read().circ() == vals::Circ::ENABLED; | ||
| 429 | let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; | ||
| 430 | en && (circular || !tcif) | ||
| 409 | } | 431 | } |
| 410 | 432 | ||
| 411 | /// Gets the total remaining transfers for the channel | 433 | /// Gets the total remaining transfers for the channel |
