diff options
| author | chasingRs <[email protected]> | 2025-11-10 04:16:05 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-10 04:16:05 -0800 |
| commit | f5d1c4eed74a9eb74c9903c78c7943c2ad41a3ef (patch) | |
| tree | 90ab86129479fdd1139eb9a711fe826eecd0384b /embassy-stm32/src | |
| parent | 944fda48a94c2d6cb6bea56c8c8471858d75da7d (diff) | |
| parent | 4ef7f91663b51e2cfeb6ef40d907bfff90737de8 (diff) | |
Merge branch 'embassy-rs:main' into fix/simple-pwm-32bit-timer-support
Diffstat (limited to 'embassy-stm32/src')
38 files changed, 2777 insertions, 737 deletions
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 5098aadd8..3767820cf 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,11 +1,14 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 1 | #[allow(unused)] | 3 | #[allow(unused)] |
| 2 | #[cfg(stm32h7)] | 4 | #[cfg(stm32h7)] |
| 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 5 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 4 | #[allow(unused)] | 6 | #[allow(unused)] |
| 5 | #[cfg(stm32g4)] | 7 | #[cfg(stm32g4)] |
| 6 | use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | 8 | pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; |
| 7 | use pac::adccommon::vals::Presc; | 9 | pub use pac::adccommon::vals::Presc; |
| 8 | use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | 10 | pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; |
| 11 | pub use stm32_metapac::adccommon::vals::Dual; | ||
| 9 | 12 | ||
| 10 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; | 13 | use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; |
| 11 | use crate::adc::SealedAdcChannel; | 14 | use crate::adc::SealedAdcChannel; |
| @@ -13,11 +16,19 @@ use crate::dma::Transfer; | |||
| 13 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 14 | use crate::{Peri, pac, rcc}; | 17 | use crate::{Peri, pac, rcc}; |
| 15 | 18 | ||
| 19 | mod ringbuffered; | ||
| 20 | pub use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 22 | mod injected; | ||
| 23 | pub use injected::InjectedAdc; | ||
| 24 | |||
| 16 | /// Default VREF voltage used for sample conversion to millivolts. | 25 | /// Default VREF voltage used for sample conversion to millivolts. |
| 17 | pub const VREF_DEFAULT_MV: u32 = 3300; | 26 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 18 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 27 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| 19 | pub const VREF_CALIB_MV: u32 = 3300; | 28 | pub const VREF_CALIB_MV: u32 = 3300; |
| 20 | 29 | ||
| 30 | const NR_INJECTED_RANKS: usize = 4; | ||
| 31 | |||
| 21 | /// Max single ADC operation clock frequency | 32 | /// Max single ADC operation clock frequency |
| 22 | #[cfg(stm32g4)] | 33 | #[cfg(stm32g4)] |
| 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | 34 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); |
| @@ -120,6 +131,24 @@ impl Prescaler { | |||
| 120 | } | 131 | } |
| 121 | } | 132 | } |
| 122 | 133 | ||
| 134 | // Trigger source for ADC conversions¨ | ||
| 135 | #[derive(Copy, Clone)] | ||
| 136 | pub struct ConversionTrigger { | ||
| 137 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers | ||
| 138 | // Note that Injected and Regular channels uses different mappings | ||
| 139 | pub channel: u8, | ||
| 140 | pub edge: Exten, | ||
| 141 | } | ||
| 142 | |||
| 143 | // Conversion mode for regular ADC channels | ||
| 144 | #[derive(Copy, Clone)] | ||
| 145 | pub enum RegularConversionMode { | ||
| 146 | // Samples as fast as possible | ||
| 147 | Continuous, | ||
| 148 | // Sample at rate determined by external trigger | ||
| 149 | Triggered(ConversionTrigger), | ||
| 150 | } | ||
| 151 | |||
| 123 | impl<'d, T: Instance> Adc<'d, T> { | 152 | impl<'d, T: Instance> Adc<'d, T> { |
| 124 | /// Create a new ADC driver. | 153 | /// Create a new ADC driver. |
| 125 | pub fn new(adc: Peri<'d, T>) -> Self { | 154 | pub fn new(adc: Peri<'d, T>) -> Self { |
| @@ -357,7 +386,29 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 357 | self.read_channel(channel) | 386 | self.read_channel(channel) |
| 358 | } | 387 | } |
| 359 | 388 | ||
| 360 | /// Read one or multiple ADC channels using DMA. | 389 | /// Start regular adc conversion |
| 390 | pub(super) fn start() { | ||
| 391 | T::regs().cr().modify(|reg| { | ||
| 392 | reg.set_adstart(true); | ||
| 393 | }); | ||
| 394 | } | ||
| 395 | |||
| 396 | /// Stop regular conversions | ||
| 397 | pub(super) fn stop() { | ||
| 398 | Self::stop_regular_conversions(); | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Teardown method for stopping regular ADC conversions | ||
| 402 | pub(super) fn teardown_adc() { | ||
| 403 | Self::stop_regular_conversions(); | ||
| 404 | |||
| 405 | // Disable dma control | ||
| 406 | T::regs().cfgr().modify(|reg| { | ||
| 407 | reg.set_dmaen(Dmaen::DISABLE); | ||
| 408 | }); | ||
| 409 | } | ||
| 410 | |||
| 411 | /// Read one or multiple ADC regular channels using DMA. | ||
| 361 | /// | 412 | /// |
| 362 | /// `sequence` iterator and `readings` must have the same length. | 413 | /// `sequence` iterator and `readings` must have the same length. |
| 363 | /// | 414 | /// |
| @@ -382,6 +433,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 382 | /// .await; | 433 | /// .await; |
| 383 | /// defmt::info!("measurements: {}", measurements); | 434 | /// defmt::info!("measurements: {}", measurements); |
| 384 | /// ``` | 435 | /// ``` |
| 436 | /// | ||
| 437 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use | ||
| 438 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 385 | pub async fn read( | 439 | pub async fn read( |
| 386 | &mut self, | 440 | &mut self, |
| 387 | rx_dma: Peri<'_, impl RxDma<T>>, | 441 | rx_dma: Peri<'_, impl RxDma<T>>, |
| @@ -399,18 +453,16 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 399 | ); | 453 | ); |
| 400 | 454 | ||
| 401 | // Ensure no conversions are ongoing and ADC is enabled. | 455 | // Ensure no conversions are ongoing and ADC is enabled. |
| 402 | Self::cancel_conversions(); | 456 | Self::stop_regular_conversions(); |
| 403 | self.enable(); | 457 | self.enable(); |
| 404 | 458 | ||
| 405 | // Set sequence length | 459 | // Set sequence length |
| 406 | T::regs().sqr1().modify(|w| { | 460 | T::regs().sqr1().modify(|w| { |
| 407 | w.set_l(sequence.len() as u8 - 1); | 461 | w.set_l(sequence.len() as u8 - 1); |
| 408 | }); | 462 | }); |
| 409 | |||
| 410 | // Configure channels and ranks | 463 | // Configure channels and ranks |
| 411 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 464 | for (_i, (channel, sample_time)) in sequence.enumerate() { |
| 412 | Self::configure_channel(channel, sample_time); | 465 | Self::configure_channel(channel, sample_time); |
| 413 | |||
| 414 | match _i { | 466 | match _i { |
| 415 | 0..=3 => { | 467 | 0..=3 => { |
| 416 | T::regs().sqr1().modify(|w| { | 468 | T::regs().sqr1().modify(|w| { |
| @@ -469,7 +521,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 469 | transfer.await; | 521 | transfer.await; |
| 470 | 522 | ||
| 471 | // Ensure conversions are finished. | 523 | // Ensure conversions are finished. |
| 472 | Self::cancel_conversions(); | 524 | Self::stop_regular_conversions(); |
| 473 | 525 | ||
| 474 | // Reset configuration. | 526 | // Reset configuration. |
| 475 | T::regs().cfgr().modify(|reg| { | 527 | T::regs().cfgr().modify(|reg| { |
| @@ -477,6 +529,277 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 477 | }); | 529 | }); |
| 478 | } | 530 | } |
| 479 | 531 | ||
| 532 | /// Set external trigger for regular conversion sequence | ||
| 533 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 534 | T::regs().cfgr().modify(|r| { | ||
| 535 | r.set_extsel(trigger.channel); | ||
| 536 | r.set_exten(trigger.edge); | ||
| 537 | }); | ||
| 538 | // Regular conversions uses DMA so no need to generate interrupt | ||
| 539 | T::regs().ier().modify(|r| r.set_eosie(false)); | ||
| 540 | } | ||
| 541 | |||
| 542 | // Dual ADC mode selection | ||
| 543 | pub fn configure_dual_mode(&mut self, val: Dual) { | ||
| 544 | T::common_regs().ccr().modify(|reg| { | ||
| 545 | reg.set_dual(val); | ||
| 546 | }) | ||
| 547 | } | ||
| 548 | |||
| 549 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 550 | /// | ||
| 551 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer | ||
| 552 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended | ||
| 553 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half | ||
| 554 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 555 | /// defines the period at which the buffer should be read. | ||
| 556 | /// | ||
| 557 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 558 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 559 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 560 | /// the buffer length should be `3 * 40 = 120`. | ||
| 561 | /// | ||
| 562 | /// # Parameters | ||
| 563 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. | ||
| 564 | /// - `dma_buf`: The buffer where DMA stores ADC samples. | ||
| 565 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. | ||
| 566 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 567 | /// | ||
| 568 | /// # Returns | ||
| 569 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 570 | pub fn into_ring_buffered<'a>( | ||
| 571 | mut self, | ||
| 572 | dma: Peri<'a, impl RxDma<T>>, | ||
| 573 | dma_buf: &'a mut [u16], | ||
| 574 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 575 | mode: RegularConversionMode, | ||
| 576 | ) -> RingBufferedAdc<'a, T> { | ||
| 577 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 578 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 579 | assert!( | ||
| 580 | sequence.len() <= 16, | ||
| 581 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 582 | ); | ||
| 583 | // reset conversions and enable the adc | ||
| 584 | Self::stop_regular_conversions(); | ||
| 585 | self.enable(); | ||
| 586 | |||
| 587 | //adc side setup | ||
| 588 | |||
| 589 | // Set sequence length | ||
| 590 | T::regs().sqr1().modify(|w| { | ||
| 591 | w.set_l(sequence.len() as u8 - 1); | ||
| 592 | }); | ||
| 593 | |||
| 594 | // Configure channels and ranks | ||
| 595 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { | ||
| 596 | Self::configure_channel(&mut channel, sample_time); | ||
| 597 | |||
| 598 | match _i { | ||
| 599 | 0..=3 => { | ||
| 600 | T::regs().sqr1().modify(|w| { | ||
| 601 | w.set_sq(_i, channel.channel()); | ||
| 602 | }); | ||
| 603 | } | ||
| 604 | 4..=8 => { | ||
| 605 | T::regs().sqr2().modify(|w| { | ||
| 606 | w.set_sq(_i - 4, channel.channel()); | ||
| 607 | }); | ||
| 608 | } | ||
| 609 | 9..=13 => { | ||
| 610 | T::regs().sqr3().modify(|w| { | ||
| 611 | w.set_sq(_i - 9, channel.channel()); | ||
| 612 | }); | ||
| 613 | } | ||
| 614 | 14..=15 => { | ||
| 615 | T::regs().sqr4().modify(|w| { | ||
| 616 | w.set_sq(_i - 14, channel.channel()); | ||
| 617 | }); | ||
| 618 | } | ||
| 619 | _ => unreachable!(), | ||
| 620 | } | ||
| 621 | } | ||
| 622 | |||
| 623 | // Clear overrun flag before starting transfer. | ||
| 624 | T::regs().isr().modify(|reg| { | ||
| 625 | reg.set_ovr(true); | ||
| 626 | }); | ||
| 627 | |||
| 628 | T::regs().cfgr().modify(|reg| { | ||
| 629 | reg.set_discen(false); // Convert all channels for each trigger | ||
| 630 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 631 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 632 | }); | ||
| 633 | |||
| 634 | match mode { | ||
| 635 | RegularConversionMode::Continuous => { | ||
| 636 | T::regs().cfgr().modify(|reg| { | ||
| 637 | reg.set_cont(true); | ||
| 638 | }); | ||
| 639 | } | ||
| 640 | RegularConversionMode::Triggered(trigger) => { | ||
| 641 | T::regs().cfgr().modify(|r| { | ||
| 642 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 643 | }); | ||
| 644 | self.set_regular_conversion_trigger(trigger); | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | mem::forget(self); | ||
| 649 | |||
| 650 | RingBufferedAdc::new(dma, dma_buf) | ||
| 651 | } | ||
| 652 | |||
| 653 | /// Configures the ADC for injected conversions. | ||
| 654 | /// | ||
| 655 | /// Injected conversions are separate from the regular conversion sequence and are typically | ||
| 656 | /// triggered by software or an external event. This method sets up a fixed-length sequence of | ||
| 657 | /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence | ||
| 658 | /// interrupt should be enabled. | ||
| 659 | /// | ||
| 660 | /// # Parameters | ||
| 661 | /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length | ||
| 662 | /// `N` determines the number of injected ranks to configure (maximum 4 for STM32). | ||
| 663 | /// - `trigger`: The trigger source that starts the injected conversion sequence. | ||
| 664 | /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions. | ||
| 665 | /// | ||
| 666 | /// # Returns | ||
| 667 | /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned | ||
| 668 | /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples. | ||
| 669 | /// | ||
| 670 | /// # Panics | ||
| 671 | /// This function will panic if: | ||
| 672 | /// - `sequence` is empty. | ||
| 673 | /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`). | ||
| 674 | /// | ||
| 675 | /// # Notes | ||
| 676 | /// - Injected conversions can run independently of regular ADC conversions. | ||
| 677 | /// - The order of channels in `sequence` determines the rank order in the injected sequence. | ||
| 678 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | ||
| 679 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | ||
| 680 | pub fn setup_injected_conversions<'a, const N: usize>( | ||
| 681 | mut self, | ||
| 682 | sequence: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 683 | trigger: ConversionTrigger, | ||
| 684 | interrupt: bool, | ||
| 685 | ) -> InjectedAdc<T, N> { | ||
| 686 | assert!(N != 0, "Read sequence cannot be empty"); | ||
| 687 | assert!( | ||
| 688 | N <= NR_INJECTED_RANKS, | ||
| 689 | "Read sequence cannot be more than {} in length", | ||
| 690 | NR_INJECTED_RANKS | ||
| 691 | ); | ||
| 692 | |||
| 693 | Self::stop_regular_conversions(); | ||
| 694 | self.enable(); | ||
| 695 | |||
| 696 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); | ||
| 697 | |||
| 698 | for (n, (mut channel, sample_time)) in sequence.into_iter().enumerate() { | ||
| 699 | Self::configure_channel(&mut channel, sample_time); | ||
| 700 | |||
| 701 | let idx = match n { | ||
| 702 | 0..=3 => n, | ||
| 703 | 4..=8 => n - 4, | ||
| 704 | 9..=13 => n - 9, | ||
| 705 | 14..=15 => n - 14, | ||
| 706 | _ => unreachable!(), | ||
| 707 | }; | ||
| 708 | |||
| 709 | T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel())); | ||
| 710 | } | ||
| 711 | |||
| 712 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); | ||
| 713 | |||
| 714 | self.set_injected_conversion_trigger(trigger); | ||
| 715 | self.enable_injected_eos_interrupt(interrupt); | ||
| 716 | Self::start_injected_conversions(); | ||
| 717 | |||
| 718 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels | ||
| 719 | } | ||
| 720 | |||
| 721 | /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions. | ||
| 722 | /// | ||
| 723 | /// # Parameters | ||
| 724 | /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers. | ||
| 725 | /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions. | ||
| 726 | /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions. | ||
| 727 | /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered). | ||
| 728 | /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`). | ||
| 729 | /// - `injected_trigger`: The trigger source for injected conversions. | ||
| 730 | /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions. | ||
| 731 | /// | ||
| 732 | /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode, | ||
| 733 | /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete. | ||
| 734 | /// | ||
| 735 | /// # Returns | ||
| 736 | /// A tuple containing: | ||
| 737 | /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA. | ||
| 738 | /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions. | ||
| 739 | /// | ||
| 740 | /// # Safety | ||
| 741 | /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the | ||
| 742 | /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. | ||
| 743 | /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. | ||
| 744 | pub fn into_ring_buffered_and_injected<'a, const N: usize>( | ||
| 745 | self, | ||
| 746 | dma: Peri<'a, impl RxDma<T>>, | ||
| 747 | dma_buf: &'a mut [u16], | ||
| 748 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, | ||
| 749 | regular_conversion_mode: RegularConversionMode, | ||
| 750 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 751 | injected_trigger: ConversionTrigger, | ||
| 752 | injected_interrupt: bool, | ||
| 753 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | ||
| 754 | unsafe { | ||
| 755 | ( | ||
| 756 | Self { | ||
| 757 | adc: self.adc.clone_unchecked(), | ||
| 758 | sample_time: self.sample_time, | ||
| 759 | } | ||
| 760 | .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode), | ||
| 761 | Self { | ||
| 762 | adc: self.adc.clone_unchecked(), | ||
| 763 | sample_time: self.sample_time, | ||
| 764 | } | ||
| 765 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), | ||
| 766 | ) | ||
| 767 | } | ||
| 768 | } | ||
| 769 | |||
| 770 | /// Stop injected conversions | ||
| 771 | pub(super) fn stop_injected_conversions() { | ||
| 772 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 773 | T::regs().cr().modify(|reg| { | ||
| 774 | reg.set_jadstp(Adstp::STOP); | ||
| 775 | }); | ||
| 776 | // The software must poll JADSTART until the bit is reset before assuming the | ||
| 777 | // ADC is completely stopped | ||
| 778 | while T::regs().cr().read().jadstart() {} | ||
| 779 | } | ||
| 780 | } | ||
| 781 | |||
| 782 | /// Start injected ADC conversion | ||
| 783 | pub(super) fn start_injected_conversions() { | ||
| 784 | T::regs().cr().modify(|reg| { | ||
| 785 | reg.set_jadstart(true); | ||
| 786 | }); | ||
| 787 | } | ||
| 788 | |||
| 789 | /// Set external trigger for injected conversion sequence | ||
| 790 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | ||
| 791 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | ||
| 792 | T::regs().jsqr().modify(|r| { | ||
| 793 | r.set_jextsel(trigger.channel); | ||
| 794 | r.set_jexten(trigger.edge); | ||
| 795 | }); | ||
| 796 | } | ||
| 797 | |||
| 798 | /// Enable end of injected sequence interrupt | ||
| 799 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { | ||
| 800 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | ||
| 801 | } | ||
| 802 | |||
| 480 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 803 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 481 | // Configure channel | 804 | // Configure channel |
| 482 | Self::set_channel_sample_time(channel.channel(), sample_time); | 805 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| @@ -509,16 +832,34 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 509 | } | 832 | } |
| 510 | } | 833 | } |
| 511 | 834 | ||
| 512 | fn cancel_conversions() { | 835 | // Stop regular conversions |
| 836 | fn stop_regular_conversions() { | ||
| 513 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 837 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 514 | T::regs().cr().modify(|reg| { | 838 | T::regs().cr().modify(|reg| { |
| 515 | reg.set_adstp(Adstp::STOP); | 839 | reg.set_adstp(Adstp::STOP); |
| 516 | }); | 840 | }); |
| 841 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 842 | // ADC is completely stopped | ||
| 517 | while T::regs().cr().read().adstart() {} | 843 | while T::regs().cr().read().adstart() {} |
| 518 | } | 844 | } |
| 519 | } | 845 | } |
| 520 | } | 846 | } |
| 521 | 847 | ||
| 848 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | ||
| 849 | /// Read sampled data from all injected ADC injected ranks | ||
| 850 | /// Clear the JEOS flag to allow a new injected sequence | ||
| 851 | pub(super) fn read_injected_data() -> [u16; N] { | ||
| 852 | let mut data = [0u16; N]; | ||
| 853 | for i in 0..N { | ||
| 854 | data[i] = T::regs().jdr(i).read().jdata(); | ||
| 855 | } | ||
| 856 | |||
| 857 | // Clear JEOS by writing 1 | ||
| 858 | T::regs().isr().modify(|r| r.set_jeos(true)); | ||
| 859 | data | ||
| 860 | } | ||
| 861 | } | ||
| 862 | |||
| 522 | /// Implemented for ADCs that have a Temperature channel | 863 | /// Implemented for ADCs that have a Temperature channel |
| 523 | pub trait TemperatureChannel { | 864 | pub trait TemperatureChannel { |
| 524 | const CHANNEL: u8; | 865 | const CHANNEL: u8; |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs new file mode 100644 index 000000000..0e4fe5847 --- /dev/null +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use super::{AnyAdcChannel, SampleTime}; | ||
| 8 | use crate::adc::Adc; | ||
| 9 | #[allow(unused_imports)] | ||
| 10 | use crate::adc::Instance; | ||
| 11 | |||
| 12 | /// Injected ADC sequence with owned channels. | ||
| 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | ||
| 14 | _channels: [(AnyAdcChannel<T>, SampleTime); N], | ||
| 15 | _phantom: PhantomData<T>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { | ||
| 19 | pub(crate) fn new(channels: [(AnyAdcChannel<T>, SampleTime); N]) -> Self { | ||
| 20 | Self { | ||
| 21 | _channels: channels, | ||
| 22 | _phantom: PhantomData, | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | pub fn stop_injected_conversions(&mut self) { | ||
| 27 | Adc::<T>::stop_injected_conversions() | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn start_injected_conversions(&mut self) { | ||
| 31 | Adc::<T>::start_injected_conversions() | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn read_injected_samples(&mut self) -> [u16; N] { | ||
| 35 | InjectedAdc::<T, N>::read_injected_data() | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | ||
| 40 | fn drop(&mut self) { | ||
| 41 | Adc::<T>::teardown_adc(); | ||
| 42 | compiler_fence(Ordering::SeqCst); | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 22ed8295f..ea7341f75 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -87,14 +87,18 @@ pub(crate) trait SealedAdcChannel<T> { | |||
| 87 | /// Performs a busy-wait delay for a specified number of microseconds. | 87 | /// Performs a busy-wait delay for a specified number of microseconds. |
| 88 | #[allow(unused)] | 88 | #[allow(unused)] |
| 89 | pub(crate) fn blocking_delay_us(us: u32) { | 89 | pub(crate) fn blocking_delay_us(us: u32) { |
| 90 | #[cfg(feature = "time")] | 90 | cfg_if::cfg_if! { |
| 91 | embassy_time::block_for(embassy_time::Duration::from_micros(us as u64)); | 91 | // this does strange things on stm32wlx in low power mode depending on exactly when it's called |
| 92 | #[cfg(not(feature = "time"))] | 92 | // as in sometimes 15 us (1 tick) would take > 20 seconds. |
| 93 | { | 93 | if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { |
| 94 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | 94 | let duration = embassy_time::Duration::from_micros(us as u64); |
| 95 | let us = us as u64; | 95 | embassy_time::block_for(duration); |
| 96 | let cycles = freq * us / 1_000_000; | 96 | } else { |
| 97 | cortex_m::asm::delay(cycles as u32); | 97 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; |
| 98 | let us = us as u64; | ||
| 99 | let cycles = freq * us / 1_000_000; | ||
| 100 | cortex_m::asm::delay(cycles as u32); | ||
| 101 | } | ||
| 98 | } | 102 | } |
| 99 | } | 103 | } |
| 100 | 104 | ||
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs new file mode 100644 index 000000000..971c8195c --- /dev/null +++ b/embassy-stm32/src/adc/ringbuffered.rs | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::adc::Adc; | ||
| 8 | #[allow(unused_imports)] | ||
| 9 | use crate::adc::{Instance, RxDma}; | ||
| 10 | #[allow(unused_imports)] | ||
| 11 | use crate::dma::{ReadableRingBuffer, TransferOptions}; | ||
| 12 | use crate::rcc; | ||
| 13 | |||
| 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 15 | pub struct OverrunError; | ||
| 16 | |||
| 17 | pub struct RingBufferedAdc<'d, T: Instance> { | ||
| 18 | _phantom: PhantomData<T>, | ||
| 19 | ring_buf: ReadableRingBuffer<'d, u16>, | ||
| 20 | } | ||
| 21 | |||
| 22 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | ||
| 23 | pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self { | ||
| 24 | //dma side setup | ||
| 25 | let opts = TransferOptions { | ||
| 26 | half_transfer_ir: true, | ||
| 27 | circular: true, | ||
| 28 | ..Default::default() | ||
| 29 | }; | ||
| 30 | |||
| 31 | // Safety: we forget the struct before this function returns. | ||
| 32 | let request = dma.request(); | ||
| 33 | |||
| 34 | let ring_buf = | ||
| 35 | unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) }; | ||
| 36 | |||
| 37 | Self { | ||
| 38 | _phantom: PhantomData, | ||
| 39 | ring_buf, | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 44 | pub fn start(&mut self) { | ||
| 45 | compiler_fence(Ordering::SeqCst); | ||
| 46 | self.ring_buf.start(); | ||
| 47 | |||
| 48 | Adc::<T>::start(); | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn stop(&mut self) { | ||
| 52 | Adc::<T>::stop(); | ||
| 53 | |||
| 54 | self.ring_buf.request_pause(); | ||
| 55 | |||
| 56 | compiler_fence(Ordering::SeqCst); | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn clear(&mut self) { | ||
| 60 | self.ring_buf.clear(); | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Reads measurements from the DMA ring buffer. | ||
| 64 | /// | ||
| 65 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 66 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. | ||
| 67 | /// Because interrupts are only generated if half or full DMA transfer completes. | ||
| 68 | /// | ||
| 69 | /// Each call to `read` will populate the `measurements` array in the same order as the channels | ||
| 70 | /// defined with `sequence`. There will be many sequences worth of measurements in this array | ||
| 71 | /// because it only returns if at least half of the DMA buffer is filled. For example if 2 | ||
| 72 | /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. | ||
| 73 | /// | ||
| 74 | /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly | ||
| 75 | /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that | ||
| 76 | /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size. | ||
| 77 | /// Example: | ||
| 78 | /// ```rust,ignore | ||
| 79 | /// const DMA_BUF_LEN: usize = 120; | ||
| 80 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 81 | /// | ||
| 82 | /// let mut adc = Adc::new(p.ADC1); | ||
| 83 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | ||
| 84 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | ||
| 85 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 86 | /// | ||
| 87 | /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered( | ||
| 88 | /// p.DMA2_CH0, | ||
| 89 | /// adc_dma_buf, [ | ||
| 90 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 91 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 92 | /// ].into_iter()); | ||
| 93 | /// | ||
| 94 | /// | ||
| 95 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 96 | /// loop { | ||
| 97 | /// match ring_buffered_adc.read(&mut measurements).await { | ||
| 98 | /// Ok(_) => { | ||
| 99 | /// defmt::info!("adc1: {}", measurements); | ||
| 100 | /// } | ||
| 101 | /// Err(e) => { | ||
| 102 | /// defmt::warn!("Error: {:?}", e); | ||
| 103 | /// } | ||
| 104 | /// } | ||
| 105 | /// } | ||
| 106 | /// ``` | ||
| 107 | /// | ||
| 108 | /// | ||
| 109 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 110 | /// [`start_continuous_sampling`]: #method.start_continuous_sampling | ||
| 111 | pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 112 | assert_eq!( | ||
| 113 | self.ring_buf.capacity() / 2, | ||
| 114 | measurements.len(), | ||
| 115 | "Buffer size must be half the size of the ring buffer" | ||
| 116 | ); | ||
| 117 | |||
| 118 | if !self.ring_buf.is_running() { | ||
| 119 | self.start(); | ||
| 120 | } | ||
| 121 | |||
| 122 | #[cfg(adc_v2)] | ||
| 123 | { | ||
| 124 | // Clear overrun flag if set. | ||
| 125 | if T::regs().sr().read().ovr() { | ||
| 126 | self.stop(); | ||
| 127 | |||
| 128 | return Err(OverrunError); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError) | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Read bytes that are readily available in the ring buffer. | ||
| 136 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 137 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 138 | /// | ||
| 139 | /// Background receive is started if `start_continuous_sampling()` has not been previously called. | ||
| 140 | /// | ||
| 141 | /// Receive in the background is terminated if an error is returned. | ||
| 142 | /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`. | ||
| 143 | pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> { | ||
| 144 | if !self.ring_buf.is_running() { | ||
| 145 | self.start(); | ||
| 146 | } | ||
| 147 | |||
| 148 | #[cfg(adc_v2)] | ||
| 149 | { | ||
| 150 | // Clear overrun flag if set. | ||
| 151 | if T::regs().sr().read().ovr() { | ||
| 152 | self.stop(); | ||
| 153 | |||
| 154 | return Err(OverrunError); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | loop { | ||
| 158 | match self.ring_buf.read(buf) { | ||
| 159 | Ok((0, _)) => {} | ||
| 160 | Ok((len, _)) => { | ||
| 161 | return Ok(len); | ||
| 162 | } | ||
| 163 | Err(_) => { | ||
| 164 | self.stop(); | ||
| 165 | |||
| 166 | return Err(OverrunError); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | ||
| 174 | fn drop(&mut self) { | ||
| 175 | Adc::<T>::teardown_adc(); | ||
| 176 | |||
| 177 | compiler_fence(Ordering::SeqCst); | ||
| 178 | |||
| 179 | self.ring_buf.request_pause(); | ||
| 180 | rcc::disable::<T>(); | ||
| 181 | } | ||
| 182 | } | ||
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs deleted file mode 100644 index 9b2e5b8fe..000000000 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ /dev/null | |||
| @@ -1,432 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::mem; | ||
| 3 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 4 | |||
| 5 | use stm32_metapac::adc::vals::SampleTime; | ||
| 6 | |||
| 7 | use crate::adc::{Adc, AdcChannel, Instance, RxDma}; | ||
| 8 | use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; | ||
| 9 | use crate::pac::adc::vals; | ||
| 10 | use crate::{Peri, rcc}; | ||
| 11 | |||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub struct OverrunError; | ||
| 14 | |||
| 15 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | ||
| 16 | r.sr().modify(|regs| { | ||
| 17 | regs.set_eoc(false); | ||
| 18 | regs.set_ovr(false); | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | #[derive(PartialOrd, PartialEq, Debug, Clone, Copy)] | ||
| 23 | pub enum Sequence { | ||
| 24 | One, | ||
| 25 | Two, | ||
| 26 | Three, | ||
| 27 | Four, | ||
| 28 | Five, | ||
| 29 | Six, | ||
| 30 | Seven, | ||
| 31 | Eight, | ||
| 32 | Nine, | ||
| 33 | Ten, | ||
| 34 | Eleven, | ||
| 35 | Twelve, | ||
| 36 | Thirteen, | ||
| 37 | Fourteen, | ||
| 38 | Fifteen, | ||
| 39 | Sixteen, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl From<Sequence> for u8 { | ||
| 43 | fn from(s: Sequence) -> u8 { | ||
| 44 | match s { | ||
| 45 | Sequence::One => 0, | ||
| 46 | Sequence::Two => 1, | ||
| 47 | Sequence::Three => 2, | ||
| 48 | Sequence::Four => 3, | ||
| 49 | Sequence::Five => 4, | ||
| 50 | Sequence::Six => 5, | ||
| 51 | Sequence::Seven => 6, | ||
| 52 | Sequence::Eight => 7, | ||
| 53 | Sequence::Nine => 8, | ||
| 54 | Sequence::Ten => 9, | ||
| 55 | Sequence::Eleven => 10, | ||
| 56 | Sequence::Twelve => 11, | ||
| 57 | Sequence::Thirteen => 12, | ||
| 58 | Sequence::Fourteen => 13, | ||
| 59 | Sequence::Fifteen => 14, | ||
| 60 | Sequence::Sixteen => 15, | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl From<u8> for Sequence { | ||
| 66 | fn from(val: u8) -> Self { | ||
| 67 | match val { | ||
| 68 | 0 => Sequence::One, | ||
| 69 | 1 => Sequence::Two, | ||
| 70 | 2 => Sequence::Three, | ||
| 71 | 3 => Sequence::Four, | ||
| 72 | 4 => Sequence::Five, | ||
| 73 | 5 => Sequence::Six, | ||
| 74 | 6 => Sequence::Seven, | ||
| 75 | 7 => Sequence::Eight, | ||
| 76 | 8 => Sequence::Nine, | ||
| 77 | 9 => Sequence::Ten, | ||
| 78 | 10 => Sequence::Eleven, | ||
| 79 | 11 => Sequence::Twelve, | ||
| 80 | 12 => Sequence::Thirteen, | ||
| 81 | 13 => Sequence::Fourteen, | ||
| 82 | 14 => Sequence::Fifteen, | ||
| 83 | 15 => Sequence::Sixteen, | ||
| 84 | _ => panic!("Invalid sequence number"), | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | pub struct RingBufferedAdc<'d, T: Instance> { | ||
| 90 | _phantom: PhantomData<T>, | ||
| 91 | ring_buf: ReadableRingBuffer<'d, u16>, | ||
| 92 | } | ||
| 93 | |||
| 94 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 95 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 96 | /// | ||
| 97 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 98 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 99 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 100 | /// | ||
| 101 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 102 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 103 | /// | ||
| 104 | /// [`read`]: #method.read | ||
| 105 | pub fn into_ring_buffered(self, dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> RingBufferedAdc<'d, T> { | ||
| 106 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 107 | |||
| 108 | let opts: crate::dma::TransferOptions = TransferOptions { | ||
| 109 | half_transfer_ir: true, | ||
| 110 | priority: Priority::VeryHigh, | ||
| 111 | ..Default::default() | ||
| 112 | }; | ||
| 113 | |||
| 114 | // Safety: we forget the struct before this function returns. | ||
| 115 | let rx_src = T::regs().dr().as_ptr() as *mut u16; | ||
| 116 | let request = dma.request(); | ||
| 117 | |||
| 118 | let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) }; | ||
| 119 | |||
| 120 | // Don't disable the clock | ||
| 121 | mem::forget(self); | ||
| 122 | |||
| 123 | RingBufferedAdc { | ||
| 124 | _phantom: PhantomData, | ||
| 125 | ring_buf, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'d, T: Instance> RingBufferedAdc<'d, T> { | ||
| 131 | fn is_on() -> bool { | ||
| 132 | T::regs().cr2().read().adon() | ||
| 133 | } | ||
| 134 | |||
| 135 | fn stop_adc() { | ||
| 136 | T::regs().cr2().modify(|reg| { | ||
| 137 | reg.set_adon(false); | ||
| 138 | }); | ||
| 139 | } | ||
| 140 | |||
| 141 | fn start_adc() { | ||
| 142 | T::regs().cr2().modify(|reg| { | ||
| 143 | reg.set_adon(true); | ||
| 144 | }); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Sets the channel sample time | ||
| 148 | /// | ||
| 149 | /// ## SAFETY: | ||
| 150 | /// - ADON == 0 i.e ADC must not be enabled when this is called. | ||
| 151 | unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { | ||
| 152 | if ch <= 9 { | ||
| 153 | T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); | ||
| 154 | } else { | ||
| 155 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) { | ||
| 160 | let ch_iter = ch.iter(); | ||
| 161 | for idx in ch_iter { | ||
| 162 | unsafe { | ||
| 163 | Self::set_channel_sample_time(*idx, sample_time); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | pub fn set_sample_sequence( | ||
| 169 | &mut self, | ||
| 170 | sequence: Sequence, | ||
| 171 | channel: &mut impl AdcChannel<T>, | ||
| 172 | sample_time: SampleTime, | ||
| 173 | ) { | ||
| 174 | let was_on = Self::is_on(); | ||
| 175 | if !was_on { | ||
| 176 | Self::start_adc(); | ||
| 177 | } | ||
| 178 | |||
| 179 | // Check the sequence is long enough | ||
| 180 | T::regs().sqr1().modify(|r| { | ||
| 181 | let prev: Sequence = r.l().into(); | ||
| 182 | if prev < sequence { | ||
| 183 | let new_l: Sequence = sequence; | ||
| 184 | trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8); | ||
| 185 | r.set_l(sequence.into()) | ||
| 186 | } else { | ||
| 187 | r.set_l(prev.into()) | ||
| 188 | } | ||
| 189 | }); | ||
| 190 | |||
| 191 | // Set this GPIO as an analog input. | ||
| 192 | channel.setup(); | ||
| 193 | |||
| 194 | // Set the channel in the right sequence field. | ||
| 195 | match sequence { | ||
| 196 | Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())), | ||
| 197 | Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())), | ||
| 198 | Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())), | ||
| 199 | Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), | ||
| 200 | Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), | ||
| 201 | Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), | ||
| 202 | Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())), | ||
| 203 | Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())), | ||
| 204 | Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())), | ||
| 205 | Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())), | ||
| 206 | Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())), | ||
| 207 | Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())), | ||
| 208 | Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())), | ||
| 209 | Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())), | ||
| 210 | Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())), | ||
| 211 | Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())), | ||
| 212 | }; | ||
| 213 | |||
| 214 | if !was_on { | ||
| 215 | Self::stop_adc(); | ||
| 216 | } | ||
| 217 | |||
| 218 | self.set_channels_sample_time(&[channel.channel()], sample_time); | ||
| 219 | |||
| 220 | Self::start_adc(); | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | ||
| 224 | pub fn start(&mut self) -> Result<(), OverrunError> { | ||
| 225 | self.setup_adc(); | ||
| 226 | self.ring_buf.clear(); | ||
| 227 | |||
| 228 | Ok(()) | ||
| 229 | } | ||
| 230 | |||
| 231 | fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> { | ||
| 232 | self.teardown_adc(); | ||
| 233 | Err(err) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Stops DMA transfer. | ||
| 237 | /// It does not turn off ADC. | ||
| 238 | /// Calling `start` restarts continuous DMA transfer. | ||
| 239 | /// | ||
| 240 | /// [`start`]: #method.start | ||
| 241 | pub fn teardown_adc(&mut self) { | ||
| 242 | // Stop the DMA transfer | ||
| 243 | self.ring_buf.request_pause(); | ||
| 244 | |||
| 245 | let r = T::regs(); | ||
| 246 | |||
| 247 | // Stop ADC | ||
| 248 | r.cr2().modify(|reg| { | ||
| 249 | // Stop ADC | ||
| 250 | reg.set_swstart(false); | ||
| 251 | // Stop DMA | ||
| 252 | reg.set_dma(false); | ||
| 253 | }); | ||
| 254 | |||
| 255 | r.cr1().modify(|w| { | ||
| 256 | // Disable interrupt for end of conversion | ||
| 257 | w.set_eocie(false); | ||
| 258 | // Disable interrupt for overrun | ||
| 259 | w.set_ovrie(false); | ||
| 260 | }); | ||
| 261 | |||
| 262 | clear_interrupt_flags(r); | ||
| 263 | |||
| 264 | compiler_fence(Ordering::SeqCst); | ||
| 265 | } | ||
| 266 | |||
| 267 | fn setup_adc(&mut self) { | ||
| 268 | compiler_fence(Ordering::SeqCst); | ||
| 269 | |||
| 270 | self.ring_buf.start(); | ||
| 271 | |||
| 272 | let r = T::regs(); | ||
| 273 | |||
| 274 | // Enable ADC | ||
| 275 | let was_on = Self::is_on(); | ||
| 276 | if !was_on { | ||
| 277 | r.cr2().modify(|reg| { | ||
| 278 | reg.set_adon(false); | ||
| 279 | reg.set_swstart(false); | ||
| 280 | }); | ||
| 281 | } | ||
| 282 | |||
| 283 | // Clear all interrupts | ||
| 284 | r.sr().modify(|regs| { | ||
| 285 | regs.set_eoc(false); | ||
| 286 | regs.set_ovr(false); | ||
| 287 | regs.set_strt(false); | ||
| 288 | }); | ||
| 289 | |||
| 290 | r.cr1().modify(|w| { | ||
| 291 | // Enable interrupt for end of conversion | ||
| 292 | w.set_eocie(true); | ||
| 293 | // Enable interrupt for overrun | ||
| 294 | w.set_ovrie(true); | ||
| 295 | // Scanning converisons of multiple channels | ||
| 296 | w.set_scan(true); | ||
| 297 | // Continuous conversion mode | ||
| 298 | w.set_discen(false); | ||
| 299 | }); | ||
| 300 | |||
| 301 | r.cr2().modify(|w| { | ||
| 302 | // Enable DMA mode | ||
| 303 | w.set_dma(true); | ||
| 304 | // Enable continuous conversions | ||
| 305 | w.set_cont(true); | ||
| 306 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 307 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 308 | // EOC flag is set at the end of each conversion. | ||
| 309 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 310 | }); | ||
| 311 | |||
| 312 | // Begin ADC conversions | ||
| 313 | T::regs().cr2().modify(|reg| { | ||
| 314 | reg.set_adon(true); | ||
| 315 | reg.set_swstart(true); | ||
| 316 | }); | ||
| 317 | |||
| 318 | super::blocking_delay_us(3); | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Read bytes that are readily available in the ring buffer. | ||
| 322 | /// If no bytes are currently available in the buffer the call waits until the some | ||
| 323 | /// bytes are available (at least one byte and at most half the buffer size) | ||
| 324 | /// | ||
| 325 | /// Background receive is started if `start()` has not been previously called. | ||
| 326 | /// | ||
| 327 | /// Receive in the background is terminated if an error is returned. | ||
| 328 | /// It must then manually be started again by calling `start()` or by re-calling `read()`. | ||
| 329 | pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 330 | let r = T::regs(); | ||
| 331 | |||
| 332 | // Start background receive if it was not already started | ||
| 333 | if !r.cr2().read().dma() { | ||
| 334 | self.start()?; | ||
| 335 | } | ||
| 336 | |||
| 337 | // Clear overrun flag if set. | ||
| 338 | if r.sr().read().ovr() { | ||
| 339 | return self.stop(OverrunError); | ||
| 340 | } | ||
| 341 | |||
| 342 | loop { | ||
| 343 | match self.ring_buf.read(buf) { | ||
| 344 | Ok((0, _)) => {} | ||
| 345 | Ok((len, _)) => { | ||
| 346 | return Ok(len); | ||
| 347 | } | ||
| 348 | Err(_) => { | ||
| 349 | return self.stop(OverrunError); | ||
| 350 | } | ||
| 351 | } | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Reads measurements from the DMA ring buffer. | ||
| 356 | /// | ||
| 357 | /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. | ||
| 358 | /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. | ||
| 359 | /// | ||
| 360 | /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`. | ||
| 361 | /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. | ||
| 362 | /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`. | ||
| 363 | /// | ||
| 364 | /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again. | ||
| 365 | /// | ||
| 366 | /// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval. | ||
| 367 | /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected. | ||
| 368 | /// | ||
| 369 | /// Example: | ||
| 370 | /// ```rust,ignore | ||
| 371 | /// const DMA_BUF_LEN: usize = 120; | ||
| 372 | /// let adc_dma_buf = [0u16; DMA_BUF_LEN]; | ||
| 373 | /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf); | ||
| 374 | /// | ||
| 375 | /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); | ||
| 376 | /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112); | ||
| 377 | /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112); | ||
| 378 | /// | ||
| 379 | /// let mut measurements = [0u16; DMA_BUF_LEN / 2]; | ||
| 380 | /// loop { | ||
| 381 | /// match adc.read(&mut measurements).await { | ||
| 382 | /// Ok(_) => { | ||
| 383 | /// defmt::info!("adc1: {}", measurements); | ||
| 384 | /// // Only needed to manually control sample rate. | ||
| 385 | /// adc.teardown_adc(); | ||
| 386 | /// } | ||
| 387 | /// Err(e) => { | ||
| 388 | /// defmt::warn!("Error: {:?}", e); | ||
| 389 | /// // DMA overrun, next call to `read` restarts ADC. | ||
| 390 | /// } | ||
| 391 | /// } | ||
| 392 | /// | ||
| 393 | /// // Manually control sample rate. | ||
| 394 | /// Timer::after_millis(100).await; | ||
| 395 | /// } | ||
| 396 | /// ``` | ||
| 397 | /// | ||
| 398 | /// | ||
| 399 | /// [`set_sample_sequence`]: #method.set_sample_sequence | ||
| 400 | /// [`teardown_adc`]: #method.teardown_adc | ||
| 401 | /// [`start`]: #method.start | ||
| 402 | pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> { | ||
| 403 | assert_eq!( | ||
| 404 | self.ring_buf.capacity() / 2, | ||
| 405 | N, | ||
| 406 | "Buffer size must be half the size of the ring buffer" | ||
| 407 | ); | ||
| 408 | |||
| 409 | let r = T::regs(); | ||
| 410 | |||
| 411 | // Start background receive if it was not already started | ||
| 412 | if !r.cr2().read().dma() { | ||
| 413 | self.start()?; | ||
| 414 | } | ||
| 415 | |||
| 416 | // Clear overrun flag if set. | ||
| 417 | if r.sr().read().ovr() { | ||
| 418 | return self.stop(OverrunError); | ||
| 419 | } | ||
| 420 | match self.ring_buf.read_exact(measurements).await { | ||
| 421 | Ok(len) => Ok(len), | ||
| 422 | Err(_) => self.stop(OverrunError), | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | impl<T: Instance> Drop for RingBufferedAdc<'_, T> { | ||
| 428 | fn drop(&mut self) { | ||
| 429 | self.teardown_adc(); | ||
| 430 | rcc::disable::<T>(); | ||
| 431 | } | ||
| 432 | } | ||
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 93ec78548..90c6294d2 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,11 +1,22 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 1 | use super::blocking_delay_us; | 4 | use super::blocking_delay_us; |
| 2 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 5 | use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; |
| 6 | use crate::pac::adc::vals; | ||
| 3 | use crate::peripherals::ADC1; | 7 | use crate::peripherals::ADC1; |
| 4 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 5 | use crate::{Peri, rcc}; | 9 | use crate::{Peri, rcc}; |
| 6 | 10 | ||
| 7 | mod ringbuffered_v2; | 11 | mod ringbuffered; |
| 8 | pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; | 12 | pub use ringbuffered::RingBufferedAdc; |
| 13 | |||
| 14 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | ||
| 15 | r.sr().modify(|regs| { | ||
| 16 | regs.set_eoc(false); | ||
| 17 | regs.set_ovr(false); | ||
| 18 | }); | ||
| 19 | } | ||
| 9 | 20 | ||
| 10 | /// Default VREF voltage used for sample conversion to millivolts. | 21 | /// Default VREF voltage used for sample conversion to millivolts. |
| 11 | pub const VREF_DEFAULT_MV: u32 = 3300; | 22 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -112,6 +123,98 @@ where | |||
| 112 | } | 123 | } |
| 113 | } | 124 | } |
| 114 | 125 | ||
| 126 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 127 | /// | ||
| 128 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 129 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 130 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 131 | /// | ||
| 132 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 133 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 134 | /// | ||
| 135 | /// [`read`]: #method.read | ||
| 136 | pub fn into_ring_buffered<'a>( | ||
| 137 | self, | ||
| 138 | dma: Peri<'d, impl RxDma<T>>, | ||
| 139 | dma_buf: &'d mut [u16], | ||
| 140 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | ||
| 141 | ) -> RingBufferedAdc<'d, T> { | ||
| 142 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 143 | |||
| 144 | T::regs().cr2().modify(|reg| { | ||
| 145 | reg.set_adon(true); | ||
| 146 | }); | ||
| 147 | |||
| 148 | // Check the sequence is long enough | ||
| 149 | T::regs().sqr1().modify(|r| { | ||
| 150 | r.set_l((sequence.len() - 1).try_into().unwrap()); | ||
| 151 | }); | ||
| 152 | |||
| 153 | for (i, (channel, sample_time)) in sequence.enumerate() { | ||
| 154 | // Set this GPIO as an analog input. | ||
| 155 | channel.setup(); | ||
| 156 | |||
| 157 | // Set the channel in the right sequence field. | ||
| 158 | T::regs().sqr3().modify(|w| w.set_sq(i, channel.channel())); | ||
| 159 | |||
| 160 | Self::set_channel_sample_time(channel.channel(), sample_time); | ||
| 161 | } | ||
| 162 | |||
| 163 | compiler_fence(Ordering::SeqCst); | ||
| 164 | |||
| 165 | let r = T::regs(); | ||
| 166 | |||
| 167 | // Clear all interrupts | ||
| 168 | r.sr().modify(|regs| { | ||
| 169 | regs.set_eoc(false); | ||
| 170 | regs.set_ovr(false); | ||
| 171 | regs.set_strt(false); | ||
| 172 | }); | ||
| 173 | |||
| 174 | r.cr1().modify(|w| { | ||
| 175 | // Enable interrupt for end of conversion | ||
| 176 | w.set_eocie(true); | ||
| 177 | // Enable interrupt for overrun | ||
| 178 | w.set_ovrie(true); | ||
| 179 | // Scanning converisons of multiple channels | ||
| 180 | w.set_scan(true); | ||
| 181 | // Continuous conversion mode | ||
| 182 | w.set_discen(false); | ||
| 183 | }); | ||
| 184 | |||
| 185 | r.cr2().modify(|w| { | ||
| 186 | // Enable DMA mode | ||
| 187 | w.set_dma(true); | ||
| 188 | // Enable continuous conversions | ||
| 189 | w.set_cont(true); | ||
| 190 | // DMA requests are issues as long as DMA=1 and data are converted. | ||
| 191 | w.set_dds(vals::Dds::CONTINUOUS); | ||
| 192 | // EOC flag is set at the end of each conversion. | ||
| 193 | w.set_eocs(vals::Eocs::EACH_CONVERSION); | ||
| 194 | }); | ||
| 195 | |||
| 196 | // Don't disable the clock | ||
| 197 | mem::forget(self); | ||
| 198 | |||
| 199 | RingBufferedAdc::new(dma, dma_buf) | ||
| 200 | } | ||
| 201 | |||
| 202 | pub(super) fn start() { | ||
| 203 | // Begin ADC conversions | ||
| 204 | T::regs().cr2().modify(|reg| { | ||
| 205 | reg.set_adon(true); | ||
| 206 | reg.set_swstart(true); | ||
| 207 | }); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub(super) fn stop() { | ||
| 211 | // Stop ADC | ||
| 212 | T::regs().cr2().modify(|reg| { | ||
| 213 | // Stop ADC | ||
| 214 | reg.set_swstart(false); | ||
| 215 | }); | ||
| 216 | } | ||
| 217 | |||
| 115 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 218 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { |
| 116 | self.sample_time = sample_time; | 219 | self.sample_time = sample_time; |
| 117 | } | 220 | } |
| @@ -198,6 +301,31 @@ where | |||
| 198 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | 301 | T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); |
| 199 | } | 302 | } |
| 200 | } | 303 | } |
| 304 | |||
| 305 | pub(super) fn teardown_adc() { | ||
| 306 | let r = T::regs(); | ||
| 307 | |||
| 308 | // Stop ADC | ||
| 309 | r.cr2().modify(|reg| { | ||
| 310 | // Stop ADC | ||
| 311 | reg.set_swstart(false); | ||
| 312 | // Stop ADC | ||
| 313 | reg.set_adon(false); | ||
| 314 | // Stop DMA | ||
| 315 | reg.set_dma(false); | ||
| 316 | }); | ||
| 317 | |||
| 318 | r.cr1().modify(|w| { | ||
| 319 | // Disable interrupt for end of conversion | ||
| 320 | w.set_eocie(false); | ||
| 321 | // Disable interrupt for overrun | ||
| 322 | w.set_ovrie(false); | ||
| 323 | }); | ||
| 324 | |||
| 325 | clear_interrupt_flags(r); | ||
| 326 | |||
| 327 | compiler_fence(Ordering::SeqCst); | ||
| 328 | } | ||
| 201 | } | 329 | } |
| 202 | 330 | ||
| 203 | impl<'d, T: Instance> Drop for Adc<'d, T> { | 331 | impl<'d, T: Instance> Drop for Adc<'d, T> { |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 47632263b..170b08a25 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -12,6 +12,13 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; | |||
| 12 | use super::{ | 12 | use super::{ |
| 13 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, | 13 | Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, |
| 14 | }; | 14 | }; |
| 15 | |||
| 16 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 17 | mod ringbuffered; | ||
| 18 | |||
| 19 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 20 | use ringbuffered::RingBufferedAdc; | ||
| 21 | |||
| 15 | use crate::dma::Transfer; | 22 | use crate::dma::Transfer; |
| 16 | use crate::{Peri, pac, rcc}; | 23 | use crate::{Peri, pac, rcc}; |
| 17 | 24 | ||
| @@ -174,6 +181,38 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 174 | blocking_delay_us(1); | 181 | blocking_delay_us(1); |
| 175 | } | 182 | } |
| 176 | 183 | ||
| 184 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 185 | pub(super) fn start() { | ||
| 186 | // Start adc conversion | ||
| 187 | T::regs().cr().modify(|reg| { | ||
| 188 | reg.set_adstart(true); | ||
| 189 | }); | ||
| 190 | } | ||
| 191 | |||
| 192 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 193 | pub(super) fn stop() { | ||
| 194 | // Stop adc conversion | ||
| 195 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 196 | T::regs().cr().modify(|reg| { | ||
| 197 | reg.set_adstp(true); | ||
| 198 | }); | ||
| 199 | while T::regs().cr().read().adstart() {} | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 204 | pub(super) fn teardown_adc() { | ||
| 205 | //disable dma control | ||
| 206 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 207 | T::regs().cfgr().modify(|reg| { | ||
| 208 | reg.set_dmaen(false); | ||
| 209 | }); | ||
| 210 | #[cfg(any(adc_g0, adc_u0))] | ||
| 211 | T::regs().cfgr1().modify(|reg| { | ||
| 212 | reg.set_dmaen(false); | ||
| 213 | }); | ||
| 214 | } | ||
| 215 | |||
| 177 | /// Initialize the ADC leaving any analog clock at reset value. | 216 | /// Initialize the ADC leaving any analog clock at reset value. |
| 178 | /// For G0 and WL, this is the async clock without prescaler. | 217 | /// For G0 and WL, this is the async clock without prescaler. |
| 179 | pub fn new(adc: Peri<'d, T>) -> Self { | 218 | pub fn new(adc: Peri<'d, T>) -> Self { |
| @@ -423,6 +462,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 423 | "Asynchronous read sequence cannot be more than 16 in length" | 462 | "Asynchronous read sequence cannot be more than 16 in length" |
| 424 | ); | 463 | ); |
| 425 | 464 | ||
| 465 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 466 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 467 | |||
| 426 | // Ensure no conversions are ongoing and ADC is enabled. | 468 | // Ensure no conversions are ongoing and ADC is enabled. |
| 427 | Self::cancel_conversions(); | 469 | Self::cancel_conversions(); |
| 428 | self.enable(); | 470 | self.enable(); |
| @@ -559,6 +601,137 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 559 | }); | 601 | }); |
| 560 | } | 602 | } |
| 561 | 603 | ||
| 604 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | ||
| 605 | /// | ||
| 606 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | ||
| 607 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | ||
| 608 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | ||
| 609 | /// | ||
| 610 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. | ||
| 611 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | ||
| 612 | /// | ||
| 613 | /// [`read`]: #method.read | ||
| 614 | #[cfg(any(adc_v3, adc_g0, adc_u0))] | ||
| 615 | pub fn into_ring_buffered<'a>( | ||
| 616 | &mut self, | ||
| 617 | dma: Peri<'a, impl RxDma<T>>, | ||
| 618 | dma_buf: &'a mut [u16], | ||
| 619 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | ||
| 620 | ) -> RingBufferedAdc<'a, T> { | ||
| 621 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | ||
| 622 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 623 | assert!( | ||
| 624 | sequence.len() <= 16, | ||
| 625 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 626 | ); | ||
| 627 | // reset conversions and enable the adc | ||
| 628 | Self::cancel_conversions(); | ||
| 629 | self.enable(); | ||
| 630 | |||
| 631 | //adc side setup | ||
| 632 | |||
| 633 | // Set sequence length | ||
| 634 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 635 | T::regs().sqr1().modify(|w| { | ||
| 636 | w.set_l(sequence.len() as u8 - 1); | ||
| 637 | }); | ||
| 638 | |||
| 639 | #[cfg(adc_g0)] | ||
| 640 | { | ||
| 641 | let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new(); | ||
| 642 | |||
| 643 | T::regs().chselr().write(|chselr| { | ||
| 644 | T::regs().smpr().write(|smpr| { | ||
| 645 | for (channel, sample_time) in sequence { | ||
| 646 | chselr.set_chsel(channel.channel.into(), true); | ||
| 647 | if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { | ||
| 648 | smpr.set_smpsel(channel.channel.into(), (i as u8).into()); | ||
| 649 | } else { | ||
| 650 | smpr.set_sample_time(sample_times.len(), sample_time); | ||
| 651 | if let Err(_) = sample_times.push(sample_time) { | ||
| 652 | panic!( | ||
| 653 | "Implementation is limited to {} unique sample times among all channels.", | ||
| 654 | SAMPLE_TIMES_CAPACITY | ||
| 655 | ); | ||
| 656 | } | ||
| 657 | } | ||
| 658 | } | ||
| 659 | }) | ||
| 660 | }); | ||
| 661 | } | ||
| 662 | #[cfg(not(adc_g0))] | ||
| 663 | { | ||
| 664 | #[cfg(adc_u0)] | ||
| 665 | let mut channel_mask = 0; | ||
| 666 | |||
| 667 | // Configure channels and ranks | ||
| 668 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 669 | Self::configure_channel(channel, sample_time); | ||
| 670 | |||
| 671 | // Each channel is sampled according to sequence | ||
| 672 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 673 | match _i { | ||
| 674 | 0..=3 => { | ||
| 675 | T::regs().sqr1().modify(|w| { | ||
| 676 | w.set_sq(_i, channel.channel()); | ||
| 677 | }); | ||
| 678 | } | ||
| 679 | 4..=8 => { | ||
| 680 | T::regs().sqr2().modify(|w| { | ||
| 681 | w.set_sq(_i - 4, channel.channel()); | ||
| 682 | }); | ||
| 683 | } | ||
| 684 | 9..=13 => { | ||
| 685 | T::regs().sqr3().modify(|w| { | ||
| 686 | w.set_sq(_i - 9, channel.channel()); | ||
| 687 | }); | ||
| 688 | } | ||
| 689 | 14..=15 => { | ||
| 690 | T::regs().sqr4().modify(|w| { | ||
| 691 | w.set_sq(_i - 14, channel.channel()); | ||
| 692 | }); | ||
| 693 | } | ||
| 694 | _ => unreachable!(), | ||
| 695 | } | ||
| 696 | |||
| 697 | #[cfg(adc_u0)] | ||
| 698 | { | ||
| 699 | channel_mask |= 1 << channel.channel(); | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | // On G0 and U0 enabled channels are sampled from 0 to last channel. | ||
| 704 | // It is possible to add up to 8 sequences if CHSELRMOD = 1. | ||
| 705 | // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. | ||
| 706 | #[cfg(adc_u0)] | ||
| 707 | T::regs().chselr().modify(|reg| { | ||
| 708 | reg.set_chsel(channel_mask); | ||
| 709 | }); | ||
| 710 | } | ||
| 711 | // Set continuous mode with Circular dma. | ||
| 712 | // Clear overrun flag before starting transfer. | ||
| 713 | T::regs().isr().modify(|reg| { | ||
| 714 | reg.set_ovr(true); | ||
| 715 | }); | ||
| 716 | |||
| 717 | #[cfg(not(any(adc_g0, adc_u0)))] | ||
| 718 | T::regs().cfgr().modify(|reg| { | ||
| 719 | reg.set_discen(false); | ||
| 720 | reg.set_cont(true); | ||
| 721 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 722 | reg.set_dmaen(true); | ||
| 723 | }); | ||
| 724 | #[cfg(any(adc_g0, adc_u0))] | ||
| 725 | T::regs().cfgr1().modify(|reg| { | ||
| 726 | reg.set_discen(false); | ||
| 727 | reg.set_cont(true); | ||
| 728 | reg.set_dmacfg(Dmacfg::CIRCULAR); | ||
| 729 | reg.set_dmaen(true); | ||
| 730 | }); | ||
| 731 | |||
| 732 | RingBufferedAdc::new(dma, dma_buf) | ||
| 733 | } | ||
| 734 | |||
| 562 | #[cfg(not(adc_g0))] | 735 | #[cfg(not(adc_g0))] |
| 563 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 736 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 564 | // RM0492, RM0481, etc. | 737 | // RM0492, RM0481, etc. |
diff --git a/embassy-stm32/src/backup_sram.rs b/embassy-stm32/src/backup_sram.rs new file mode 100644 index 000000000..31b373c6c --- /dev/null +++ b/embassy-stm32/src/backup_sram.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | //! Battary backed SRAM | ||
| 2 | |||
| 3 | use core::slice; | ||
| 4 | |||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::_generated::{BKPSRAM_BASE, BKPSRAM_SIZE}; | ||
| 8 | use crate::peripherals::BKPSRAM; | ||
| 9 | |||
| 10 | /// Struct used to initilize backup sram | ||
| 11 | pub struct BackupMemory {} | ||
| 12 | |||
| 13 | impl BackupMemory { | ||
| 14 | /// Setup battery backed sram | ||
| 15 | /// | ||
| 16 | /// Returns slice to sram and whether the sram was retained | ||
| 17 | pub fn new(_backup_sram: Peri<'static, BKPSRAM>) -> (&'static mut [u8], bool) { | ||
| 18 | // Assert bksram has been enabled in rcc | ||
| 19 | assert!(crate::pac::PWR.bdcr().read().bren() == crate::pac::pwr::vals::Retention::PRESERVED); | ||
| 20 | |||
| 21 | unsafe { | ||
| 22 | ( | ||
| 23 | slice::from_raw_parts_mut(BKPSRAM_BASE as *mut u8, BKPSRAM_SIZE), | ||
| 24 | critical_section::with(|_| crate::rcc::BKSRAM_RETAINED), | ||
| 25 | ) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs index e08349f02..4fe634ce4 100644 --- a/embassy-stm32/src/can/fd/config.rs +++ b/embassy-stm32/src/can/fd/config.rs | |||
| @@ -360,6 +360,8 @@ pub struct FdCanConfig { | |||
| 360 | pub global_filter: GlobalFilter, | 360 | pub global_filter: GlobalFilter, |
| 361 | /// TX buffer mode (FIFO or priority queue) | 361 | /// TX buffer mode (FIFO or priority queue) |
| 362 | pub tx_buffer_mode: TxBufferMode, | 362 | pub tx_buffer_mode: TxBufferMode, |
| 363 | /// Automatic recovery from bus off state | ||
| 364 | pub automatic_bus_off_recovery: bool, | ||
| 363 | } | 365 | } |
| 364 | 366 | ||
| 365 | impl FdCanConfig { | 367 | impl FdCanConfig { |
| @@ -456,6 +458,16 @@ impl FdCanConfig { | |||
| 456 | self.tx_buffer_mode = txbm; | 458 | self.tx_buffer_mode = txbm; |
| 457 | self | 459 | self |
| 458 | } | 460 | } |
| 461 | |||
| 462 | /// Enables or disables automatic recovery from bus off state | ||
| 463 | /// | ||
| 464 | /// Automatic recovery is performed by clearing the INIT bit in the CCCR register if | ||
| 465 | /// the BO bit is active in the IR register in the IT0 interrupt. | ||
| 466 | #[inline] | ||
| 467 | pub const fn set_automatic_bus_off_recovery(mut self, enabled: bool) -> Self { | ||
| 468 | self.automatic_bus_off_recovery = enabled; | ||
| 469 | self | ||
| 470 | } | ||
| 459 | } | 471 | } |
| 460 | 472 | ||
| 461 | impl Default for FdCanConfig { | 473 | impl Default for FdCanConfig { |
| @@ -474,6 +486,7 @@ impl Default for FdCanConfig { | |||
| 474 | timestamp_source: TimestampSource::None, | 486 | timestamp_source: TimestampSource::None, |
| 475 | global_filter: GlobalFilter::default(), | 487 | global_filter: GlobalFilter::default(), |
| 476 | tx_buffer_mode: TxBufferMode::Priority, | 488 | tx_buffer_mode: TxBufferMode::Priority, |
| 489 | automatic_bus_off_recovery: true, | ||
| 477 | } | 490 | } |
| 478 | } | 491 | } |
| 479 | } | 492 | } |
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index a142a6d63..9883aff57 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -53,7 +53,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 53 | regs.ir().write(|w| w.set_tefn(true)); | 53 | regs.ir().write(|w| w.set_tefn(true)); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | T::info().state.lock(|s| { | 56 | let recover_from_bo = T::info().state.lock(|s| { |
| 57 | let state = s.borrow_mut(); | 57 | let state = s.borrow_mut(); |
| 58 | match &state.tx_mode { | 58 | match &state.tx_mode { |
| 59 | TxMode::NonBuffered(waker) => waker.wake(), | 59 | TxMode::NonBuffered(waker) => waker.wake(), |
| @@ -85,11 +85,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 85 | if ir.rfn(1) { | 85 | if ir.rfn(1) { |
| 86 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); | 86 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); |
| 87 | } | 87 | } |
| 88 | |||
| 89 | state.automatic_bus_off_recovery | ||
| 88 | }); | 90 | }); |
| 89 | 91 | ||
| 90 | if ir.bo() { | 92 | if ir.bo() { |
| 91 | regs.ir().write(|w| w.set_bo(true)); | 93 | regs.ir().write(|w| w.set_bo(true)); |
| 92 | if regs.psr().read().bo() { | 94 | if let Some(true) = recover_from_bo |
| 95 | && regs.psr().read().bo() | ||
| 96 | { | ||
| 93 | // Initiate bus-off recovery sequence by resetting CCCR.INIT | 97 | // Initiate bus-off recovery sequence by resetting CCCR.INIT |
| 94 | regs.cccr().modify(|w| w.set_init(false)); | 98 | regs.cccr().modify(|w| w.set_init(false)); |
| 95 | } | 99 | } |
| @@ -263,7 +267,9 @@ impl<'d> CanConfigurator<'d> { | |||
| 263 | pub fn start(self, mode: OperatingMode) -> Can<'d> { | 267 | pub fn start(self, mode: OperatingMode) -> Can<'d> { |
| 264 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); | 268 | let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); |
| 265 | self.info.state.lock(|s| { | 269 | self.info.state.lock(|s| { |
| 266 | s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; | 270 | let mut state = s.borrow_mut(); |
| 271 | state.ns_per_timer_tick = ns_per_timer_tick; | ||
| 272 | state.automatic_bus_off_recovery = Some(self.config.automatic_bus_off_recovery); | ||
| 267 | }); | 273 | }); |
| 268 | self.info.regs.into_mode(self.config, mode); | 274 | self.info.regs.into_mode(self.config, mode); |
| 269 | Can { | 275 | Can { |
| @@ -861,7 +867,7 @@ struct State { | |||
| 861 | sender_instance_count: usize, | 867 | sender_instance_count: usize, |
| 862 | tx_pin_port: Option<u8>, | 868 | tx_pin_port: Option<u8>, |
| 863 | rx_pin_port: Option<u8>, | 869 | rx_pin_port: Option<u8>, |
| 864 | 870 | automatic_bus_off_recovery: Option<bool>, // controlled by CanConfigurator::start() | |
| 865 | pub err_waker: AtomicWaker, | 871 | pub err_waker: AtomicWaker, |
| 866 | } | 872 | } |
| 867 | 873 | ||
| @@ -876,6 +882,7 @@ impl State { | |||
| 876 | sender_instance_count: 0, | 882 | sender_instance_count: 0, |
| 877 | tx_pin_port: None, | 883 | tx_pin_port: None, |
| 878 | rx_pin_port: None, | 884 | rx_pin_port: None, |
| 885 | automatic_bus_off_recovery: None, | ||
| 879 | } | 886 | } |
| 880 | } | 887 | } |
| 881 | } | 888 | } |
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index fd1682d2b..59a2cbcdb 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -121,17 +121,15 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 121 | 121 | ||
| 122 | /// DCS or Generic short/long write command | 122 | /// DCS or Generic short/long write command |
| 123 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { | 123 | pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> { |
| 124 | assert!(data.len() > 0); | 124 | match data.len() { |
| 125 | 125 | 0 => self.short_write(channel_id, PacketType::DcsShortPktWriteP0, address, 0), | |
| 126 | if data.len() == 1 { | 126 | 1 => self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]), |
| 127 | self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]) | 127 | _ => self.long_write( |
| 128 | } else { | ||
| 129 | self.long_write( | ||
| 130 | channel_id, | 128 | channel_id, |
| 131 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... | 129 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... |
| 132 | address, | 130 | address, |
| 133 | data, | 131 | data, |
| 134 | ) | 132 | ), |
| 135 | } | 133 | } |
| 136 | } | 134 | } |
| 137 | 135 | ||
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 12600d4eb..2f5c3406a 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -71,7 +71,7 @@ unsafe fn on_irq() { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | #[cfg(feature = "low-power")] | 73 | #[cfg(feature = "low-power")] |
| 74 | crate::low_power::on_wakeup_irq(); | 74 | crate::low_power::Executor::on_wakeup_irq(); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | struct BitIter(u32); | 77 | struct BitIter(u32); |
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index cd23cda5c..b3281f2d5 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs | |||
| @@ -96,14 +96,20 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] | 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] |
| 97 | { | 97 | { |
| 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; |
| 99 | #[cfg(any(flash_l4, flash_l5))] | ||
| 100 | let pgn = super::BANK1_REGION.size as u32 / super::BANK1_REGION.erase_size as u32; | ||
| 99 | 101 | ||
| 100 | #[cfg(flash_l4)] | 102 | #[cfg(flash_l4)] |
| 101 | let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; | 103 | let (idx, bank) = if idx > (pgn - 1) { |
| 104 | (idx - pgn, true) | ||
| 105 | } else { | ||
| 106 | (idx, false) | ||
| 107 | }; | ||
| 102 | 108 | ||
| 103 | #[cfg(flash_l5)] | 109 | #[cfg(flash_l5)] |
| 104 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { | 110 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { |
| 105 | if idx > 255 { | 111 | if idx > (pgn - 1) { |
| 106 | (idx - 256, Some(true)) | 112 | (idx - pgn, Some(true)) |
| 107 | } else { | 113 | } else { |
| 108 | (idx, Some(false)) | 114 | (idx, Some(false)) |
| 109 | } | 115 | } |
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 8ecfbc522..a7c6c90bb 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs | |||
| @@ -236,6 +236,42 @@ impl<'d, T: Instance> Fmc<'d, T> { | |||
| 236 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | 236 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) |
| 237 | ] | 237 | ] |
| 238 | )); | 238 | )); |
| 239 | |||
| 240 | fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank1: ( | ||
| 241 | bank: stm32_fmc::SdramTargetBank::Bank1, | ||
| 242 | addr: [ | ||
| 243 | (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin) | ||
| 244 | ], | ||
| 245 | ba: [(ba0: BA0Pin), (ba1: BA1Pin)], | ||
| 246 | d: [ | ||
| 247 | (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), | ||
| 248 | (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) | ||
| 249 | ], | ||
| 250 | nbl: [ | ||
| 251 | (nbl0: NBL0Pin), (nbl1: NBL1Pin) | ||
| 252 | ], | ||
| 253 | ctrl: [ | ||
| 254 | (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | ||
| 255 | ] | ||
| 256 | )); | ||
| 257 | |||
| 258 | fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank2: ( | ||
| 259 | bank: stm32_fmc::SdramTargetBank::Bank2, | ||
| 260 | addr: [ | ||
| 261 | (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin) | ||
| 262 | ], | ||
| 263 | ba: [(ba0: BA0Pin), (ba1: BA1Pin)], | ||
| 264 | d: [ | ||
| 265 | (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), | ||
| 266 | (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) | ||
| 267 | ], | ||
| 268 | nbl: [ | ||
| 269 | (nbl0: NBL0Pin), (nbl1: NBL1Pin) | ||
| 270 | ], | ||
| 271 | ctrl: [ | ||
| 272 | (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) | ||
| 273 | ] | ||
| 274 | )); | ||
| 239 | } | 275 | } |
| 240 | 276 | ||
| 241 | trait SealedInstance: crate::rcc::RccPeripheral { | 277 | trait SealedInstance: crate::rcc::RccPeripheral { |
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 6fece5eb2..6c6807479 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs | |||
| @@ -10,6 +10,7 @@ pub use traits::Instance; | |||
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::rcc; | 11 | use crate::rcc; |
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | pub use crate::timer::simple_pwm::PwmPinConfig; | ||
| 13 | 14 | ||
| 14 | /// HRTIM burst controller instance. | 15 | /// HRTIM burst controller instance. |
| 15 | pub struct BurstController<T: Instance> { | 16 | pub struct BurstController<T: Instance> { |
| @@ -73,7 +74,7 @@ pub struct ComplementaryPwmPin<'d, T, C> { | |||
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | macro_rules! advanced_channel_impl { | 76 | macro_rules! advanced_channel_impl { |
| 76 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { | 77 | ($new_chx:ident, $new_chx_with_config:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { |
| 77 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { | 78 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { |
| 78 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 79 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 79 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | 80 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| @@ -86,6 +87,21 @@ macro_rules! advanced_channel_impl { | |||
| 86 | phantom: PhantomData, | 87 | phantom: PhantomData, |
| 87 | } | 88 | } |
| 88 | } | 89 | } |
| 90 | |||
| 91 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with a specific configuration.")] | ||
| 92 | pub fn $new_chx_with_config( | ||
| 93 | pin: Peri<'d, impl $pin_trait<T>>, | ||
| 94 | pin_config: PwmPinConfig, | ||
| 95 | ) -> Self { | ||
| 96 | critical_section::with(|_| { | ||
| 97 | pin.set_low(); | ||
| 98 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 99 | }); | ||
| 100 | PwmPin { | ||
| 101 | _pin: pin.into(), | ||
| 102 | phantom: PhantomData, | ||
| 103 | } | ||
| 104 | } | ||
| 89 | } | 105 | } |
| 90 | 106 | ||
| 91 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { | 107 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { |
| @@ -100,6 +116,21 @@ macro_rules! advanced_channel_impl { | |||
| 100 | phantom: PhantomData, | 116 | phantom: PhantomData, |
| 101 | } | 117 | } |
| 102 | } | 118 | } |
| 119 | |||
| 120 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance with a specific configuration.")] | ||
| 121 | pub fn $new_chx_with_config( | ||
| 122 | pin: Peri<'d, impl $complementary_pin_trait<T>>, | ||
| 123 | pin_config: PwmPinConfig, | ||
| 124 | ) -> Self { | ||
| 125 | critical_section::with(|_| { | ||
| 126 | pin.set_low(); | ||
| 127 | set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); | ||
| 128 | }); | ||
| 129 | ComplementaryPwmPin { | ||
| 130 | _pin: pin.into(), | ||
| 131 | phantom: PhantomData, | ||
| 132 | } | ||
| 133 | } | ||
| 103 | } | 134 | } |
| 104 | 135 | ||
| 105 | impl<T: Instance> SealedAdvancedChannel<T> for $channel<T> { | 136 | impl<T: Instance> SealedAdvancedChannel<T> for $channel<T> { |
| @@ -111,13 +142,55 @@ macro_rules! advanced_channel_impl { | |||
| 111 | }; | 142 | }; |
| 112 | } | 143 | } |
| 113 | 144 | ||
| 114 | advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); | 145 | advanced_channel_impl!( |
| 115 | advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); | 146 | new_cha, |
| 116 | advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); | 147 | new_cha_with_config, |
| 117 | advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); | 148 | ChA, |
| 118 | advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); | 149 | 0, |
| 150 | ChannelAPin, | ||
| 151 | ChannelAComplementaryPin | ||
| 152 | ); | ||
| 153 | advanced_channel_impl!( | ||
| 154 | new_chb, | ||
| 155 | new_chb_with_config, | ||
| 156 | ChB, | ||
| 157 | 1, | ||
| 158 | ChannelBPin, | ||
| 159 | ChannelBComplementaryPin | ||
| 160 | ); | ||
| 161 | advanced_channel_impl!( | ||
| 162 | new_chc, | ||
| 163 | new_chc_with_config, | ||
| 164 | ChC, | ||
| 165 | 2, | ||
| 166 | ChannelCPin, | ||
| 167 | ChannelCComplementaryPin | ||
| 168 | ); | ||
| 169 | advanced_channel_impl!( | ||
| 170 | new_chd, | ||
| 171 | new_chd_with_config, | ||
| 172 | ChD, | ||
| 173 | 3, | ||
| 174 | ChannelDPin, | ||
| 175 | ChannelDComplementaryPin | ||
| 176 | ); | ||
| 177 | advanced_channel_impl!( | ||
| 178 | new_che, | ||
| 179 | new_che_with_config, | ||
| 180 | ChE, | ||
| 181 | 4, | ||
| 182 | ChannelEPin, | ||
| 183 | ChannelEComplementaryPin | ||
| 184 | ); | ||
| 119 | #[cfg(hrtim_v2)] | 185 | #[cfg(hrtim_v2)] |
| 120 | advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin); | 186 | advanced_channel_impl!( |
| 187 | new_chf, | ||
| 188 | new_chf_with_config, | ||
| 189 | ChF, | ||
| 190 | 5, | ||
| 191 | ChannelFPin, | ||
| 192 | ChannelFComplementaryPin | ||
| 193 | ); | ||
| 121 | 194 | ||
| 122 | /// Struct used to divide a high resolution timer into multiple channels | 195 | /// Struct used to divide a high resolution timer into multiple channels |
| 123 | pub struct AdvancedPwm<'d, T: Instance> { | 196 | pub struct AdvancedPwm<'d, T: Instance> { |
diff --git a/embassy-stm32/src/i2c/config.rs b/embassy-stm32/src/i2c/config.rs index 4e3b736c7..74fac14b2 100644 --- a/embassy-stm32/src/i2c/config.rs +++ b/embassy-stm32/src/i2c/config.rs | |||
| @@ -4,7 +4,7 @@ use crate::gpio::{AfType, OutputType, Speed}; | |||
| 4 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 5 | 5 | ||
| 6 | #[repr(u8)] | 6 | #[repr(u8)] |
| 7 | #[derive(Copy, Clone)] | 7 | #[derive(Debug, Copy, Clone)] |
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 9 | /// Bits of the I2C OA2 register to mask out. | 9 | /// Bits of the I2C OA2 register to mask out. |
| 10 | pub enum AddrMask { | 10 | pub enum AddrMask { |
| @@ -60,7 +60,7 @@ impl Address { | |||
| 60 | } | 60 | } |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | #[derive(Copy, Clone)] | 63 | #[derive(Debug, Copy, Clone)] |
| 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 65 | /// The second Own Address register. | 65 | /// The second Own Address register. |
| 66 | pub struct OA2 { | 66 | pub struct OA2 { |
| @@ -70,7 +70,7 @@ pub struct OA2 { | |||
| 70 | pub mask: AddrMask, | 70 | pub mask: AddrMask, |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | #[derive(Copy, Clone)] | 73 | #[derive(Debug, Copy, Clone)] |
| 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 75 | /// The Own Address(es) of the I2C peripheral. | 75 | /// The Own Address(es) of the I2C peripheral. |
| 76 | pub enum OwnAddresses { | 76 | pub enum OwnAddresses { |
| @@ -88,7 +88,7 @@ pub enum OwnAddresses { | |||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | /// Slave Configuration | 90 | /// Slave Configuration |
| 91 | #[derive(Copy, Clone)] | 91 | #[derive(Debug, Copy, Clone)] |
| 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 92 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 93 | pub struct SlaveAddrConfig { | 93 | pub struct SlaveAddrConfig { |
| 94 | /// Target Address(es) | 94 | /// Target Address(es) |
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f4bf55d34..ee60c3f44 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -219,6 +219,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { | |||
| 219 | sda, | 219 | sda, |
| 220 | }, | 220 | }, |
| 221 | }; | 221 | }; |
| 222 | |||
| 222 | this.enable_and_init(config); | 223 | this.enable_and_init(config); |
| 223 | 224 | ||
| 224 | this | 225 | this |
| @@ -437,15 +438,15 @@ impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> { | |||
| 437 | 438 | ||
| 438 | /// Frame type in I2C transaction. | 439 | /// Frame type in I2C transaction. |
| 439 | /// | 440 | /// |
| 440 | /// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST | 441 | /// This tells each method what kind of frame to use, to generate a (repeated) start condition (ST |
| 441 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an | 442 | /// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an |
| 442 | /// ACK or NACK after the last byte received. | 443 | /// ACK or NACK after the last byte received. |
| 443 | /// | 444 | /// |
| 444 | /// For write operations, the following options are identical because they differ only in the (N)ACK | 445 | /// For write operations, the following options are identical because they differ only in the (N)ACK |
| 445 | /// treatment relevant for read operations: | 446 | /// treatment relevant for read operations: |
| 446 | /// | 447 | /// |
| 447 | /// - `FirstFrame` and `FirstAndNextFrame` | 448 | /// - `FirstFrame` and `FirstAndNextFrame` behave identically for writes |
| 448 | /// - `NextFrame` and `LastFrameNoStop` | 449 | /// - `NextFrame` and `LastFrameNoStop` behave identically for writes |
| 449 | /// | 450 | /// |
| 450 | /// Abbreviations used below: | 451 | /// Abbreviations used below: |
| 451 | /// | 452 | /// |
| @@ -474,7 +475,7 @@ enum FrameOptions { | |||
| 474 | 475 | ||
| 475 | #[allow(dead_code)] | 476 | #[allow(dead_code)] |
| 476 | impl FrameOptions { | 477 | impl FrameOptions { |
| 477 | /// Sends start or repeated start condition before transfer. | 478 | /// Returns true if a start or repeated start condition should be generated before this operation. |
| 478 | fn send_start(self) -> bool { | 479 | fn send_start(self) -> bool { |
| 479 | match self { | 480 | match self { |
| 480 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, | 481 | Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, |
| @@ -482,7 +483,7 @@ impl FrameOptions { | |||
| 482 | } | 483 | } |
| 483 | } | 484 | } |
| 484 | 485 | ||
| 485 | /// Sends stop condition after transfer. | 486 | /// Returns true if a stop condition should be generated after this operation. |
| 486 | fn send_stop(self) -> bool { | 487 | fn send_stop(self) -> bool { |
| 487 | match self { | 488 | match self { |
| 488 | Self::FirstAndLastFrame | Self::LastFrame => true, | 489 | Self::FirstAndLastFrame | Self::LastFrame => true, |
| @@ -490,7 +491,10 @@ impl FrameOptions { | |||
| 490 | } | 491 | } |
| 491 | } | 492 | } |
| 492 | 493 | ||
| 493 | /// Sends NACK after last byte received, indicating end of read operation. | 494 | /// Returns true if NACK should be sent after the last byte received in a read operation. |
| 495 | /// | ||
| 496 | /// This signals the end of a read sequence and releases the bus for the master's | ||
| 497 | /// next transmission (or stop condition). | ||
| 494 | fn send_nack(self) -> bool { | 498 | fn send_nack(self) -> bool { |
| 495 | match self { | 499 | match self { |
| 496 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, | 500 | Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, |
| @@ -499,24 +503,44 @@ impl FrameOptions { | |||
| 499 | } | 503 | } |
| 500 | } | 504 | } |
| 501 | 505 | ||
| 502 | /// Iterates over operations in transaction. | 506 | /// Analyzes I2C transaction operations and assigns appropriate frame to each. |
| 507 | /// | ||
| 508 | /// This function processes a sequence of I2C operations and determines the correct | ||
| 509 | /// frame configuration for each operation to ensure proper I2C protocol compliance. | ||
| 510 | /// It handles the complex logic of: | ||
| 511 | /// | ||
| 512 | /// - Generating start conditions for the first operation of each type (read/write) | ||
| 513 | /// - Generating stop conditions for the final operation in the entire transaction | ||
| 514 | /// - Managing ACK/NACK behavior for read operations, including merging consecutive reads | ||
| 515 | /// - Ensuring proper bus handoff between different operation types | ||
| 516 | /// | ||
| 517 | /// **Transaction Contract Compliance:** | ||
| 518 | /// The frame assignments ensure compliance with the embedded-hal I2C transaction contract, | ||
| 519 | /// where consecutive operations of the same type are logically merged while maintaining | ||
| 520 | /// proper protocol boundaries. | ||
| 503 | /// | 521 | /// |
| 504 | /// Returns necessary frame options for each operation to uphold the [transaction contract] and have | 522 | /// **Error Handling:** |
| 505 | /// the right start/stop/(N)ACK conditions on the wire. | 523 | /// Returns an error if any read operation has an empty buffer, as this would create |
| 524 | /// an invalid I2C transaction that could halt mid-execution. | ||
| 525 | /// | ||
| 526 | /// # Arguments | ||
| 527 | /// * `operations` - Mutable slice of I2C operations from embedded-hal | ||
| 528 | /// | ||
| 529 | /// # Returns | ||
| 530 | /// An iterator over (operation, frame) pairs, or an error if the transaction is invalid | ||
| 506 | /// | 531 | /// |
| 507 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | ||
| 508 | #[allow(dead_code)] | 532 | #[allow(dead_code)] |
| 509 | fn operation_frames<'a, 'b: 'a>( | 533 | fn operation_frames<'a, 'b: 'a>( |
| 510 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], | 534 | operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], |
| 511 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { | 535 | ) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> { |
| 512 | use embedded_hal_1::i2c::Operation::{Read, Write}; | 536 | use embedded_hal_1::i2c::Operation::{Read, Write}; |
| 513 | 537 | ||
| 514 | // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an | 538 | // Validate that no read operations have empty buffers before starting the transaction. |
| 515 | // error in the middle of the transaction. | 539 | // Empty read operations would risk halting with an error mid-transaction. |
| 516 | // | 540 | // |
| 517 | // In principle, we could allow empty read frames within consecutive read operations, as long as | 541 | // Note: We could theoretically allow empty read operations within consecutive read |
| 518 | // at least one byte remains in the final (merged) read operation, but that makes the logic more | 542 | // sequences as long as the final merged read has at least one byte, but this would |
| 519 | // complicated and error-prone. | 543 | // complicate the logic significantly and create error-prone edge cases. |
| 520 | if operations.iter().any(|op| match op { | 544 | if operations.iter().any(|op| match op { |
| 521 | Read(read) => read.is_empty(), | 545 | Read(read) => read.is_empty(), |
| 522 | Write(_) => false, | 546 | Write(_) => false, |
| @@ -525,46 +549,52 @@ fn operation_frames<'a, 'b: 'a>( | |||
| 525 | } | 549 | } |
| 526 | 550 | ||
| 527 | let mut operations = operations.iter_mut().peekable(); | 551 | let mut operations = operations.iter_mut().peekable(); |
| 528 | 552 | let mut next_first_operation = true; | |
| 529 | let mut next_first_frame = true; | ||
| 530 | 553 | ||
| 531 | Ok(iter::from_fn(move || { | 554 | Ok(iter::from_fn(move || { |
| 532 | let op = operations.next()?; | 555 | let current_op = operations.next()?; |
| 533 | 556 | ||
| 534 | // Is `op` first frame of its type? | 557 | // Determine if this is the first operation of its type (read or write) |
| 535 | let first_frame = next_first_frame; | 558 | let is_first_of_type = next_first_operation; |
| 536 | let next_op = operations.peek(); | 559 | let next_op = operations.peek(); |
| 537 | 560 | ||
| 538 | // Get appropriate frame options as combination of the following properties: | 561 | // Compute the appropriate frame based on three key properties: |
| 539 | // | 562 | // |
| 540 | // - For each first operation of its type, generate a (repeated) start condition. | 563 | // 1. **Start Condition**: Generate (repeated) start for first operation of each type |
| 541 | // - For the last operation overall in the entire transaction, generate a stop condition. | 564 | // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction |
| 542 | // - For read operations, check the next operation: if it is also a read operation, we merge | 565 | // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the |
| 543 | // these and send ACK for all bytes in the current operation; send NACK only for the final | 566 | // sequence, or NACK for the final read in a sequence (before write or transaction end) |
| 544 | // read operation's last byte (before write or end of entire transaction) to indicate last | ||
| 545 | // byte read and release the bus for transmission of the bus master's next byte (or stop). | ||
| 546 | // | 567 | // |
| 547 | // We check the third property unconditionally, i.e. even for write opeartions. This is okay | 568 | // The third property is checked for all operations since the resulting frame |
| 548 | // because the resulting frame options are identical for write operations. | 569 | // configurations are identical for write operations regardless of ACK/NACK treatment. |
| 549 | let frame = match (first_frame, next_op) { | 570 | let frame = match (is_first_of_type, next_op) { |
| 571 | // First operation of type, and it's also the final operation overall | ||
| 550 | (true, None) => FrameOptions::FirstAndLastFrame, | 572 | (true, None) => FrameOptions::FirstAndLastFrame, |
| 573 | // First operation of type, next operation is also a read (continue read sequence) | ||
| 551 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, | 574 | (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, |
| 575 | // First operation of type, next operation is write (end current sequence) | ||
| 552 | (true, Some(Write(_))) => FrameOptions::FirstFrame, | 576 | (true, Some(Write(_))) => FrameOptions::FirstFrame, |
| 553 | // | 577 | |
| 578 | // Continuation operation, and it's the final operation overall | ||
| 554 | (false, None) => FrameOptions::LastFrame, | 579 | (false, None) => FrameOptions::LastFrame, |
| 580 | // Continuation operation, next operation is also a read (continue read sequence) | ||
| 555 | (false, Some(Read(_))) => FrameOptions::NextFrame, | 581 | (false, Some(Read(_))) => FrameOptions::NextFrame, |
| 582 | // Continuation operation, next operation is write (end current sequence, no stop) | ||
| 556 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, | 583 | (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, |
| 557 | }; | 584 | }; |
| 558 | 585 | ||
| 559 | // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at | 586 | // Pre-calculate whether the next operation will be the first of its type. |
| 560 | // the beginning of the loop because we hand out `op` as iterator value and cannot access it | 587 | // This is done here because we consume `current_op` as the iterator value |
| 561 | // anymore in the next iteration. | 588 | // and cannot access it in the next iteration. |
| 562 | next_first_frame = match (&op, next_op) { | 589 | next_first_operation = match (¤t_op, next_op) { |
| 590 | // No next operation | ||
| 563 | (_, None) => false, | 591 | (_, None) => false, |
| 592 | // Operation type changes: next will be first of its type | ||
| 564 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, | 593 | (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, |
| 594 | // Operation type continues: next will not be first of its type | ||
| 565 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, | 595 | (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, |
| 566 | }; | 596 | }; |
| 567 | 597 | ||
| 568 | Some((op, frame)) | 598 | Some((current_op, frame)) |
| 569 | })) | 599 | })) |
| 570 | } | 600 | } |
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index e6b6c7c42..128a58db7 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -30,6 +30,7 @@ use crate::pac::i2c; | |||
| 30 | // hit a case like this! | 30 | // hit a case like this! |
| 31 | pub unsafe fn on_interrupt<T: Instance>() { | 31 | pub unsafe fn on_interrupt<T: Instance>() { |
| 32 | let regs = T::info().regs; | 32 | let regs = T::info().regs; |
| 33 | trace!("I2C interrupt triggered"); | ||
| 33 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of | 34 | // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of |
| 34 | // other stuff, so we wake the task on every interrupt. | 35 | // other stuff, so we wake the task on every interrupt. |
| 35 | T::state().waker.wake(); | 36 | T::state().waker.wake(); |
| @@ -92,6 +93,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 92 | self.info.regs.cr1().modify(|reg| { | 93 | self.info.regs.cr1().modify(|reg| { |
| 93 | reg.set_pe(true); | 94 | reg.set_pe(true); |
| 94 | }); | 95 | }); |
| 96 | trace!("i2c v1 init complete"); | ||
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | 99 | fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { |
| @@ -151,7 +153,13 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 151 | Ok(sr1) | 153 | Ok(sr1) |
| 152 | } | 154 | } |
| 153 | 155 | ||
| 154 | fn write_bytes(&mut self, addr: u8, bytes: &[u8], timeout: Timeout, frame: FrameOptions) -> Result<(), Error> { | 156 | fn write_bytes( |
| 157 | &mut self, | ||
| 158 | address: u8, | ||
| 159 | write_buffer: &[u8], | ||
| 160 | timeout: Timeout, | ||
| 161 | frame: FrameOptions, | ||
| 162 | ) -> Result<(), Error> { | ||
| 155 | if frame.send_start() { | 163 | if frame.send_start() { |
| 156 | // Send a START condition | 164 | // Send a START condition |
| 157 | 165 | ||
| @@ -170,7 +178,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 170 | } | 178 | } |
| 171 | 179 | ||
| 172 | // Set up current address we're trying to talk to | 180 | // Set up current address we're trying to talk to |
| 173 | self.info.regs.dr().write(|reg| reg.set_dr(addr << 1)); | 181 | self.info.regs.dr().write(|reg| reg.set_dr(address << 1)); |
| 174 | 182 | ||
| 175 | // Wait until address was sent | 183 | // Wait until address was sent |
| 176 | // Wait for the address to be acknowledged | 184 | // Wait for the address to be acknowledged |
| @@ -184,7 +192,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 184 | } | 192 | } |
| 185 | 193 | ||
| 186 | // Send bytes | 194 | // Send bytes |
| 187 | for c in bytes { | 195 | for c in write_buffer { |
| 188 | self.send_byte(*c, timeout)?; | 196 | self.send_byte(*c, timeout)?; |
| 189 | } | 197 | } |
| 190 | 198 | ||
| @@ -236,12 +244,12 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 236 | 244 | ||
| 237 | fn blocking_read_timeout( | 245 | fn blocking_read_timeout( |
| 238 | &mut self, | 246 | &mut self, |
| 239 | addr: u8, | 247 | address: u8, |
| 240 | buffer: &mut [u8], | 248 | read_buffer: &mut [u8], |
| 241 | timeout: Timeout, | 249 | timeout: Timeout, |
| 242 | frame: FrameOptions, | 250 | frame: FrameOptions, |
| 243 | ) -> Result<(), Error> { | 251 | ) -> Result<(), Error> { |
| 244 | let Some((last, buffer)) = buffer.split_last_mut() else { | 252 | let Some((last_byte, read_buffer)) = read_buffer.split_last_mut() else { |
| 245 | return Err(Error::Overrun); | 253 | return Err(Error::Overrun); |
| 246 | }; | 254 | }; |
| 247 | 255 | ||
| @@ -263,7 +271,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 263 | } | 271 | } |
| 264 | 272 | ||
| 265 | // Set up current address we're trying to talk to | 273 | // Set up current address we're trying to talk to |
| 266 | self.info.regs.dr().write(|reg| reg.set_dr((addr << 1) + 1)); | 274 | self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1)); |
| 267 | 275 | ||
| 268 | // Wait until address was sent | 276 | // Wait until address was sent |
| 269 | // Wait for the address to be acknowledged | 277 | // Wait for the address to be acknowledged |
| @@ -276,7 +284,7 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 276 | } | 284 | } |
| 277 | 285 | ||
| 278 | // Receive bytes into buffer | 286 | // Receive bytes into buffer |
| 279 | for c in buffer { | 287 | for c in read_buffer { |
| 280 | *c = self.recv_byte(timeout)?; | 288 | *c = self.recv_byte(timeout)?; |
| 281 | } | 289 | } |
| 282 | 290 | ||
| @@ -291,37 +299,42 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 291 | }); | 299 | }); |
| 292 | 300 | ||
| 293 | // Receive last byte | 301 | // Receive last byte |
| 294 | *last = self.recv_byte(timeout)?; | 302 | *last_byte = self.recv_byte(timeout)?; |
| 295 | 303 | ||
| 296 | // Fallthrough is success | 304 | // Fallthrough is success |
| 297 | Ok(()) | 305 | Ok(()) |
| 298 | } | 306 | } |
| 299 | 307 | ||
| 300 | /// Blocking read. | 308 | /// Blocking read. |
| 301 | pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { | 309 | pub fn blocking_read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 302 | self.blocking_read_timeout(addr, read, self.timeout(), FrameOptions::FirstAndLastFrame) | 310 | self.blocking_read_timeout(address, read_buffer, self.timeout(), FrameOptions::FirstAndLastFrame) |
| 303 | } | 311 | } |
| 304 | 312 | ||
| 305 | /// Blocking write. | 313 | /// Blocking write. |
| 306 | pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { | 314 | pub fn blocking_write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { |
| 307 | self.write_bytes(addr, write, self.timeout(), FrameOptions::FirstAndLastFrame)?; | 315 | self.write_bytes(address, write_buffer, self.timeout(), FrameOptions::FirstAndLastFrame)?; |
| 308 | 316 | ||
| 309 | // Fallthrough is success | 317 | // Fallthrough is success |
| 310 | Ok(()) | 318 | Ok(()) |
| 311 | } | 319 | } |
| 312 | 320 | ||
| 313 | /// Blocking write, restart, read. | 321 | /// Blocking write, restart, read. |
| 314 | pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 322 | pub fn blocking_write_read( |
| 323 | &mut self, | ||
| 324 | address: u8, | ||
| 325 | write_buffer: &[u8], | ||
| 326 | read_buffer: &mut [u8], | ||
| 327 | ) -> Result<(), Error> { | ||
| 315 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 328 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 316 | // stop condition below. | 329 | // stop condition below. |
| 317 | if read.is_empty() { | 330 | if read_buffer.is_empty() { |
| 318 | return Err(Error::Overrun); | 331 | return Err(Error::Overrun); |
| 319 | } | 332 | } |
| 320 | 333 | ||
| 321 | let timeout = self.timeout(); | 334 | let timeout = self.timeout(); |
| 322 | 335 | ||
| 323 | self.write_bytes(addr, write, timeout, FrameOptions::FirstFrame)?; | 336 | self.write_bytes(address, write_buffer, timeout, FrameOptions::FirstFrame)?; |
| 324 | self.blocking_read_timeout(addr, read, timeout, FrameOptions::FirstAndLastFrame)?; | 337 | self.blocking_read_timeout(address, read_buffer, timeout, FrameOptions::FirstAndLastFrame)?; |
| 325 | 338 | ||
| 326 | Ok(()) | 339 | Ok(()) |
| 327 | } | 340 | } |
| @@ -331,32 +344,43 @@ impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 331 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 344 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 332 | /// | 345 | /// |
| 333 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 346 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 334 | pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 347 | pub fn blocking_transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 335 | let timeout = self.timeout(); | 348 | let timeout = self.timeout(); |
| 336 | 349 | ||
| 337 | for (op, frame) in operation_frames(operations)? { | 350 | for (op, frame) in operation_frames(operations)? { |
| 338 | match op { | 351 | match op { |
| 339 | Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, | 352 | Operation::Read(read_buffer) => self.blocking_read_timeout(address, read_buffer, timeout, frame)?, |
| 340 | Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, | 353 | Operation::Write(write_buffer) => self.write_bytes(address, write_buffer, timeout, frame)?, |
| 341 | } | 354 | } |
| 342 | } | 355 | } |
| 343 | 356 | ||
| 344 | Ok(()) | 357 | Ok(()) |
| 345 | } | 358 | } |
| 346 | 359 | ||
| 347 | // Async | 360 | /// Can be used by both blocking and async implementations |
| 348 | |||
| 349 | #[inline] // pretty sure this should always be inlined | 361 | #[inline] // pretty sure this should always be inlined |
| 350 | fn enable_interrupts(info: &'static Info) -> () { | 362 | fn enable_interrupts(info: &'static Info) { |
| 351 | info.regs.cr2().modify(|w| { | 363 | // The interrupt handler disables interrupts globally, so we need to re-enable them |
| 352 | w.set_iterren(true); | 364 | // This must be done in a critical section to avoid races |
| 353 | w.set_itevten(true); | 365 | critical_section::with(|_| { |
| 366 | info.regs.cr2().modify(|w| { | ||
| 367 | w.set_iterren(true); | ||
| 368 | w.set_itevten(true); | ||
| 369 | }); | ||
| 354 | }); | 370 | }); |
| 355 | } | 371 | } |
| 372 | |||
| 373 | /// Can be used by both blocking and async implementations | ||
| 374 | fn clear_stop_flag(info: &'static Info) { | ||
| 375 | trace!("I2C slave: clearing STOPF flag (v1 sequence)"); | ||
| 376 | // v1 requires: READ SR1 then WRITE CR1 to clear STOPF | ||
| 377 | let _ = info.regs.sr1().read(); | ||
| 378 | info.regs.cr1().modify(|_| {}); // Dummy write to clear STOPF | ||
| 379 | } | ||
| 356 | } | 380 | } |
| 357 | 381 | ||
| 358 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | 382 | impl<'d, IM: MasterMode> I2c<'d, Async, IM> { |
| 359 | async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { | 383 | async fn write_frame(&mut self, address: u8, write_buffer: &[u8], frame: FrameOptions) -> Result<(), Error> { |
| 360 | self.info.regs.cr2().modify(|w| { | 384 | self.info.regs.cr2().modify(|w| { |
| 361 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 385 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| 362 | // reception. | 386 | // reception. |
| @@ -439,7 +463,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 439 | // this address from the memory after each TxE event. | 463 | // this address from the memory after each TxE event. |
| 440 | let dst = self.info.regs.dr().as_ptr() as *mut u8; | 464 | let dst = self.info.regs.dr().as_ptr() as *mut u8; |
| 441 | 465 | ||
| 442 | self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) | 466 | self.tx_dma |
| 467 | .as_mut() | ||
| 468 | .unwrap() | ||
| 469 | .write(write_buffer, dst, Default::default()) | ||
| 443 | }; | 470 | }; |
| 444 | 471 | ||
| 445 | // Wait for bytes to be sent, or an error to occur. | 472 | // Wait for bytes to be sent, or an error to occur. |
| @@ -501,28 +528,28 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 501 | } | 528 | } |
| 502 | 529 | ||
| 503 | /// Write. | 530 | /// Write. |
| 504 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 531 | pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { |
| 505 | self.write_frame(address, write, FrameOptions::FirstAndLastFrame) | 532 | self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) |
| 506 | .await?; | 533 | .await?; |
| 507 | 534 | ||
| 508 | Ok(()) | 535 | Ok(()) |
| 509 | } | 536 | } |
| 510 | 537 | ||
| 511 | /// Read. | 538 | /// Read. |
| 512 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 539 | pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { |
| 513 | self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) | 540 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) |
| 514 | .await?; | 541 | .await?; |
| 515 | 542 | ||
| 516 | Ok(()) | 543 | Ok(()) |
| 517 | } | 544 | } |
| 518 | 545 | ||
| 519 | async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { | 546 | async fn read_frame(&mut self, address: u8, read_buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { |
| 520 | if buffer.is_empty() { | 547 | if read_buffer.is_empty() { |
| 521 | return Err(Error::Overrun); | 548 | return Err(Error::Overrun); |
| 522 | } | 549 | } |
| 523 | 550 | ||
| 524 | // Some branches below depend on whether the buffer contains only a single byte. | 551 | // Some branches below depend on whether the buffer contains only a single byte. |
| 525 | let single_byte = buffer.len() == 1; | 552 | let single_byte = read_buffer.len() == 1; |
| 526 | 553 | ||
| 527 | self.info.regs.cr2().modify(|w| { | 554 | self.info.regs.cr2().modify(|w| { |
| 528 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for | 555 | // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for |
| @@ -612,7 +639,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 612 | self.info.regs.sr2().read(); | 639 | self.info.regs.sr2().read(); |
| 613 | } else { | 640 | } else { |
| 614 | // Before starting reception of single byte (but without START condition, i.e. in case | 641 | // Before starting reception of single byte (but without START condition, i.e. in case |
| 615 | // of continued frame), program NACK to emit at end of this byte. | 642 | // of merged operations), program NACK to emit at end of this byte. |
| 616 | if frame.send_nack() && single_byte { | 643 | if frame.send_nack() && single_byte { |
| 617 | self.info.regs.cr1().modify(|w| { | 644 | self.info.regs.cr1().modify(|w| { |
| 618 | w.set_ack(false); | 645 | w.set_ack(false); |
| @@ -634,7 +661,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 634 | // from this address from the memory after each RxE event. | 661 | // from this address from the memory after each RxE event. |
| 635 | let src = self.info.regs.dr().as_ptr() as *mut u8; | 662 | let src = self.info.regs.dr().as_ptr() as *mut u8; |
| 636 | 663 | ||
| 637 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | 664 | self.rx_dma.as_mut().unwrap().read(src, read_buffer, Default::default()) |
| 638 | }; | 665 | }; |
| 639 | 666 | ||
| 640 | // Wait for bytes to be received, or an error to occur. | 667 | // Wait for bytes to be received, or an error to occur. |
| @@ -673,15 +700,17 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 673 | } | 700 | } |
| 674 | 701 | ||
| 675 | /// Write, restart, read. | 702 | /// Write, restart, read. |
| 676 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 703 | pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { |
| 677 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the | 704 | // Check empty read buffer before starting transaction. Otherwise, we would not generate the |
| 678 | // stop condition below. | 705 | // stop condition below. |
| 679 | if read.is_empty() { | 706 | if read_buffer.is_empty() { |
| 680 | return Err(Error::Overrun); | 707 | return Err(Error::Overrun); |
| 681 | } | 708 | } |
| 682 | 709 | ||
| 683 | self.write_frame(address, write, FrameOptions::FirstFrame).await?; | 710 | self.write_frame(address, write_buffer, FrameOptions::FirstFrame) |
| 684 | self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await | 711 | .await?; |
| 712 | self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) | ||
| 713 | .await | ||
| 685 | } | 714 | } |
| 686 | 715 | ||
| 687 | /// Transaction with operations. | 716 | /// Transaction with operations. |
| @@ -689,11 +718,11 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 689 | /// Consecutive operations of same type are merged. See [transaction contract] for details. | 718 | /// Consecutive operations of same type are merged. See [transaction contract] for details. |
| 690 | /// | 719 | /// |
| 691 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 720 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 692 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 721 | pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 693 | for (op, frame) in operation_frames(operations)? { | 722 | for (op, frame) in operation_frames(operations)? { |
| 694 | match op { | 723 | match op { |
| 695 | Operation::Read(read) => self.read_frame(addr, read, frame).await?, | 724 | Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, |
| 696 | Operation::Write(write) => self.write_frame(addr, write, frame).await?, | 725 | Operation::Write(write_buffer) => self.write_frame(address, write_buffer, frame).await?, |
| 697 | } | 726 | } |
| 698 | } | 727 | } |
| 699 | 728 | ||
| @@ -729,12 +758,956 @@ impl Duty { | |||
| 729 | } | 758 | } |
| 730 | } | 759 | } |
| 731 | 760 | ||
| 761 | /// Result of attempting to send a byte in slave transmitter mode | ||
| 762 | #[derive(Debug, PartialEq)] | ||
| 763 | enum TransmitResult { | ||
| 764 | /// Byte sent and ACKed by master - continue transmission | ||
| 765 | Acknowledged, | ||
| 766 | /// Byte sent but NACKed by master - normal end of read transaction | ||
| 767 | NotAcknowledged, | ||
| 768 | /// STOP condition detected - master terminated transaction | ||
| 769 | Stopped, | ||
| 770 | /// RESTART condition detected - master starting new transaction | ||
| 771 | Restarted, | ||
| 772 | } | ||
| 773 | |||
| 774 | /// Result of attempting to receive a byte in slave receiver mode | ||
| 775 | #[derive(Debug, PartialEq)] | ||
| 776 | enum ReceiveResult { | ||
| 777 | /// Data byte successfully received | ||
| 778 | Data(u8), | ||
| 779 | /// STOP condition detected - end of write transaction | ||
| 780 | Stopped, | ||
| 781 | /// RESTART condition detected - master starting new transaction | ||
| 782 | Restarted, | ||
| 783 | } | ||
| 784 | |||
| 785 | /// Enumeration of slave transaction termination conditions | ||
| 786 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
| 787 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 788 | enum SlaveTermination { | ||
| 789 | /// STOP condition received - normal end of transaction | ||
| 790 | Stop, | ||
| 791 | /// RESTART condition received - master starting new transaction | ||
| 792 | Restart, | ||
| 793 | /// NACK received - normal end of read transaction | ||
| 794 | Nack, | ||
| 795 | } | ||
| 796 | |||
| 797 | impl<'d, M: PeriMode> I2c<'d, M, Master> { | ||
| 798 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | ||
| 799 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | ||
| 800 | let mut slave = I2c { | ||
| 801 | info: self.info, | ||
| 802 | state: self.state, | ||
| 803 | kernel_clock: self.kernel_clock, | ||
| 804 | tx_dma: self.tx_dma.take(), // Use take() to move ownership | ||
| 805 | rx_dma: self.rx_dma.take(), // Use take() to move ownership | ||
| 806 | #[cfg(feature = "time")] | ||
| 807 | timeout: self.timeout, | ||
| 808 | _phantom: PhantomData, | ||
| 809 | _phantom2: PhantomData, | ||
| 810 | _drop_guard: self._drop_guard, // Move the drop guard | ||
| 811 | }; | ||
| 812 | slave.init_slave(slave_addr_config); | ||
| 813 | slave | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | // Address configuration methods | ||
| 818 | impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 819 | /// Initialize slave mode with address configuration | ||
| 820 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 821 | trace!("I2C slave: initializing with config={:?}", config); | ||
| 822 | |||
| 823 | // Disable peripheral for configuration | ||
| 824 | self.info.regs.cr1().modify(|reg| reg.set_pe(false)); | ||
| 825 | |||
| 826 | // Configure slave addresses | ||
| 827 | self.apply_address_configuration(config); | ||
| 828 | |||
| 829 | // Enable peripheral with slave settings | ||
| 830 | self.info.regs.cr1().modify(|reg| { | ||
| 831 | reg.set_pe(true); | ||
| 832 | reg.set_ack(true); // Enable acknowledgment for slave mode | ||
| 833 | reg.set_nostretch(false); // Allow clock stretching for processing time | ||
| 834 | }); | ||
| 835 | |||
| 836 | trace!("I2C slave: initialization complete"); | ||
| 837 | } | ||
| 838 | |||
| 839 | /// Apply the complete address configuration for slave mode | ||
| 840 | fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { | ||
| 841 | match config.addr { | ||
| 842 | OwnAddresses::OA1(addr) => { | ||
| 843 | self.configure_primary_address(addr); | ||
| 844 | self.disable_secondary_address(); | ||
| 845 | } | ||
| 846 | OwnAddresses::OA2(oa2) => { | ||
| 847 | self.configure_default_primary_address(); | ||
| 848 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 849 | } | ||
| 850 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 851 | self.configure_primary_address(oa1); | ||
| 852 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 853 | } | ||
| 854 | } | ||
| 855 | |||
| 856 | // Configure general call detection | ||
| 857 | if config.general_call { | ||
| 858 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 859 | } | ||
| 860 | } | ||
| 861 | |||
| 862 | /// Configure the primary address (OA1) register | ||
| 863 | fn configure_primary_address(&mut self, addr: Address) { | ||
| 864 | match addr { | ||
| 865 | Address::SevenBit(addr) => { | ||
| 866 | self.info.regs.oar1().write(|reg| { | ||
| 867 | let hw_addr = (addr as u16) << 1; // Address in bits [7:1] | ||
| 868 | reg.set_add(hw_addr); | ||
| 869 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 870 | }); | ||
| 871 | } | ||
| 872 | Address::TenBit(addr) => { | ||
| 873 | self.info.regs.oar1().write(|reg| { | ||
| 874 | reg.set_add(addr); | ||
| 875 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 876 | }); | ||
| 877 | } | ||
| 878 | } | ||
| 879 | |||
| 880 | // Set required bit 14 as per reference manual | ||
| 881 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 882 | } | ||
| 883 | |||
| 884 | /// Configure the secondary address (OA2) register | ||
| 885 | fn configure_secondary_address(&mut self, addr: u8) { | ||
| 886 | self.info.regs.oar2().write(|reg| { | ||
| 887 | reg.set_add2(addr); | ||
| 888 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 889 | }); | ||
| 890 | } | ||
| 891 | |||
| 892 | /// Set a default primary address when using OA2-only mode | ||
| 893 | fn configure_default_primary_address(&mut self) { | ||
| 894 | self.info.regs.oar1().write(|reg| { | ||
| 895 | reg.set_add(0); // Reserved address, safe to use | ||
| 896 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 897 | }); | ||
| 898 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 899 | } | ||
| 900 | |||
| 901 | /// Disable secondary address when not needed | ||
| 902 | fn disable_secondary_address(&mut self) { | ||
| 903 | self.info.regs.oar2().write(|reg| { | ||
| 904 | reg.set_endual(i2c::vals::Endual::SINGLE); | ||
| 905 | }); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | |||
| 909 | impl<'d, M: PeriMode> I2c<'d, M, MultiMaster> { | ||
| 910 | /// Listen for incoming I2C address match and return the command type | ||
| 911 | /// | ||
| 912 | /// This method blocks until the slave address is matched by a master. | ||
| 913 | /// Returns the command type (Read/Write) and the matched address. | ||
| 914 | pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 915 | trace!("I2C slave: starting blocking listen for address match"); | ||
| 916 | let result = self.blocking_listen_with_timeout(self.timeout()); | ||
| 917 | trace!("I2C slave: blocking listen complete, result={:?}", result); | ||
| 918 | result | ||
| 919 | } | ||
| 920 | |||
| 921 | /// Respond to a master read request by transmitting data | ||
| 922 | /// | ||
| 923 | /// Sends the provided data to the master. If the master requests more bytes | ||
| 924 | /// than available, padding bytes (0x00) are sent until the master terminates | ||
| 925 | /// the transaction with NACK. | ||
| 926 | /// | ||
| 927 | /// Returns the total number of bytes transmitted (including padding). | ||
| 928 | pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 929 | trace!("I2C slave: starting blocking respond_to_read, data_len={}", data.len()); | ||
| 930 | |||
| 931 | if let Some(zero_length_result) = self.detect_zero_length_read(self.timeout())? { | ||
| 932 | trace!("I2C slave: zero-length read detected"); | ||
| 933 | return Ok(zero_length_result); | ||
| 934 | } | ||
| 935 | |||
| 936 | let result = self.transmit_to_master(data, self.timeout()); | ||
| 937 | trace!("I2C slave: blocking respond_to_read complete, result={:?}", result); | ||
| 938 | result | ||
| 939 | } | ||
| 940 | |||
| 941 | /// Respond to a master write request by receiving data | ||
| 942 | /// | ||
| 943 | /// Receives data from the master into the provided buffer. If the master | ||
| 944 | /// sends more bytes than the buffer can hold, excess bytes are acknowledged | ||
| 945 | /// but discarded. | ||
| 946 | /// | ||
| 947 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 948 | pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 949 | trace!( | ||
| 950 | "I2C slave: starting blocking respond_to_write, buffer_len={}", | ||
| 951 | buffer.len() | ||
| 952 | ); | ||
| 953 | let result = self.receive_from_master(buffer, self.timeout()); | ||
| 954 | trace!("I2C slave: blocking respond_to_write complete, result={:?}", result); | ||
| 955 | result | ||
| 956 | } | ||
| 957 | |||
| 958 | // Private implementation methods | ||
| 959 | |||
| 960 | /// Wait for address match and determine transaction type | ||
| 961 | fn blocking_listen_with_timeout(&mut self, timeout: Timeout) -> Result<SlaveCommand, Error> { | ||
| 962 | // Ensure interrupts are disabled for blocking operation | ||
| 963 | self.disable_i2c_interrupts(); | ||
| 964 | |||
| 965 | // Wait for address match (ADDR flag) | ||
| 966 | loop { | ||
| 967 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 968 | |||
| 969 | if sr1.addr() { | ||
| 970 | // Address matched - read SR2 to get direction and clear ADDR flag | ||
| 971 | let sr2 = self.info.regs.sr2().read(); | ||
| 972 | let direction = if sr2.tra() { | ||
| 973 | SlaveCommandKind::Read | ||
| 974 | } else { | ||
| 975 | SlaveCommandKind::Write | ||
| 976 | }; | ||
| 977 | |||
| 978 | // Use the static method instead of the instance method | ||
| 979 | let matched_address = Self::decode_matched_address(sr2, self.info)?; | ||
| 980 | trace!( | ||
| 981 | "I2C slave: address matched, direction={:?}, addr={:?}", | ||
| 982 | direction, matched_address | ||
| 983 | ); | ||
| 984 | |||
| 985 | return Ok(SlaveCommand { | ||
| 986 | kind: direction, | ||
| 987 | address: matched_address, | ||
| 988 | }); | ||
| 989 | } | ||
| 990 | |||
| 991 | timeout.check()?; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | /// Transmit data to master in response to read request | ||
| 996 | fn transmit_to_master(&mut self, data: &[u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 997 | let mut bytes_transmitted = 0; | ||
| 998 | let mut padding_count = 0; | ||
| 999 | |||
| 1000 | loop { | ||
| 1001 | let byte_to_send = if bytes_transmitted < data.len() { | ||
| 1002 | data[bytes_transmitted] | ||
| 1003 | } else { | ||
| 1004 | padding_count += 1; | ||
| 1005 | 0x00 // Send padding bytes when data is exhausted | ||
| 1006 | }; | ||
| 1007 | |||
| 1008 | match self.transmit_byte(byte_to_send, timeout)? { | ||
| 1009 | TransmitResult::Acknowledged => { | ||
| 1010 | bytes_transmitted += 1; | ||
| 1011 | } | ||
| 1012 | TransmitResult::NotAcknowledged => { | ||
| 1013 | bytes_transmitted += 1; // Count the NACKed byte | ||
| 1014 | break; | ||
| 1015 | } | ||
| 1016 | TransmitResult::Stopped | TransmitResult::Restarted => { | ||
| 1017 | break; | ||
| 1018 | } | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | if padding_count > 0 { | ||
| 1023 | trace!( | ||
| 1024 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1025 | data.len(), | ||
| 1026 | padding_count, | ||
| 1027 | bytes_transmitted | ||
| 1028 | ); | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | Ok(bytes_transmitted) | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /// Receive data from master during write request | ||
| 1035 | fn receive_from_master(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> { | ||
| 1036 | let mut bytes_stored = 0; | ||
| 1037 | |||
| 1038 | // Receive bytes that fit in buffer | ||
| 1039 | while bytes_stored < buffer.len() { | ||
| 1040 | match self.receive_byte(timeout)? { | ||
| 1041 | ReceiveResult::Data(byte) => { | ||
| 1042 | buffer[bytes_stored] = byte; | ||
| 1043 | bytes_stored += 1; | ||
| 1044 | } | ||
| 1045 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1046 | return Ok(bytes_stored); | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | // Handle buffer overflow by discarding excess bytes | ||
| 1052 | if bytes_stored == buffer.len() { | ||
| 1053 | trace!("I2C slave: buffer full, discarding excess bytes"); | ||
| 1054 | self.discard_excess_bytes(timeout)?; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | Ok(bytes_stored) | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /// Detect zero-length read pattern early | ||
| 1061 | /// | ||
| 1062 | /// Zero-length reads occur when a master sends START+ADDR+R followed immediately | ||
| 1063 | /// by NACK+STOP without wanting any data. This must be detected before attempting | ||
| 1064 | /// to transmit any bytes to avoid SDA line issues. | ||
| 1065 | fn detect_zero_length_read(&mut self, _timeout: Timeout) -> Result<Option<usize>, Error> { | ||
| 1066 | // Quick check for immediate termination signals | ||
| 1067 | let sr1 = self.info.regs.sr1().read(); | ||
| 1068 | |||
| 1069 | // Check for immediate NACK (fastest zero-length pattern) | ||
| 1070 | if sr1.af() { | ||
| 1071 | self.clear_acknowledge_failure(); | ||
| 1072 | return Ok(Some(0)); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | // Check for immediate STOP (alternative zero-length pattern) | ||
| 1076 | if sr1.stopf() { | ||
| 1077 | Self::clear_stop_flag(self.info); | ||
| 1078 | return Ok(Some(0)); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | // Give a brief window for master to send termination signals | ||
| 1082 | // This handles masters that have slight delays between address ACK and NACK | ||
| 1083 | const ZERO_LENGTH_DETECTION_CYCLES: u32 = 20; // ~5-10µs window | ||
| 1084 | |||
| 1085 | for _ in 0..ZERO_LENGTH_DETECTION_CYCLES { | ||
| 1086 | let sr1 = self.info.regs.sr1().read(); | ||
| 1087 | |||
| 1088 | // Immediate NACK indicates zero-length read | ||
| 1089 | if sr1.af() { | ||
| 1090 | self.clear_acknowledge_failure(); | ||
| 1091 | return Ok(Some(0)); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | // Immediate STOP indicates zero-length read | ||
| 1095 | if sr1.stopf() { | ||
| 1096 | Self::clear_stop_flag(self.info); | ||
| 1097 | return Ok(Some(0)); | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | // If TXE becomes ready, master is waiting for data - not zero-length | ||
| 1101 | if sr1.txe() { | ||
| 1102 | return Ok(None); // Proceed with normal transmission | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | // If RESTART detected, handle as zero-length | ||
| 1106 | if sr1.addr() { | ||
| 1107 | return Ok(Some(0)); | ||
| 1108 | } | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | // No zero-length pattern detected within the window | ||
| 1112 | Ok(None) | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /// Discard excess bytes when buffer is full | ||
| 1116 | fn discard_excess_bytes(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1117 | let mut discarded_count = 0; | ||
| 1118 | |||
| 1119 | loop { | ||
| 1120 | match self.receive_byte(timeout)? { | ||
| 1121 | ReceiveResult::Data(_) => { | ||
| 1122 | discarded_count += 1; | ||
| 1123 | continue; | ||
| 1124 | } | ||
| 1125 | ReceiveResult::Stopped | ReceiveResult::Restarted => { | ||
| 1126 | if discarded_count > 0 { | ||
| 1127 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1128 | } | ||
| 1129 | break; | ||
| 1130 | } | ||
| 1131 | } | ||
| 1132 | } | ||
| 1133 | Ok(()) | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | /// Send a single byte and wait for master's response | ||
| 1137 | fn transmit_byte(&mut self, byte: u8, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1138 | // Wait for transmit buffer ready | ||
| 1139 | self.wait_for_transmit_ready(timeout)?; | ||
| 1140 | |||
| 1141 | // Send the byte | ||
| 1142 | self.info.regs.dr().write(|w| w.set_dr(byte)); | ||
| 1143 | |||
| 1144 | // Wait for transmission completion or master response | ||
| 1145 | self.wait_for_transmit_completion(timeout) | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | /// Wait until transmit buffer is ready (TXE flag set) | ||
| 1149 | fn wait_for_transmit_ready(&mut self, timeout: Timeout) -> Result<(), Error> { | ||
| 1150 | loop { | ||
| 1151 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1152 | |||
| 1153 | // Check for early termination conditions | ||
| 1154 | if let Some(result) = Self::check_early_termination(sr1) { | ||
| 1155 | return Err(self.handle_early_termination(result)); | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | if sr1.txe() { | ||
| 1159 | return Ok(()); // Ready to transmit | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | timeout.check()?; | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /// Wait for byte transmission completion or master response | ||
| 1167 | fn wait_for_transmit_completion(&mut self, timeout: Timeout) -> Result<TransmitResult, Error> { | ||
| 1168 | loop { | ||
| 1169 | let sr1 = self.info.regs.sr1().read(); | ||
| 1170 | |||
| 1171 | // Check flags in priority order | ||
| 1172 | if sr1.af() { | ||
| 1173 | self.clear_acknowledge_failure(); | ||
| 1174 | return Ok(TransmitResult::NotAcknowledged); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | if sr1.btf() { | ||
| 1178 | return Ok(TransmitResult::Acknowledged); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if sr1.stopf() { | ||
| 1182 | Self::clear_stop_flag(self.info); | ||
| 1183 | return Ok(TransmitResult::Stopped); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | if sr1.addr() { | ||
| 1187 | return Ok(TransmitResult::Restarted); | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | // Check for other error conditions | ||
| 1191 | self.check_for_hardware_errors(sr1)?; | ||
| 1192 | |||
| 1193 | timeout.check()?; | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /// Receive a single byte or detect transaction termination | ||
| 1198 | fn receive_byte(&mut self, timeout: Timeout) -> Result<ReceiveResult, Error> { | ||
| 1199 | loop { | ||
| 1200 | let sr1 = Self::read_status_and_handle_errors(self.info)?; | ||
| 1201 | |||
| 1202 | // Check for received data first (prioritize data over control signals) | ||
| 1203 | if sr1.rxne() { | ||
| 1204 | let byte = self.info.regs.dr().read().dr(); | ||
| 1205 | return Ok(ReceiveResult::Data(byte)); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | // Check for transaction termination | ||
| 1209 | if sr1.addr() { | ||
| 1210 | return Ok(ReceiveResult::Restarted); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | if sr1.stopf() { | ||
| 1214 | Self::clear_stop_flag(self.info); | ||
| 1215 | return Ok(ReceiveResult::Stopped); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | timeout.check()?; | ||
| 1219 | } | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | /// Determine which slave address was matched based on SR2 flags | ||
| 1223 | fn decode_matched_address(sr2: i2c::regs::Sr2, info: &'static Info) -> Result<Address, Error> { | ||
| 1224 | if sr2.gencall() { | ||
| 1225 | Ok(Address::SevenBit(0x00)) // General call address | ||
| 1226 | } else if sr2.dualf() { | ||
| 1227 | // OA2 (secondary address) was matched | ||
| 1228 | let oar2 = info.regs.oar2().read(); | ||
| 1229 | if oar2.endual() != i2c::vals::Endual::DUAL { | ||
| 1230 | return Err(Error::Bus); // Hardware inconsistency | ||
| 1231 | } | ||
| 1232 | Ok(Address::SevenBit(oar2.add2())) | ||
| 1233 | } else { | ||
| 1234 | // OA1 (primary address) was matched | ||
| 1235 | let oar1 = info.regs.oar1().read(); | ||
| 1236 | match oar1.addmode() { | ||
| 1237 | i2c::vals::Addmode::BIT7 => { | ||
| 1238 | let addr = (oar1.add() >> 1) as u8; | ||
| 1239 | Ok(Address::SevenBit(addr)) | ||
| 1240 | } | ||
| 1241 | i2c::vals::Addmode::BIT10 => Ok(Address::TenBit(oar1.add())), | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | // Helper methods for hardware interaction | ||
| 1247 | |||
| 1248 | /// Read status register and handle I2C errors (except NACK in slave mode) | ||
| 1249 | fn read_status_and_handle_errors(info: &'static Info) -> Result<i2c::regs::Sr1, Error> { | ||
| 1250 | match Self::check_and_clear_error_flags(info) { | ||
| 1251 | Ok(sr1) => Ok(sr1), | ||
| 1252 | Err(Error::Nack) => { | ||
| 1253 | // In slave mode, NACK is normal protocol behavior, not an error | ||
| 1254 | Ok(info.regs.sr1().read()) | ||
| 1255 | } | ||
| 1256 | Err(other_error) => Err(other_error), | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /// Check for conditions that cause early termination of operations | ||
| 1261 | fn check_early_termination(sr1: i2c::regs::Sr1) -> Option<TransmitResult> { | ||
| 1262 | if sr1.stopf() { | ||
| 1263 | Some(TransmitResult::Stopped) | ||
| 1264 | } else if sr1.addr() { | ||
| 1265 | Some(TransmitResult::Restarted) | ||
| 1266 | } else if sr1.af() { | ||
| 1267 | Some(TransmitResult::NotAcknowledged) | ||
| 1268 | } else { | ||
| 1269 | None | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | /// Convert early termination to appropriate error | ||
| 1274 | fn handle_early_termination(&mut self, result: TransmitResult) -> Error { | ||
| 1275 | match result { | ||
| 1276 | TransmitResult::Stopped => { | ||
| 1277 | Self::clear_stop_flag(self.info); | ||
| 1278 | Error::Bus // Unexpected STOP during setup | ||
| 1279 | } | ||
| 1280 | TransmitResult::Restarted => { | ||
| 1281 | Error::Bus // Unexpected RESTART during setup | ||
| 1282 | } | ||
| 1283 | TransmitResult::NotAcknowledged => { | ||
| 1284 | self.clear_acknowledge_failure(); | ||
| 1285 | Error::Bus // Unexpected NACK during setup | ||
| 1286 | } | ||
| 1287 | TransmitResult::Acknowledged => { | ||
| 1288 | unreachable!() // This should never be passed to this function | ||
| 1289 | } | ||
| 1290 | } | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | /// Check for hardware-level I2C errors during transmission | ||
| 1294 | fn check_for_hardware_errors(&self, sr1: i2c::regs::Sr1) -> Result<(), Error> { | ||
| 1295 | if sr1.timeout() || sr1.ovr() || sr1.arlo() || sr1.berr() { | ||
| 1296 | // Delegate to existing error handling | ||
| 1297 | Self::check_and_clear_error_flags(self.info)?; | ||
| 1298 | } | ||
| 1299 | Ok(()) | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | /// Disable I2C event and error interrupts for blocking operations | ||
| 1303 | fn disable_i2c_interrupts(&mut self) { | ||
| 1304 | self.info.regs.cr2().modify(|w| { | ||
| 1305 | w.set_itevten(false); | ||
| 1306 | w.set_iterren(false); | ||
| 1307 | }); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /// Clear the acknowledge failure flag | ||
| 1311 | fn clear_acknowledge_failure(&mut self) { | ||
| 1312 | self.info.regs.sr1().write(|reg| { | ||
| 1313 | reg.0 = !0; | ||
| 1314 | reg.set_af(false); | ||
| 1315 | }); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | /// Configure DMA settings for slave operations (shared between read/write) | ||
| 1319 | fn setup_slave_dma_base(&mut self) { | ||
| 1320 | self.info.regs.cr2().modify(|w| { | ||
| 1321 | w.set_itbufen(false); // Always disable buffer interrupts when using DMA | ||
| 1322 | w.set_dmaen(true); // Enable DMA requests | ||
| 1323 | w.set_last(false); // LAST bit not used in slave mode for v1 hardware | ||
| 1324 | }); | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | /// Disable DMA and interrupts in a critical section | ||
| 1328 | fn disable_dma_and_interrupts(info: &'static Info) { | ||
| 1329 | critical_section::with(|_| { | ||
| 1330 | info.regs.cr2().modify(|w| { | ||
| 1331 | w.set_dmaen(false); | ||
| 1332 | w.set_iterren(false); | ||
| 1333 | w.set_itevten(false); | ||
| 1334 | }); | ||
| 1335 | }); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | /// Check for early termination conditions during slave operations | ||
| 1339 | /// Returns Some(result) if termination detected, None to continue | ||
| 1340 | fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option<SlaveTermination> { | ||
| 1341 | if sr1.stopf() { | ||
| 1342 | Some(SlaveTermination::Stop) | ||
| 1343 | } else if sr1.addr() { | ||
| 1344 | Some(SlaveTermination::Restart) | ||
| 1345 | } else if sr1.af() { | ||
| 1346 | Some(SlaveTermination::Nack) | ||
| 1347 | } else { | ||
| 1348 | None | ||
| 1349 | } | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | impl<'d> I2c<'d, Async, MultiMaster> { | ||
| 1354 | /// Async listen for incoming I2C messages using interrupts | ||
| 1355 | /// | ||
| 1356 | /// Waits for a master to address this slave and returns the command type | ||
| 1357 | /// (Read/Write) and the matched address. This method will suspend until | ||
| 1358 | /// an address match occurs. | ||
| 1359 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | ||
| 1360 | trace!("I2C slave: starting async listen for address match"); | ||
| 1361 | let state = self.state; | ||
| 1362 | let info = self.info; | ||
| 1363 | |||
| 1364 | Self::enable_interrupts(info); | ||
| 1365 | |||
| 1366 | let on_drop = OnDrop::new(|| { | ||
| 1367 | Self::disable_dma_and_interrupts(info); | ||
| 1368 | }); | ||
| 1369 | |||
| 1370 | let result = poll_fn(|cx| { | ||
| 1371 | state.waker.register(cx.waker()); | ||
| 1372 | |||
| 1373 | match Self::check_and_clear_error_flags(info) { | ||
| 1374 | Err(e) => { | ||
| 1375 | error!("I2C slave: error during listen: {:?}", e); | ||
| 1376 | Poll::Ready(Err(e)) | ||
| 1377 | } | ||
| 1378 | Ok(sr1) => { | ||
| 1379 | if sr1.addr() { | ||
| 1380 | let sr2 = info.regs.sr2().read(); | ||
| 1381 | let direction = if sr2.tra() { | ||
| 1382 | SlaveCommandKind::Read | ||
| 1383 | } else { | ||
| 1384 | SlaveCommandKind::Write | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | let matched_address = match Self::decode_matched_address(sr2, info) { | ||
| 1388 | Ok(addr) => { | ||
| 1389 | trace!("I2C slave: address matched, direction={:?}, addr={:?}", direction, addr); | ||
| 1390 | addr | ||
| 1391 | } | ||
| 1392 | Err(e) => { | ||
| 1393 | error!("I2C slave: failed to decode matched address: {:?}", e); | ||
| 1394 | return Poll::Ready(Err(e)); | ||
| 1395 | } | ||
| 1396 | }; | ||
| 1397 | |||
| 1398 | Poll::Ready(Ok(SlaveCommand { | ||
| 1399 | kind: direction, | ||
| 1400 | address: matched_address, | ||
| 1401 | })) | ||
| 1402 | } else { | ||
| 1403 | Self::enable_interrupts(info); | ||
| 1404 | Poll::Pending | ||
| 1405 | } | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | }) | ||
| 1409 | .await; | ||
| 1410 | |||
| 1411 | drop(on_drop); | ||
| 1412 | trace!("I2C slave: listen complete, result={:?}", result); | ||
| 1413 | result | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | /// Async respond to write command using RX DMA | ||
| 1417 | /// | ||
| 1418 | /// Receives data from the master into the provided buffer using DMA. | ||
| 1419 | /// If the master sends more bytes than the buffer can hold, excess bytes | ||
| 1420 | /// are acknowledged but discarded to prevent interrupt flooding. | ||
| 1421 | /// | ||
| 1422 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 1423 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | ||
| 1424 | trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); | ||
| 1425 | |||
| 1426 | if buffer.is_empty() { | ||
| 1427 | warn!("I2C slave: respond_to_write called with empty buffer"); | ||
| 1428 | return Err(Error::Overrun); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | let state = self.state; | ||
| 1432 | let info = self.info; | ||
| 1433 | |||
| 1434 | self.setup_slave_dma_base(); | ||
| 1435 | |||
| 1436 | let on_drop = OnDrop::new(|| { | ||
| 1437 | Self::disable_dma_and_interrupts(info); | ||
| 1438 | }); | ||
| 1439 | |||
| 1440 | info.regs.sr2().read(); | ||
| 1441 | |||
| 1442 | let result = self.execute_slave_receive_transfer(buffer, state, info).await; | ||
| 1443 | |||
| 1444 | drop(on_drop); | ||
| 1445 | trace!("I2C slave: respond_to_write complete, result={:?}", result); | ||
| 1446 | result | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /// Async respond to read command using TX DMA | ||
| 1450 | /// | ||
| 1451 | /// Transmits data to the master using DMA. If the master requests more bytes | ||
| 1452 | /// than available in the data buffer, padding bytes (0x00) are sent until | ||
| 1453 | /// the master terminates the transaction with NACK, STOP, or RESTART. | ||
| 1454 | /// | ||
| 1455 | /// Returns the total number of bytes transmitted (data + padding). | ||
| 1456 | pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | ||
| 1457 | trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); | ||
| 1458 | |||
| 1459 | if data.is_empty() { | ||
| 1460 | warn!("I2C slave: respond_to_read called with empty data"); | ||
| 1461 | return Err(Error::Overrun); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | let state = self.state; | ||
| 1465 | let info = self.info; | ||
| 1466 | |||
| 1467 | self.setup_slave_dma_base(); | ||
| 1468 | |||
| 1469 | let on_drop = OnDrop::new(|| { | ||
| 1470 | Self::disable_dma_and_interrupts(info); | ||
| 1471 | }); | ||
| 1472 | |||
| 1473 | info.regs.sr2().read(); | ||
| 1474 | |||
| 1475 | let result = self.execute_slave_transmit_transfer(data, state, info).await; | ||
| 1476 | |||
| 1477 | drop(on_drop); | ||
| 1478 | trace!("I2C slave: respond_to_read complete, result={:?}", result); | ||
| 1479 | result | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | // === Private Transfer Execution Methods === | ||
| 1483 | |||
| 1484 | /// Execute complete slave receive transfer with excess byte handling | ||
| 1485 | async fn execute_slave_receive_transfer( | ||
| 1486 | &mut self, | ||
| 1487 | buffer: &mut [u8], | ||
| 1488 | state: &'static State, | ||
| 1489 | info: &'static Info, | ||
| 1490 | ) -> Result<usize, Error> { | ||
| 1491 | let dma_transfer = unsafe { | ||
| 1492 | let src = info.regs.dr().as_ptr() as *mut u8; | ||
| 1493 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1494 | }; | ||
| 1495 | |||
| 1496 | let i2c_monitor = | ||
| 1497 | Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart]); | ||
| 1498 | |||
| 1499 | match select(dma_transfer, i2c_monitor).await { | ||
| 1500 | Either::Second(Err(e)) => { | ||
| 1501 | error!("I2C slave: error during receive transfer: {:?}", e); | ||
| 1502 | Self::disable_dma_and_interrupts(info); | ||
| 1503 | Err(e) | ||
| 1504 | } | ||
| 1505 | Either::First(_) => { | ||
| 1506 | trace!("I2C slave: DMA receive completed, handling excess bytes"); | ||
| 1507 | Self::disable_dma_and_interrupts(info); | ||
| 1508 | self.handle_excess_bytes(state, info).await?; | ||
| 1509 | Ok(buffer.len()) | ||
| 1510 | } | ||
| 1511 | Either::Second(Ok(termination)) => { | ||
| 1512 | trace!("I2C slave: receive terminated by I2C event: {:?}", termination); | ||
| 1513 | Self::disable_dma_and_interrupts(info); | ||
| 1514 | Ok(buffer.len()) | ||
| 1515 | } | ||
| 1516 | } | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | /// Execute complete slave transmit transfer with padding byte handling | ||
| 1520 | async fn execute_slave_transmit_transfer( | ||
| 1521 | &mut self, | ||
| 1522 | data: &[u8], | ||
| 1523 | state: &'static State, | ||
| 1524 | info: &'static Info, | ||
| 1525 | ) -> Result<usize, Error> { | ||
| 1526 | let dma_transfer = unsafe { | ||
| 1527 | let dst = info.regs.dr().as_ptr() as *mut u8; | ||
| 1528 | self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) | ||
| 1529 | }; | ||
| 1530 | |||
| 1531 | let i2c_monitor = Self::create_termination_monitor( | ||
| 1532 | state, | ||
| 1533 | info, | ||
| 1534 | &[ | ||
| 1535 | SlaveTermination::Stop, | ||
| 1536 | SlaveTermination::Restart, | ||
| 1537 | SlaveTermination::Nack, | ||
| 1538 | ], | ||
| 1539 | ); | ||
| 1540 | |||
| 1541 | match select(dma_transfer, i2c_monitor).await { | ||
| 1542 | Either::Second(Err(e)) => { | ||
| 1543 | error!("I2C slave: error during transmit transfer: {:?}", e); | ||
| 1544 | Self::disable_dma_and_interrupts(info); | ||
| 1545 | Err(e) | ||
| 1546 | } | ||
| 1547 | Either::First(_) => { | ||
| 1548 | trace!("I2C slave: DMA transmit completed, handling padding bytes"); | ||
| 1549 | Self::disable_dma_and_interrupts(info); | ||
| 1550 | let padding_count = self.handle_padding_bytes(state, info).await?; | ||
| 1551 | let total_bytes = data.len() + padding_count; | ||
| 1552 | trace!( | ||
| 1553 | "I2C slave: sent {} data bytes + {} padding bytes = {} total", | ||
| 1554 | data.len(), | ||
| 1555 | padding_count, | ||
| 1556 | total_bytes | ||
| 1557 | ); | ||
| 1558 | Ok(total_bytes) | ||
| 1559 | } | ||
| 1560 | Either::Second(Ok(termination)) => { | ||
| 1561 | trace!("I2C slave: transmit terminated by I2C event: {:?}", termination); | ||
| 1562 | Self::disable_dma_and_interrupts(info); | ||
| 1563 | Ok(data.len()) | ||
| 1564 | } | ||
| 1565 | } | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | /// Create a future that monitors for specific slave termination conditions | ||
| 1569 | fn create_termination_monitor( | ||
| 1570 | state: &'static State, | ||
| 1571 | info: &'static Info, | ||
| 1572 | allowed_terminations: &'static [SlaveTermination], | ||
| 1573 | ) -> impl Future<Output = Result<SlaveTermination, Error>> { | ||
| 1574 | poll_fn(move |cx| { | ||
| 1575 | state.waker.register(cx.waker()); | ||
| 1576 | |||
| 1577 | match Self::check_and_clear_error_flags(info) { | ||
| 1578 | Err(Error::Nack) if allowed_terminations.contains(&SlaveTermination::Nack) => { | ||
| 1579 | Poll::Ready(Ok(SlaveTermination::Nack)) | ||
| 1580 | } | ||
| 1581 | Err(e) => Poll::Ready(Err(e)), | ||
| 1582 | Ok(sr1) => { | ||
| 1583 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1584 | if allowed_terminations.contains(&termination) { | ||
| 1585 | // Handle the specific termination condition | ||
| 1586 | match termination { | ||
| 1587 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1588 | SlaveTermination::Nack => { | ||
| 1589 | info.regs.sr1().write(|reg| { | ||
| 1590 | reg.0 = !0; | ||
| 1591 | reg.set_af(false); | ||
| 1592 | }); | ||
| 1593 | } | ||
| 1594 | SlaveTermination::Restart => { | ||
| 1595 | // ADDR flag will be handled by next listen() call | ||
| 1596 | } | ||
| 1597 | } | ||
| 1598 | Poll::Ready(Ok(termination)) | ||
| 1599 | } else { | ||
| 1600 | // Unexpected termination condition | ||
| 1601 | Poll::Ready(Err(Error::Bus)) | ||
| 1602 | } | ||
| 1603 | } else { | ||
| 1604 | Self::enable_interrupts(info); | ||
| 1605 | Poll::Pending | ||
| 1606 | } | ||
| 1607 | } | ||
| 1608 | } | ||
| 1609 | }) | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | /// Handle excess bytes after DMA buffer is full | ||
| 1613 | /// | ||
| 1614 | /// Reads and discards bytes until transaction termination to prevent interrupt flooding | ||
| 1615 | async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> { | ||
| 1616 | let mut discarded_count = 0; | ||
| 1617 | |||
| 1618 | poll_fn(|cx| { | ||
| 1619 | state.waker.register(cx.waker()); | ||
| 1620 | |||
| 1621 | match Self::check_and_clear_error_flags(info) { | ||
| 1622 | Err(e) => { | ||
| 1623 | error!("I2C slave: error while discarding excess bytes: {:?}", e); | ||
| 1624 | Poll::Ready(Err(e)) | ||
| 1625 | } | ||
| 1626 | Ok(sr1) => { | ||
| 1627 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1628 | match termination { | ||
| 1629 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1630 | SlaveTermination::Restart => {} | ||
| 1631 | SlaveTermination::Nack => unreachable!("NACK not expected during receive"), | ||
| 1632 | } | ||
| 1633 | if discarded_count > 0 { | ||
| 1634 | trace!("I2C slave: discarded {} excess bytes", discarded_count); | ||
| 1635 | } | ||
| 1636 | return Poll::Ready(Ok(())); | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | if sr1.rxne() { | ||
| 1640 | let _discarded_byte = info.regs.dr().read().dr(); | ||
| 1641 | discarded_count += 1; | ||
| 1642 | Self::enable_interrupts(info); | ||
| 1643 | return Poll::Pending; | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | Self::enable_interrupts(info); | ||
| 1647 | Poll::Pending | ||
| 1648 | } | ||
| 1649 | } | ||
| 1650 | }) | ||
| 1651 | .await | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | /// Handle padding bytes after DMA data is exhausted | ||
| 1655 | /// | ||
| 1656 | /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding | ||
| 1657 | async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<usize, Error> { | ||
| 1658 | let mut padding_count = 0; | ||
| 1659 | |||
| 1660 | poll_fn(|cx| { | ||
| 1661 | state.waker.register(cx.waker()); | ||
| 1662 | |||
| 1663 | match Self::check_and_clear_error_flags(info) { | ||
| 1664 | Err(Error::Nack) => Poll::Ready(Ok(padding_count)), | ||
| 1665 | Err(e) => { | ||
| 1666 | error!("I2C slave: error while sending padding bytes: {:?}", e); | ||
| 1667 | Poll::Ready(Err(e)) | ||
| 1668 | } | ||
| 1669 | Ok(sr1) => { | ||
| 1670 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1671 | match termination { | ||
| 1672 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1673 | SlaveTermination::Restart => {} | ||
| 1674 | SlaveTermination::Nack => { | ||
| 1675 | info.regs.sr1().write(|reg| { | ||
| 1676 | reg.0 = !0; | ||
| 1677 | reg.set_af(false); | ||
| 1678 | }); | ||
| 1679 | } | ||
| 1680 | } | ||
| 1681 | return Poll::Ready(Ok(padding_count)); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | if sr1.txe() { | ||
| 1685 | info.regs.dr().write(|w| w.set_dr(0x00)); | ||
| 1686 | padding_count += 1; | ||
| 1687 | Self::enable_interrupts(info); | ||
| 1688 | return Poll::Pending; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | Self::enable_interrupts(info); | ||
| 1692 | Poll::Pending | ||
| 1693 | } | ||
| 1694 | } | ||
| 1695 | }) | ||
| 1696 | .await | ||
| 1697 | } | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | /// Timing configuration for I2C v1 hardware | ||
| 1701 | /// | ||
| 1702 | /// This struct encapsulates the complex timing calculations required for STM32 I2C v1 | ||
| 1703 | /// peripherals, which use three separate registers (CR2.FREQ, CCR, TRISE) instead of | ||
| 1704 | /// the unified TIMINGR register found in v2 hardware. | ||
| 732 | struct Timings { | 1705 | struct Timings { |
| 733 | freq: u8, | 1706 | freq: u8, // APB frequency in MHz for CR2.FREQ register |
| 734 | mode: Mode, | 1707 | mode: Mode, // Standard or Fast mode selection |
| 735 | trise: u8, | 1708 | trise: u8, // Rise time compensation value |
| 736 | ccr: u16, | 1709 | ccr: u16, // Clock control register value |
| 737 | duty: Duty, | 1710 | duty: Duty, // Fast mode duty cycle selection |
| 738 | } | 1711 | } |
| 739 | 1712 | ||
| 740 | impl Timings { | 1713 | impl Timings { |
| @@ -771,14 +1744,10 @@ impl Timings { | |||
| 771 | duty = Duty::Duty2_1; | 1744 | duty = Duty::Duty2_1; |
| 772 | ccr = clock / (frequency * 3); | 1745 | ccr = clock / (frequency * 3); |
| 773 | ccr = if ccr < 1 { 1 } else { ccr }; | 1746 | ccr = if ccr < 1 { 1 } else { ccr }; |
| 774 | |||
| 775 | // Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle) | ||
| 776 | } else { | 1747 | } else { |
| 777 | duty = Duty::Duty16_9; | 1748 | duty = Duty::Duty16_9; |
| 778 | ccr = clock / (frequency * 25); | 1749 | ccr = clock / (frequency * 25); |
| 779 | ccr = if ccr < 1 { 1 } else { ccr }; | 1750 | ccr = if ccr < 1 { 1 } else { ccr }; |
| 780 | |||
| 781 | // Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle) | ||
| 782 | } | 1751 | } |
| 783 | } | 1752 | } |
| 784 | 1753 | ||
| @@ -788,11 +1757,6 @@ impl Timings { | |||
| 788 | ccr: ccr as u16, | 1757 | ccr: ccr as u16, |
| 789 | duty, | 1758 | duty, |
| 790 | mode, | 1759 | mode, |
| 791 | //prescale: presc_reg, | ||
| 792 | //scll, | ||
| 793 | //sclh, | ||
| 794 | //sdadel, | ||
| 795 | //scldel, | ||
| 796 | } | 1760 | } |
| 797 | } | 1761 | } |
| 798 | } | 1762 | } |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 01b6b8800..57a7acee7 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -70,6 +70,11 @@ fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) { | |||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { | 72 | pub(crate) unsafe fn on_interrupt<T: Instance>() { |
| 73 | // restore the clocks to their last configured state as | ||
| 74 | // much is lost in STOP modes | ||
| 75 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 76 | crate::low_power::Executor::on_wakeup_irq(); | ||
| 77 | |||
| 73 | let regs = T::info().regs; | 78 | let regs = T::info().regs; |
| 74 | let isr = regs.isr().read(); | 79 | let isr = regs.isr().read(); |
| 75 | 80 | ||
| @@ -814,6 +819,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 814 | 819 | ||
| 815 | /// Write. | 820 | /// Write. |
| 816 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 821 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 822 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 823 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 817 | let timeout = self.timeout(); | 824 | let timeout = self.timeout(); |
| 818 | if write.is_empty() { | 825 | if write.is_empty() { |
| 819 | self.write_internal(address.into(), write, true, timeout) | 826 | self.write_internal(address.into(), write, true, timeout) |
| @@ -828,6 +835,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 828 | /// | 835 | /// |
| 829 | /// The buffers are concatenated in a single write transaction. | 836 | /// The buffers are concatenated in a single write transaction. |
| 830 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 837 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 838 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 839 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 831 | let timeout = self.timeout(); | 840 | let timeout = self.timeout(); |
| 832 | 841 | ||
| 833 | if write.is_empty() { | 842 | if write.is_empty() { |
| @@ -851,6 +860,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 851 | 860 | ||
| 852 | /// Read. | 861 | /// Read. |
| 853 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 862 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 863 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 864 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 854 | let timeout = self.timeout(); | 865 | let timeout = self.timeout(); |
| 855 | 866 | ||
| 856 | if buffer.is_empty() { | 867 | if buffer.is_empty() { |
| @@ -863,6 +874,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 863 | 874 | ||
| 864 | /// Write, restart, read. | 875 | /// Write, restart, read. |
| 865 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 876 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 877 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 878 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 866 | let timeout = self.timeout(); | 879 | let timeout = self.timeout(); |
| 867 | 880 | ||
| 868 | if write.is_empty() { | 881 | if write.is_empty() { |
| @@ -888,6 +901,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 888 | /// | 901 | /// |
| 889 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 902 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 890 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 905 | let _device_busy = crate::low_power::DeviceBusy::new_stop1(); | ||
| 891 | let _ = addr; | 906 | let _ = addr; |
| 892 | let _ = operations; | 907 | let _ = operations; |
| 893 | todo!() | 908 | todo!() |
| @@ -1181,7 +1196,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1181 | 1196 | ||
| 1182 | let regs = self.info.regs; | 1197 | let regs = self.info.regs; |
| 1183 | 1198 | ||
| 1184 | let dma_transfer = unsafe { | 1199 | let mut dma_transfer = unsafe { |
| 1185 | regs.cr1().modify(|w| { | 1200 | regs.cr1().modify(|w| { |
| 1186 | w.set_rxdmaen(true); | 1201 | w.set_rxdmaen(true); |
| 1187 | w.set_stopie(true); | 1202 | w.set_stopie(true); |
| @@ -1229,6 +1244,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1229 | }) | 1244 | }) |
| 1230 | .await?; | 1245 | .await?; |
| 1231 | 1246 | ||
| 1247 | dma_transfer.request_pause(); | ||
| 1232 | dma_transfer.await; | 1248 | dma_transfer.await; |
| 1233 | 1249 | ||
| 1234 | drop(on_drop); | 1250 | drop(on_drop); |
| @@ -1294,6 +1310,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1294 | }) | 1310 | }) |
| 1295 | .await?; | 1311 | .await?; |
| 1296 | 1312 | ||
| 1313 | dma_transfer.request_pause(); | ||
| 1297 | dma_transfer.await; | 1314 | dma_transfer.await; |
| 1298 | 1315 | ||
| 1299 | drop(on_drop); | 1316 | drop(on_drop); |
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index db22cfa11..df077a3ae 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs | |||
| @@ -7,6 +7,7 @@ use crate::Peri; | |||
| 7 | use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer}; | 7 | use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer}; |
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 8 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 9 | use crate::mode::Async; | 9 | use crate::mode::Async; |
| 10 | use crate::spi::mode::Master; | ||
| 10 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; | 11 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; |
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | 13 | ||
| @@ -225,7 +226,7 @@ impl<'s, 'd, W: Word> Reader<'s, 'd, W> { | |||
| 225 | pub struct I2S<'d, W: Word> { | 226 | pub struct I2S<'d, W: Word> { |
| 226 | #[allow(dead_code)] | 227 | #[allow(dead_code)] |
| 227 | mode: Mode, | 228 | mode: Mode, |
| 228 | spi: Spi<'d, Async>, | 229 | spi: Spi<'d, Async, Master>, |
| 229 | txsd: Option<Peri<'d, AnyPin>>, | 230 | txsd: Option<Peri<'d, AnyPin>>, |
| 230 | rxsd: Option<Peri<'d, AnyPin>>, | 231 | rxsd: Option<Peri<'d, AnyPin>>, |
| 231 | ws: Option<Peri<'d, AnyPin>>, | 232 | ws: Option<Peri<'d, AnyPin>>, |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index dbf0fe620..e08ab30e6 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -54,6 +54,8 @@ pub mod timer; | |||
| 54 | 54 | ||
| 55 | #[cfg(adc)] | 55 | #[cfg(adc)] |
| 56 | pub mod adc; | 56 | pub mod adc; |
| 57 | #[cfg(backup_sram)] | ||
| 58 | pub mod backup_sram; | ||
| 57 | #[cfg(can)] | 59 | #[cfg(can)] |
| 58 | pub mod can; | 60 | pub mod can; |
| 59 | // FIXME: Cordic driver cause stm32u5a5zj crash | 61 | // FIXME: Cordic driver cause stm32u5a5zj crash |
| @@ -241,6 +243,14 @@ pub struct Config { | |||
| 241 | /// RCC config. | 243 | /// RCC config. |
| 242 | pub rcc: rcc::Config, | 244 | pub rcc: rcc::Config, |
| 243 | 245 | ||
| 246 | #[cfg(feature = "low-power")] | ||
| 247 | /// RTC config | ||
| 248 | pub rtc: rtc::RtcConfig, | ||
| 249 | |||
| 250 | #[cfg(feature = "low-power")] | ||
| 251 | /// Minimum time to stop | ||
| 252 | pub min_stop_pause: embassy_time::Duration, | ||
| 253 | |||
| 244 | /// Enable debug during sleep and stop. | 254 | /// Enable debug during sleep and stop. |
| 245 | /// | 255 | /// |
| 246 | /// May increase power consumption. Defaults to true. | 256 | /// May increase power consumption. Defaults to true. |
| @@ -294,6 +304,10 @@ impl Default for Config { | |||
| 294 | fn default() -> Self { | 304 | fn default() -> Self { |
| 295 | Self { | 305 | Self { |
| 296 | rcc: Default::default(), | 306 | rcc: Default::default(), |
| 307 | #[cfg(feature = "low-power")] | ||
| 308 | rtc: Default::default(), | ||
| 309 | #[cfg(feature = "low-power")] | ||
| 310 | min_stop_pause: embassy_time::Duration::from_millis(250), | ||
| 297 | #[cfg(dbgmcu)] | 311 | #[cfg(dbgmcu)] |
| 298 | enable_debug_during_sleep: true, | 312 | enable_debug_during_sleep: true, |
| 299 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] | 313 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] |
| @@ -623,6 +637,12 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 623 | exti::init(cs); | 637 | exti::init(cs); |
| 624 | 638 | ||
| 625 | rcc::init_rcc(cs, config.rcc); | 639 | rcc::init_rcc(cs, config.rcc); |
| 640 | |||
| 641 | #[cfg(feature = "low-power")] | ||
| 642 | crate::rtc::init_rtc(cs, config.rtc); | ||
| 643 | |||
| 644 | #[cfg(feature = "low-power")] | ||
| 645 | crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause); | ||
| 626 | } | 646 | } |
| 627 | 647 | ||
| 628 | p | 648 | p |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 1b66ca680..696dfe83f 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -41,12 +41,6 @@ | |||
| 41 | //! config.enable_debug_during_sleep = false; | 41 | //! config.enable_debug_during_sleep = false; |
| 42 | //! let p = embassy_stm32::init(config); | 42 | //! let p = embassy_stm32::init(config); |
| 43 | //! | 43 | //! |
| 44 | //! // give the RTC to the executor... | ||
| 45 | //! let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); | ||
| 46 | //! static RTC: StaticCell<Rtc> = StaticCell::new(); | ||
| 47 | //! let rtc = RTC.init(rtc); | ||
| 48 | //! embassy_stm32::low_power::stop_with_rtc(rtc); | ||
| 49 | //! | ||
| 50 | //! // your application here... | 44 | //! // your application here... |
| 51 | //! } | 45 | //! } |
| 52 | //! ``` | 46 | //! ``` |
| @@ -59,24 +53,69 @@ use core::marker::PhantomData; | |||
| 59 | use core::sync::atomic::{Ordering, compiler_fence}; | 53 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 60 | 54 | ||
| 61 | use cortex_m::peripheral::SCB; | 55 | use cortex_m::peripheral::SCB; |
| 56 | use critical_section::CriticalSection; | ||
| 62 | use embassy_executor::*; | 57 | use embassy_executor::*; |
| 63 | 58 | ||
| 64 | use crate::interrupt; | 59 | use crate::interrupt; |
| 65 | use crate::time_driver::{RtcDriver, get_driver}; | 60 | use crate::time_driver::get_driver; |
| 66 | 61 | ||
| 67 | const THREAD_PENDER: usize = usize::MAX; | 62 | const THREAD_PENDER: usize = usize::MAX; |
| 68 | 63 | ||
| 69 | use crate::rtc::Rtc; | ||
| 70 | |||
| 71 | static mut EXECUTOR: Option<Executor> = None; | 64 | static mut EXECUTOR: Option<Executor> = None; |
| 72 | 65 | ||
| 66 | /// Prevent the device from going into the stop mode if held | ||
| 67 | pub struct DeviceBusy(StopMode); | ||
| 68 | |||
| 69 | impl DeviceBusy { | ||
| 70 | /// Create a new DeviceBusy with stop1. | ||
| 71 | pub fn new_stop1() -> Self { | ||
| 72 | Self::new(StopMode::Stop1) | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Create a new DeviceBusy with stop2. | ||
| 76 | pub fn new_stop2() -> Self { | ||
| 77 | Self::new(StopMode::Stop2) | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Create a new DeviceBusy. | ||
| 81 | pub fn new(stop_mode: StopMode) -> Self { | ||
| 82 | critical_section::with(|_| unsafe { | ||
| 83 | match stop_mode { | ||
| 84 | StopMode::Stop1 => { | ||
| 85 | crate::rcc::REFCOUNT_STOP1 += 1; | ||
| 86 | } | ||
| 87 | StopMode::Stop2 => { | ||
| 88 | crate::rcc::REFCOUNT_STOP2 += 1; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | }); | ||
| 92 | |||
| 93 | Self(stop_mode) | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Drop for DeviceBusy { | ||
| 98 | fn drop(&mut self) { | ||
| 99 | critical_section::with(|_| unsafe { | ||
| 100 | match self.0 { | ||
| 101 | StopMode::Stop1 => { | ||
| 102 | crate::rcc::REFCOUNT_STOP1 -= 1; | ||
| 103 | } | ||
| 104 | StopMode::Stop2 => { | ||
| 105 | crate::rcc::REFCOUNT_STOP2 -= 1; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | }); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 73 | #[cfg(not(stm32u0))] | 112 | #[cfg(not(stm32u0))] |
| 74 | foreach_interrupt! { | 113 | foreach_interrupt! { |
| 75 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | 114 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { |
| 76 | #[interrupt] | 115 | #[interrupt] |
| 77 | #[allow(non_snake_case)] | 116 | #[allow(non_snake_case)] |
| 78 | unsafe fn $irq() { | 117 | unsafe fn $irq() { |
| 79 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 118 | Executor::on_wakeup_irq(); |
| 80 | } | 119 | } |
| 81 | }; | 120 | }; |
| 82 | } | 121 | } |
| @@ -87,31 +126,21 @@ foreach_interrupt! { | |||
| 87 | #[interrupt] | 126 | #[interrupt] |
| 88 | #[allow(non_snake_case)] | 127 | #[allow(non_snake_case)] |
| 89 | unsafe fn $irq() { | 128 | unsafe fn $irq() { |
| 90 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 129 | Executor::on_wakeup_irq(); |
| 91 | } | 130 | } |
| 92 | }; | 131 | }; |
| 93 | } | 132 | } |
| 94 | 133 | ||
| 95 | #[allow(dead_code)] | ||
| 96 | pub(crate) unsafe fn on_wakeup_irq() { | ||
| 97 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Configure STOP mode with RTC. | ||
| 101 | pub fn stop_with_rtc(rtc: &'static Rtc) { | ||
| 102 | unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Get whether the core is ready to enter the given stop mode. | 134 | /// Get whether the core is ready to enter the given stop mode. |
| 106 | /// | 135 | /// |
| 107 | /// This will return false if some peripheral driver is in use that | 136 | /// This will return false if some peripheral driver is in use that |
| 108 | /// prevents entering the given stop mode. | 137 | /// prevents entering the given stop mode. |
| 109 | pub fn stop_ready(stop_mode: StopMode) -> bool { | 138 | pub fn stop_ready(stop_mode: StopMode) -> bool { |
| 110 | match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { | 139 | critical_section::with(|cs| match Executor::stop_mode(cs) { |
| 111 | Some(StopMode::Stop2) => true, | 140 | Some(StopMode::Stop2) => true, |
| 112 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, | 141 | Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, |
| 113 | None => false, | 142 | None => false, |
| 114 | } | 143 | }) |
| 115 | } | 144 | } |
| 116 | 145 | ||
| 117 | /// Available Stop modes. | 146 | /// Available Stop modes. |
| @@ -124,10 +153,10 @@ pub enum StopMode { | |||
| 124 | Stop2, | 153 | Stop2, |
| 125 | } | 154 | } |
| 126 | 155 | ||
| 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 156 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] |
| 128 | use stm32_metapac::pwr::vals::Lpms; | 157 | use stm32_metapac::pwr::vals::Lpms; |
| 129 | 158 | ||
| 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] | 159 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] |
| 131 | impl Into<Lpms> for StopMode { | 160 | impl Into<Lpms> for StopMode { |
| 132 | fn into(self) -> Lpms { | 161 | fn into(self) -> Lpms { |
| 133 | match self { | 162 | match self { |
| @@ -154,7 +183,6 @@ pub struct Executor { | |||
| 154 | inner: raw::Executor, | 183 | inner: raw::Executor, |
| 155 | not_send: PhantomData<*mut ()>, | 184 | not_send: PhantomData<*mut ()>, |
| 156 | scb: SCB, | 185 | scb: SCB, |
| 157 | time_driver: &'static RtcDriver, | ||
| 158 | } | 186 | } |
| 159 | 187 | ||
| 160 | impl Executor { | 188 | impl Executor { |
| @@ -167,7 +195,6 @@ impl Executor { | |||
| 167 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), | 195 | inner: raw::Executor::new(THREAD_PENDER as *mut ()), |
| 168 | not_send: PhantomData, | 196 | not_send: PhantomData, |
| 169 | scb: cortex_m::Peripherals::steal().SCB, | 197 | scb: cortex_m::Peripherals::steal().SCB, |
| 170 | time_driver: get_driver(), | ||
| 171 | }); | 198 | }); |
| 172 | 199 | ||
| 173 | let executor = EXECUTOR.as_mut().unwrap(); | 200 | let executor = EXECUTOR.as_mut().unwrap(); |
| @@ -176,21 +203,31 @@ impl Executor { | |||
| 176 | }) | 203 | }) |
| 177 | } | 204 | } |
| 178 | 205 | ||
| 179 | unsafe fn on_wakeup_irq(&mut self) { | 206 | pub(crate) unsafe fn on_wakeup_irq() { |
| 180 | self.time_driver.resume_time(); | 207 | critical_section::with(|cs| { |
| 181 | trace!("low power: resume"); | 208 | #[cfg(stm32wlex)] |
| 182 | } | 209 | { |
| 183 | 210 | let extscr = crate::pac::PWR.extscr().read(); | |
| 184 | pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { | 211 | if extscr.c1stop2f() || extscr.c1stopf() { |
| 185 | self.time_driver.set_rtc(rtc); | 212 | // when we wake from any stop mode we need to re-initialize the rcc |
| 186 | 213 | crate::rcc::apply_resume_config(); | |
| 187 | rtc.enable_wakeup_line(); | 214 | if extscr.c1stop2f() { |
| 188 | 215 | // when we wake from STOP2, we need to re-initialize the time driver | |
| 189 | trace!("low power: stop with rtc configured"); | 216 | crate::time_driver::init_timer(cs); |
| 217 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | ||
| 218 | // and given that we just woke from STOP2, we can reset them | ||
| 219 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 220 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | get_driver().resume_time(cs); | ||
| 225 | trace!("low power: resume"); | ||
| 226 | }); | ||
| 190 | } | 227 | } |
| 191 | 228 | ||
| 192 | fn stop_mode(&self) -> Option<StopMode> { | 229 | fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { |
| 193 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 230 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { |
| 194 | Some(StopMode::Stop2) | 231 | Some(StopMode::Stop2) |
| 195 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 232 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { |
| 196 | Some(StopMode::Stop1) | 233 | Some(StopMode::Stop1) |
| @@ -201,7 +238,7 @@ impl Executor { | |||
| 201 | 238 | ||
| 202 | #[allow(unused_variables)] | 239 | #[allow(unused_variables)] |
| 203 | fn configure_stop(&mut self, stop_mode: StopMode) { | 240 | fn configure_stop(&mut self, stop_mode: StopMode) { |
| 204 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] | 241 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] |
| 205 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 242 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 206 | #[cfg(stm32h5)] | 243 | #[cfg(stm32h5)] |
| 207 | crate::pac::PWR.pmcr().modify(|v| { | 244 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -213,17 +250,22 @@ impl Executor { | |||
| 213 | 250 | ||
| 214 | fn configure_pwr(&mut self) { | 251 | fn configure_pwr(&mut self) { |
| 215 | self.scb.clear_sleepdeep(); | 252 | self.scb.clear_sleepdeep(); |
| 253 | // Clear any previous stop flags | ||
| 254 | #[cfg(stm32wlex)] | ||
| 255 | crate::pac::PWR.extscr().modify(|w| { | ||
| 256 | w.set_c1cssf(true); | ||
| 257 | }); | ||
| 216 | 258 | ||
| 217 | compiler_fence(Ordering::SeqCst); | 259 | compiler_fence(Ordering::SeqCst); |
| 218 | 260 | ||
| 219 | let stop_mode = self.stop_mode(); | 261 | let stop_mode = critical_section::with(|cs| Self::stop_mode(cs)); |
| 220 | 262 | ||
| 221 | if stop_mode.is_none() { | 263 | if stop_mode.is_none() { |
| 222 | trace!("low power: not ready to stop"); | 264 | trace!("low power: not ready to stop"); |
| 223 | return; | 265 | return; |
| 224 | } | 266 | } |
| 225 | 267 | ||
| 226 | if self.time_driver.pause_time().is_err() { | 268 | if get_driver().pause_time().is_err() { |
| 227 | trace!("low power: failed to pause time"); | 269 | trace!("low power: failed to pause time"); |
| 228 | return; | 270 | return; |
| 229 | } | 271 | } |
| @@ -266,6 +308,19 @@ impl Executor { | |||
| 266 | executor.inner.poll(); | 308 | executor.inner.poll(); |
| 267 | self.configure_pwr(); | 309 | self.configure_pwr(); |
| 268 | asm!("wfe"); | 310 | asm!("wfe"); |
| 311 | #[cfg(stm32wlex)] | ||
| 312 | { | ||
| 313 | let es = crate::pac::PWR.extscr().read(); | ||
| 314 | match (es.c1stopf(), es.c1stop2f()) { | ||
| 315 | (true, false) => debug!("low power: wake from STOP1"), | ||
| 316 | (false, true) => debug!("low power: wake from STOP2"), | ||
| 317 | (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"), | ||
| 318 | (false, false) => trace!("low power: stop mode not entered"), | ||
| 319 | }; | ||
| 320 | crate::pac::PWR.extscr().modify(|w| { | ||
| 321 | w.set_c1cssf(false); | ||
| 322 | }); | ||
| 323 | } | ||
| 269 | }; | 324 | }; |
| 270 | } | 325 | } |
| 271 | } | 326 | } |
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 3b2a10581..5b367c043 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | use core::sync::atomic::{Ordering, compiler_fence}; | 1 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 2 | 2 | ||
| 3 | use crate::pac::common::{RW, Reg}; | 3 | use crate::pac::common::{RW, Reg}; |
| 4 | #[cfg(backup_sram)] | ||
| 5 | use crate::pac::pwr::vals::Retention; | ||
| 4 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; | 6 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; |
| 5 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 6 | 8 | ||
| @@ -89,6 +91,8 @@ pub struct LsConfig { | |||
| 89 | pub rtc: RtcClockSource, | 91 | pub rtc: RtcClockSource, |
| 90 | pub lsi: bool, | 92 | pub lsi: bool, |
| 91 | pub lse: Option<LseConfig>, | 93 | pub lse: Option<LseConfig>, |
| 94 | #[cfg(backup_sram)] | ||
| 95 | pub enable_backup_sram: bool, | ||
| 92 | } | 96 | } |
| 93 | 97 | ||
| 94 | impl LsConfig { | 98 | impl LsConfig { |
| @@ -113,6 +117,8 @@ impl LsConfig { | |||
| 113 | peripherals_clocked: false, | 117 | peripherals_clocked: false, |
| 114 | }), | 118 | }), |
| 115 | lsi: false, | 119 | lsi: false, |
| 120 | #[cfg(backup_sram)] | ||
| 121 | enable_backup_sram: false, | ||
| 116 | } | 122 | } |
| 117 | } | 123 | } |
| 118 | 124 | ||
| @@ -121,6 +127,8 @@ impl LsConfig { | |||
| 121 | rtc: RtcClockSource::LSI, | 127 | rtc: RtcClockSource::LSI, |
| 122 | lsi: true, | 128 | lsi: true, |
| 123 | lse: None, | 129 | lse: None, |
| 130 | #[cfg(backup_sram)] | ||
| 131 | enable_backup_sram: false, | ||
| 124 | } | 132 | } |
| 125 | } | 133 | } |
| 126 | 134 | ||
| @@ -129,6 +137,8 @@ impl LsConfig { | |||
| 129 | rtc: RtcClockSource::DISABLE, | 137 | rtc: RtcClockSource::DISABLE, |
| 130 | lsi: false, | 138 | lsi: false, |
| 131 | lse: None, | 139 | lse: None, |
| 140 | #[cfg(backup_sram)] | ||
| 141 | enable_backup_sram: false, | ||
| 132 | } | 142 | } |
| 133 | } | 143 | } |
| 134 | } | 144 | } |
| @@ -193,6 +203,22 @@ impl LsConfig { | |||
| 193 | while !csr.read().lsi1rdy() {} | 203 | while !csr.read().lsi1rdy() {} |
| 194 | } | 204 | } |
| 195 | 205 | ||
| 206 | // Enable backup regulator for peristent battery backed sram | ||
| 207 | #[cfg(backup_sram)] | ||
| 208 | { | ||
| 209 | unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED }; | ||
| 210 | |||
| 211 | crate::pac::PWR.bdcr().modify(|w| { | ||
| 212 | w.set_bren(match self.enable_backup_sram { | ||
| 213 | true => Retention::PRESERVED, | ||
| 214 | false => Retention::LOST, | ||
| 215 | }); | ||
| 216 | }); | ||
| 217 | |||
| 218 | // Wait for backup regulator voltage to stabilize | ||
| 219 | while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {} | ||
| 220 | } | ||
| 221 | |||
| 196 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. | 222 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. |
| 197 | // once set, changing it requires a backup domain reset. | 223 | // once set, changing it requires a backup domain reset. |
| 198 | // first check if the configuration matches what we want. | 224 | // first check if the configuration matches what we want. |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 2e1cbd702..584957c6d 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -1,3 +1,6 @@ | |||
| 1 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 2 | use core::mem::MaybeUninit; | ||
| 3 | |||
| 1 | #[cfg(any(stm32l0, stm32l1))] | 4 | #[cfg(any(stm32l0, stm32l1))] |
| 2 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 5 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 3 | use crate::pac::rcc::regs::Cfgr; | 6 | use crate::pac::rcc::regs::Cfgr; |
| @@ -11,6 +14,42 @@ use crate::time::Hertz; | |||
| 11 | /// HSI speed | 14 | /// HSI speed |
| 12 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 15 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 13 | 16 | ||
| 17 | /// Saved RCC Config | ||
| 18 | /// | ||
| 19 | /// Used when exiting STOP2 to re-enable clocks to their last configured state | ||
| 20 | /// for chips that need it. | ||
| 21 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 22 | static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit(); | ||
| 23 | |||
| 24 | /// Set the rcc config to be restored when exiting STOP2 | ||
| 25 | /// | ||
| 26 | /// Safety: Sets a mutable global. | ||
| 27 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 28 | pub(crate) unsafe fn set_resume_config(config: Config) { | ||
| 29 | trace!("rcc set_resume_config()"); | ||
| 30 | RESUME_RCC_CONFIG = MaybeUninit::new(config); | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Get the rcc config to be restored when exiting STOP2 | ||
| 34 | /// | ||
| 35 | /// Safety: Reads a mutable global. | ||
| 36 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 37 | pub(crate) unsafe fn get_resume_config() -> Config { | ||
| 38 | *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref() | ||
| 39 | } | ||
| 40 | |||
| 41 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 42 | /// Safety: should only be called from low power executable just after resuming from STOP2 | ||
| 43 | pub(crate) unsafe fn apply_resume_config() { | ||
| 44 | trace!("rcc apply_resume_config()"); | ||
| 45 | |||
| 46 | while RCC.cfgr().read().sws() != Sysclk::MSI {} | ||
| 47 | |||
| 48 | let config = get_resume_config(); | ||
| 49 | |||
| 50 | init(config); | ||
| 51 | } | ||
| 52 | |||
| 14 | #[derive(Clone, Copy, Eq, PartialEq)] | 53 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 15 | pub enum HseMode { | 54 | pub enum HseMode { |
| 16 | /// crystal/ceramic oscillator (HSEBYP=0) | 55 | /// crystal/ceramic oscillator (HSEBYP=0) |
| @@ -154,6 +193,10 @@ fn msi_enable(range: MSIRange) { | |||
| 154 | } | 193 | } |
| 155 | 194 | ||
| 156 | pub(crate) unsafe fn init(config: Config) { | 195 | pub(crate) unsafe fn init(config: Config) { |
| 196 | // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup | ||
| 197 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 198 | set_resume_config(config); | ||
| 199 | |||
| 157 | // Switch to MSI to prevent problems with PLL configuration. | 200 | // Switch to MSI to prevent problems with PLL configuration. |
| 158 | if !RCC.cr().read().msion() { | 201 | if !RCC.cr().read().msion() { |
| 159 | // Turn on MSI and configure it to 4MHz. | 202 | // Turn on MSI and configure it to 4MHz. |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index addfca3c3..01fa3a475 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -48,6 +48,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 48 | /// May be read without a critical section | 48 | /// May be read without a critical section |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | 50 | ||
| 51 | #[cfg(backup_sram)] | ||
| 52 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | ||
| 53 | |||
| 51 | #[cfg(not(feature = "_dual-core"))] | 54 | #[cfg(not(feature = "_dual-core"))] |
| 52 | /// Frozen clock frequencies | 55 | /// Frozen clock frequencies |
| 53 | /// | 56 | /// |
| @@ -390,7 +393,7 @@ pub fn disable<T: RccPeripheral>() { | |||
| 390 | /// | 393 | /// |
| 391 | /// This should only be called after `init`. | 394 | /// This should only be called after `init`. |
| 392 | #[cfg(not(feature = "_dual-core"))] | 395 | #[cfg(not(feature = "_dual-core"))] |
| 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | 396 | pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) { |
| 394 | critical_section::with(|cs| init_rcc(cs, config)) | 397 | critical_section::with(|cs| init_rcc(cs, config)) |
| 395 | } | 398 | } |
| 396 | 399 | ||
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 999f24714..e5bf30927 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -4,7 +4,7 @@ use embassy_time::{Duration, TICK_HZ}; | |||
| 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; | 4 | use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; |
| 5 | use crate::interrupt::typelevel::Interrupt; | 5 | use crate::interrupt::typelevel::Interrupt; |
| 6 | use crate::peripherals::RTC; | 6 | use crate::peripherals::RTC; |
| 7 | use crate::rtc::SealedInstance; | 7 | use crate::rtc::{RtcTimeProvider, SealedInstance}; |
| 8 | 8 | ||
| 9 | /// Represents an instant in time that can be substracted to compute a duration | 9 | /// Represents an instant in time that can be substracted to compute a duration |
| 10 | pub(super) struct RtcInstant { | 10 | pub(super) struct RtcInstant { |
| @@ -68,7 +68,7 @@ pub(crate) enum WakeupPrescaler { | |||
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | #[cfg(any( | 70 | #[cfg(any( |
| 71 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | 71 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex |
| 72 | ))] | 72 | ))] |
| 73 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | 73 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { |
| 74 | fn from(val: WakeupPrescaler) -> Self { | 74 | fn from(val: WakeupPrescaler) -> Self { |
| @@ -84,7 +84,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | |||
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | #[cfg(any( | 86 | #[cfg(any( |
| 87 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba | 87 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex |
| 88 | ))] | 88 | ))] |
| 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | 89 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { |
| 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | 90 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { |
| @@ -117,7 +117,7 @@ impl WakeupPrescaler { | |||
| 117 | impl Rtc { | 117 | impl Rtc { |
| 118 | /// Return the current instant. | 118 | /// Return the current instant. |
| 119 | fn instant(&self) -> Result<RtcInstant, RtcError> { | 119 | fn instant(&self) -> Result<RtcInstant, RtcError> { |
| 120 | self.time_provider().read(|_, tr, ss| { | 120 | RtcTimeProvider::new().read(|_, tr, ss| { |
| 121 | let second = bcd2_to_byte((tr.st(), tr.su())); | 121 | let second = bcd2_to_byte((tr.st(), tr.su())); |
| 122 | 122 | ||
| 123 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) | 123 | RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) |
| @@ -127,7 +127,7 @@ impl Rtc { | |||
| 127 | /// start the wakeup alarm and with a duration that is as close to but less than | 127 | /// start the wakeup alarm and with a duration that is as close to but less than |
| 128 | /// the requested duration, and record the instant the wakeup alarm was started | 128 | /// the requested duration, and record the instant the wakeup alarm was started |
| 129 | pub(crate) fn start_wakeup_alarm( | 129 | pub(crate) fn start_wakeup_alarm( |
| 130 | &self, | 130 | &mut self, |
| 131 | requested_duration: embassy_time::Duration, | 131 | requested_duration: embassy_time::Duration, |
| 132 | cs: critical_section::CriticalSection, | 132 | cs: critical_section::CriticalSection, |
| 133 | ) { | 133 | ) { |
| @@ -179,7 +179,10 @@ impl Rtc { | |||
| 179 | 179 | ||
| 180 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | 180 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 181 | /// was called, otherwise none | 181 | /// was called, otherwise none |
| 182 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { | 182 | pub(crate) fn stop_wakeup_alarm( |
| 183 | &mut self, | ||
| 184 | cs: critical_section::CriticalSection, | ||
| 185 | ) -> Option<embassy_time::Duration> { | ||
| 183 | let instant = self.instant().unwrap(); | 186 | let instant = self.instant().unwrap(); |
| 184 | if RTC::regs().cr().read().wute() { | 187 | if RTC::regs().cr().read().wute() { |
| 185 | trace!("rtc: stop wakeup alarm at {}", instant); | 188 | trace!("rtc: stop wakeup alarm at {}", instant); |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index bc6df528b..116b3c7ed 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -5,9 +5,13 @@ mod datetime; | |||
| 5 | mod low_power; | 5 | mod low_power; |
| 6 | 6 | ||
| 7 | #[cfg(feature = "low-power")] | 7 | #[cfg(feature = "low-power")] |
| 8 | use core::cell::Cell; | 8 | use core::cell::{Cell, RefCell, RefMut}; |
| 9 | #[cfg(feature = "low-power")] | ||
| 10 | use core::ops; | ||
| 9 | 11 | ||
| 10 | #[cfg(feature = "low-power")] | 12 | #[cfg(feature = "low-power")] |
| 13 | use critical_section::CriticalSection; | ||
| 14 | #[cfg(feature = "low-power")] | ||
| 11 | use embassy_sync::blocking_mutex::Mutex; | 15 | use embassy_sync::blocking_mutex::Mutex; |
| 12 | #[cfg(feature = "low-power")] | 16 | #[cfg(feature = "low-power")] |
| 13 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 17 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| @@ -44,11 +48,17 @@ pub enum RtcError { | |||
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | /// Provides immutable access to the current time of the RTC. | 50 | /// Provides immutable access to the current time of the RTC. |
| 51 | #[derive(Clone)] | ||
| 47 | pub struct RtcTimeProvider { | 52 | pub struct RtcTimeProvider { |
| 48 | _private: (), | 53 | _private: (), |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 51 | impl RtcTimeProvider { | 56 | impl RtcTimeProvider { |
| 57 | /// Create a new RTC time provider instance. | ||
| 58 | pub(self) const fn new() -> Self { | ||
| 59 | Self { _private: () } | ||
| 60 | } | ||
| 61 | |||
| 52 | /// Return the current datetime. | 62 | /// Return the current datetime. |
| 53 | /// | 63 | /// |
| 54 | /// # Errors | 64 | /// # Errors |
| @@ -106,6 +116,50 @@ impl RtcTimeProvider { | |||
| 106 | } | 116 | } |
| 107 | } | 117 | } |
| 108 | 118 | ||
| 119 | #[cfg(feature = "low-power")] | ||
| 120 | /// Contains an RTC driver. | ||
| 121 | pub struct RtcContainer { | ||
| 122 | pub(self) mutex: &'static Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, | ||
| 123 | } | ||
| 124 | |||
| 125 | #[cfg(feature = "low-power")] | ||
| 126 | impl RtcContainer { | ||
| 127 | pub(self) const fn new() -> Self { | ||
| 128 | Self { | ||
| 129 | mutex: &crate::time_driver::get_driver().rtc, | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Acquire an RTC borrow. | ||
| 134 | pub fn borrow_mut<'a>(&self, cs: CriticalSection<'a>) -> RtcBorrow<'a> { | ||
| 135 | RtcBorrow { | ||
| 136 | ref_mut: self.mutex.borrow(cs).borrow_mut(), | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | #[cfg(feature = "low-power")] | ||
| 142 | /// Contains an RTC borrow. | ||
| 143 | pub struct RtcBorrow<'a> { | ||
| 144 | pub(self) ref_mut: RefMut<'a, Option<Rtc>>, | ||
| 145 | } | ||
| 146 | |||
| 147 | #[cfg(feature = "low-power")] | ||
| 148 | impl<'a> ops::Deref for RtcBorrow<'a> { | ||
| 149 | type Target = Rtc; | ||
| 150 | |||
| 151 | fn deref(&self) -> &Self::Target { | ||
| 152 | self.ref_mut.as_ref().unwrap() | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | #[cfg(feature = "low-power")] | ||
| 157 | impl<'a> ops::DerefMut for RtcBorrow<'a> { | ||
| 158 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 159 | self.ref_mut.as_mut().unwrap() | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 109 | /// RTC driver. | 163 | /// RTC driver. |
| 110 | pub struct Rtc { | 164 | pub struct Rtc { |
| 111 | #[cfg(feature = "low-power")] | 165 | #[cfg(feature = "low-power")] |
| @@ -121,13 +175,21 @@ pub struct RtcConfig { | |||
| 121 | /// | 175 | /// |
| 122 | /// A high counter frequency may impact stop power consumption | 176 | /// A high counter frequency may impact stop power consumption |
| 123 | pub frequency: Hertz, | 177 | pub frequency: Hertz, |
| 178 | |||
| 179 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 180 | /// Allow disabling the rtc, even when stop is configured | ||
| 181 | pub _disable_rtc: bool, | ||
| 124 | } | 182 | } |
| 125 | 183 | ||
| 126 | impl Default for RtcConfig { | 184 | impl Default for RtcConfig { |
| 127 | /// LSI with prescalers assuming 32.768 kHz. | 185 | /// LSI with prescalers assuming 32.768 kHz. |
| 128 | /// Raw sub-seconds in 1/256. | 186 | /// Raw sub-seconds in 1/256. |
| 129 | fn default() -> Self { | 187 | fn default() -> Self { |
| 130 | RtcConfig { frequency: Hertz(256) } | 188 | RtcConfig { |
| 189 | frequency: Hertz(256), | ||
| 190 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 191 | _disable_rtc: false, | ||
| 192 | } | ||
| 131 | } | 193 | } |
| 132 | } | 194 | } |
| 133 | 195 | ||
| @@ -145,8 +207,19 @@ pub enum RtcCalibrationCyclePeriod { | |||
| 145 | } | 207 | } |
| 146 | 208 | ||
| 147 | impl Rtc { | 209 | impl Rtc { |
| 210 | #[cfg(not(feature = "low-power"))] | ||
| 211 | /// Create a new RTC instance. | ||
| 212 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> (Self, RtcTimeProvider) { | ||
| 213 | (Self::new_inner(rtc_config), RtcTimeProvider::new()) | ||
| 214 | } | ||
| 215 | |||
| 216 | #[cfg(feature = "low-power")] | ||
| 148 | /// Create a new RTC instance. | 217 | /// Create a new RTC instance. |
| 149 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { | 218 | pub fn new(_rtc: Peri<'static, RTC>) -> (RtcContainer, RtcTimeProvider) { |
| 219 | (RtcContainer::new(), RtcTimeProvider::new()) | ||
| 220 | } | ||
| 221 | |||
| 222 | pub(self) fn new_inner(rtc_config: RtcConfig) -> Self { | ||
| 150 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] | 223 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] |
| 151 | crate::rcc::enable_and_reset::<RTC>(); | 224 | crate::rcc::enable_and_reset::<RTC>(); |
| 152 | 225 | ||
| @@ -165,10 +238,13 @@ impl Rtc { | |||
| 165 | // Wait for the clock to update after initialization | 238 | // Wait for the clock to update after initialization |
| 166 | #[cfg(not(rtc_v2_f2))] | 239 | #[cfg(not(rtc_v2_f2))] |
| 167 | { | 240 | { |
| 168 | let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); | 241 | let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap(); |
| 169 | while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} | 242 | while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {} |
| 170 | } | 243 | } |
| 171 | 244 | ||
| 245 | #[cfg(feature = "low-power")] | ||
| 246 | this.enable_wakeup_line(); | ||
| 247 | |||
| 172 | this | 248 | this |
| 173 | } | 249 | } |
| 174 | 250 | ||
| @@ -177,11 +253,6 @@ impl Rtc { | |||
| 177 | freqs.rtc.to_hertz().unwrap() | 253 | freqs.rtc.to_hertz().unwrap() |
| 178 | } | 254 | } |
| 179 | 255 | ||
| 180 | /// Acquire a [`RtcTimeProvider`] instance. | ||
| 181 | pub const fn time_provider(&self) -> RtcTimeProvider { | ||
| 182 | RtcTimeProvider { _private: () } | ||
| 183 | } | ||
| 184 | |||
| 185 | /// Set the datetime to a new value. | 256 | /// Set the datetime to a new value. |
| 186 | /// | 257 | /// |
| 187 | /// # Errors | 258 | /// # Errors |
| @@ -225,15 +296,6 @@ impl Rtc { | |||
| 225 | Ok(()) | 296 | Ok(()) |
| 226 | } | 297 | } |
| 227 | 298 | ||
| 228 | /// Return the current datetime. | ||
| 229 | /// | ||
| 230 | /// # Errors | ||
| 231 | /// | ||
| 232 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | ||
| 233 | pub fn now(&self) -> Result<DateTime, RtcError> { | ||
| 234 | self.time_provider().now() | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Check if daylight savings time is active. | 299 | /// Check if daylight savings time is active. |
| 238 | pub fn get_daylight_savings(&self) -> bool { | 300 | pub fn get_daylight_savings(&self) -> bool { |
| 239 | let cr = RTC::regs().cr().read(); | 301 | let cr = RTC::regs().cr().read(); |
| @@ -315,3 +377,15 @@ trait SealedInstance { | |||
| 315 | 377 | ||
| 316 | // fn apply_config(&mut self, rtc_config: RtcConfig); | 378 | // fn apply_config(&mut self, rtc_config: RtcConfig); |
| 317 | } | 379 | } |
| 380 | |||
| 381 | #[cfg(feature = "low-power")] | ||
| 382 | pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { | ||
| 383 | #[cfg(feature = "_allow-disable-rtc")] | ||
| 384 | if config._disable_rtc { | ||
| 385 | return; | ||
| 386 | } | ||
| 387 | |||
| 388 | crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); | ||
| 389 | |||
| 390 | trace!("low power: stop with rtc configured"); | ||
| 391 | } | ||
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 23f6ccb0c..8ac022536 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -93,7 +93,7 @@ impl super::Rtc { | |||
| 93 | }) | 93 | }) |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R | 96 | pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R |
| 97 | where | 97 | where |
| 98 | F: FnOnce(crate::pac::rtc::Rtc) -> R, | 98 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
| 99 | { | 99 | { |
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 01da5d70a..f7ebea73e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs | |||
| @@ -95,7 +95,7 @@ impl super::Rtc { | |||
| 95 | }) | 95 | }) |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R | 98 | pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R |
| 99 | where | 99 | where |
| 100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, | 100 | F: FnOnce(crate::pac::rtc::Rtc) -> R, |
| 101 | { | 101 | { |
| @@ -131,7 +131,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 131 | 131 | ||
| 132 | #[cfg(feature = "low-power")] | 132 | #[cfg(feature = "low-power")] |
| 133 | cfg_if::cfg_if!( | 133 | cfg_if::cfg_if!( |
| 134 | if #[cfg(stm32g4)] { | 134 | if #[cfg(any(stm32g4, stm32wlex))] { |
| 135 | const EXTI_WAKEUP_LINE: usize = 20; | 135 | const EXTI_WAKEUP_LINE: usize = 20; |
| 136 | } else if #[cfg(stm32g0)] { | 136 | } else if #[cfg(stm32g0)] { |
| 137 | const EXTI_WAKEUP_LINE: usize = 19; | 137 | const EXTI_WAKEUP_LINE: usize = 19; |
| @@ -142,7 +142,7 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 142 | 142 | ||
| 143 | #[cfg(feature = "low-power")] | 143 | #[cfg(feature = "low-power")] |
| 144 | cfg_if::cfg_if!( | 144 | cfg_if::cfg_if!( |
| 145 | if #[cfg(stm32g4)] { | 145 | if #[cfg(any(stm32g4, stm32wlex))] { |
| 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
| 147 | } else if #[cfg(any(stm32g0, stm32u0))] { | 147 | } else if #[cfg(any(stm32g0, stm32u0))] { |
| 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; | 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 408d1b764..e05131040 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -1032,12 +1032,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1032 | 1032 | ||
| 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1033 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1034 | #[inline] | 1034 | #[inline] |
| 1035 | #[allow(unused)] | ||
| 1035 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { | 1036 | async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { |
| 1036 | let regs = T::regs(); | ||
| 1037 | |||
| 1038 | let res = poll_fn(|cx| { | 1037 | let res = poll_fn(|cx| { |
| 1038 | // Compiler might not be sufficiently constrained here | ||
| 1039 | // https://github.com/embassy-rs/embassy/issues/4723 | ||
| 1039 | T::state().register(cx.waker()); | 1040 | T::state().register(cx.waker()); |
| 1040 | let status = regs.star().read(); | 1041 | let status = T::regs().star().read(); |
| 1041 | 1042 | ||
| 1042 | if status.dcrcfail() { | 1043 | if status.dcrcfail() { |
| 1043 | return Poll::Ready(Err(Error::Crc)); | 1044 | return Poll::Ready(Err(Error::Crc)); |
| @@ -1052,10 +1053,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1052 | if status.stbiterr() { | 1053 | if status.stbiterr() { |
| 1053 | return Poll::Ready(Err(Error::StBitErr)); | 1054 | return Poll::Ready(Err(Error::StBitErr)); |
| 1054 | } | 1055 | } |
| 1056 | #[cfg(sdmmc_v1)] | ||
| 1055 | let done = match block { | 1057 | let done = match block { |
| 1056 | true => status.dbckend(), | 1058 | true => status.dbckend(), |
| 1057 | false => status.dataend(), | 1059 | false => status.dataend(), |
| 1058 | }; | 1060 | }; |
| 1061 | #[cfg(sdmmc_v2)] | ||
| 1062 | let done = status.dataend(); | ||
| 1059 | if done { | 1063 | if done { |
| 1060 | return Poll::Ready(Ok(())); | 1064 | return Poll::Ready(Ok(())); |
| 1061 | } | 1065 | } |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c27d09ea7..abb80ed26 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -125,26 +125,69 @@ impl Config { | |||
| 125 | ) | 125 | ) |
| 126 | } | 126 | } |
| 127 | } | 127 | } |
| 128 | |||
| 129 | /// SPI communication mode | ||
| 130 | pub mod mode { | ||
| 131 | use stm32_metapac::spi::vals; | ||
| 132 | |||
| 133 | trait SealedMode {} | ||
| 134 | |||
| 135 | /// Trait for SPI communication mode operations. | ||
| 136 | #[allow(private_bounds)] | ||
| 137 | pub trait CommunicationMode: SealedMode { | ||
| 138 | /// Spi communication mode | ||
| 139 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 140 | const MASTER: vals::Mstr; | ||
| 141 | /// Spi communication mode | ||
| 142 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 143 | const MASTER: vals::Master; | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Mode allowing for SPI master operations. | ||
| 147 | pub struct Master; | ||
| 148 | /// Mode allowing for SPI slave operations. | ||
| 149 | pub struct Slave; | ||
| 150 | |||
| 151 | impl SealedMode for Master {} | ||
| 152 | impl CommunicationMode for Master { | ||
| 153 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 154 | const MASTER: vals::Mstr = vals::Mstr::MASTER; | ||
| 155 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 156 | const MASTER: vals::Master = vals::Master::MASTER; | ||
| 157 | } | ||
| 158 | |||
| 159 | impl SealedMode for Slave {} | ||
| 160 | impl CommunicationMode for Slave { | ||
| 161 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 162 | const MASTER: vals::Mstr = vals::Mstr::SLAVE; | ||
| 163 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 164 | const MASTER: vals::Master = vals::Master::SLAVE; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | use mode::{CommunicationMode, Master, Slave}; | ||
| 168 | |||
| 128 | /// SPI driver. | 169 | /// SPI driver. |
| 129 | pub struct Spi<'d, M: PeriMode> { | 170 | pub struct Spi<'d, M: PeriMode, CM: CommunicationMode> { |
| 130 | pub(crate) info: &'static Info, | 171 | pub(crate) info: &'static Info, |
| 131 | kernel_clock: Hertz, | 172 | kernel_clock: Hertz, |
| 132 | sck: Option<Peri<'d, AnyPin>>, | 173 | sck: Option<Peri<'d, AnyPin>>, |
| 133 | mosi: Option<Peri<'d, AnyPin>>, | 174 | mosi: Option<Peri<'d, AnyPin>>, |
| 134 | miso: Option<Peri<'d, AnyPin>>, | 175 | miso: Option<Peri<'d, AnyPin>>, |
| 176 | nss: Option<Peri<'d, AnyPin>>, | ||
| 135 | tx_dma: Option<ChannelAndRequest<'d>>, | 177 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 136 | rx_dma: Option<ChannelAndRequest<'d>>, | 178 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 137 | _phantom: PhantomData<M>, | 179 | _phantom: PhantomData<(M, CM)>, |
| 138 | current_word_size: word_impl::Config, | 180 | current_word_size: word_impl::Config, |
| 139 | gpio_speed: Speed, | 181 | gpio_speed: Speed, |
| 140 | } | 182 | } |
| 141 | 183 | ||
| 142 | impl<'d, M: PeriMode> Spi<'d, M> { | 184 | impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { |
| 143 | fn new_inner<T: Instance>( | 185 | fn new_inner<T: Instance>( |
| 144 | _peri: Peri<'d, T>, | 186 | _peri: Peri<'d, T>, |
| 145 | sck: Option<Peri<'d, AnyPin>>, | 187 | sck: Option<Peri<'d, AnyPin>>, |
| 146 | mosi: Option<Peri<'d, AnyPin>>, | 188 | mosi: Option<Peri<'d, AnyPin>>, |
| 147 | miso: Option<Peri<'d, AnyPin>>, | 189 | miso: Option<Peri<'d, AnyPin>>, |
| 190 | nss: Option<Peri<'d, AnyPin>>, | ||
| 148 | tx_dma: Option<ChannelAndRequest<'d>>, | 191 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 149 | rx_dma: Option<ChannelAndRequest<'d>>, | 192 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 150 | config: Config, | 193 | config: Config, |
| @@ -155,6 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 155 | sck, | 198 | sck, |
| 156 | mosi, | 199 | mosi, |
| 157 | miso, | 200 | miso, |
| 201 | nss, | ||
| 158 | tx_dma, | 202 | tx_dma, |
| 159 | rx_dma, | 203 | rx_dma, |
| 160 | current_word_size: <u8 as SealedWord>::CONFIG, | 204 | current_word_size: <u8 as SealedWord>::CONFIG, |
| @@ -183,12 +227,12 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 183 | w.set_cpha(cpha); | 227 | w.set_cpha(cpha); |
| 184 | w.set_cpol(cpol); | 228 | w.set_cpol(cpol); |
| 185 | 229 | ||
| 186 | w.set_mstr(vals::Mstr::MASTER); | 230 | w.set_mstr(CM::MASTER); |
| 187 | w.set_br(br); | 231 | w.set_br(br); |
| 188 | w.set_spe(true); | 232 | w.set_spe(true); |
| 189 | w.set_lsbfirst(lsbfirst); | 233 | w.set_lsbfirst(lsbfirst); |
| 190 | w.set_ssi(true); | 234 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 191 | w.set_ssm(true); | 235 | w.set_ssm(CM::MASTER == vals::Mstr::MASTER); |
| 192 | w.set_crcen(false); | 236 | w.set_crcen(false); |
| 193 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 237 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 194 | // we're doing "fake rxonly", by actually writing one | 238 | // we're doing "fake rxonly", by actually writing one |
| @@ -210,11 +254,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 210 | w.set_cpha(cpha); | 254 | w.set_cpha(cpha); |
| 211 | w.set_cpol(cpol); | 255 | w.set_cpol(cpol); |
| 212 | 256 | ||
| 213 | w.set_mstr(vals::Mstr::MASTER); | 257 | w.set_mstr(CM::MASTER); |
| 214 | w.set_br(br); | 258 | w.set_br(br); |
| 215 | w.set_lsbfirst(lsbfirst); | 259 | w.set_lsbfirst(lsbfirst); |
| 216 | w.set_ssi(true); | 260 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 217 | w.set_ssm(true); | 261 | w.set_ssm(CM::MASTER == vals::Mstr::MASTER); |
| 218 | w.set_crcen(false); | 262 | w.set_crcen(false); |
| 219 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 263 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 220 | w.set_spe(true); | 264 | w.set_spe(true); |
| @@ -229,8 +273,8 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 229 | w.set_cpha(cpha); | 273 | w.set_cpha(cpha); |
| 230 | w.set_cpol(cpol); | 274 | w.set_cpol(cpol); |
| 231 | w.set_lsbfirst(lsbfirst); | 275 | w.set_lsbfirst(lsbfirst); |
| 232 | w.set_ssm(true); | 276 | w.set_ssm(CM::MASTER == vals::Master::MASTER); |
| 233 | w.set_master(vals::Master::MASTER); | 277 | w.set_master(CM::MASTER); |
| 234 | w.set_comm(vals::Comm::FULL_DUPLEX); | 278 | w.set_comm(vals::Comm::FULL_DUPLEX); |
| 235 | w.set_ssom(vals::Ssom::ASSERTED); | 279 | w.set_ssom(vals::Ssom::ASSERTED); |
| 236 | w.set_midi(0); | 280 | w.set_midi(0); |
| @@ -469,7 +513,30 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 469 | } | 513 | } |
| 470 | } | 514 | } |
| 471 | 515 | ||
| 472 | impl<'d> Spi<'d, Blocking> { | 516 | impl<'d> Spi<'d, Blocking, Slave> { |
| 517 | /// Create a new blocking SPI slave driver. | ||
| 518 | pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>( | ||
| 519 | peri: Peri<'d, T>, | ||
| 520 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 521 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 522 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 523 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 524 | config: Config, | ||
| 525 | ) -> Self { | ||
| 526 | Self::new_inner( | ||
| 527 | peri, | ||
| 528 | new_pin!(sck, config.sck_af()), | ||
| 529 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 530 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 531 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 532 | None, | ||
| 533 | None, | ||
| 534 | config, | ||
| 535 | ) | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | impl<'d> Spi<'d, Blocking, Master> { | ||
| 473 | /// Create a new blocking SPI driver. | 540 | /// Create a new blocking SPI driver. |
| 474 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( | 541 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 475 | peri: Peri<'d, T>, | 542 | peri: Peri<'d, T>, |
| @@ -485,6 +552,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 485 | new_pin!(miso, AfType::input(config.miso_pull)), | 552 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 486 | None, | 553 | None, |
| 487 | None, | 554 | None, |
| 555 | None, | ||
| 488 | config, | 556 | config, |
| 489 | ) | 557 | ) |
| 490 | } | 558 | } |
| @@ -503,6 +571,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 503 | new_pin!(miso, AfType::input(config.miso_pull)), | 571 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 504 | None, | 572 | None, |
| 505 | None, | 573 | None, |
| 574 | None, | ||
| 506 | config, | 575 | config, |
| 507 | ) | 576 | ) |
| 508 | } | 577 | } |
| @@ -521,6 +590,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 521 | None, | 590 | None, |
| 522 | None, | 591 | None, |
| 523 | None, | 592 | None, |
| 593 | None, | ||
| 524 | config, | 594 | config, |
| 525 | ) | 595 | ) |
| 526 | } | 596 | } |
| @@ -540,12 +610,38 @@ impl<'d> Spi<'d, Blocking> { | |||
| 540 | None, | 610 | None, |
| 541 | None, | 611 | None, |
| 542 | None, | 612 | None, |
| 613 | None, | ||
| 614 | config, | ||
| 615 | ) | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 619 | impl<'d> Spi<'d, Async, Slave> { | ||
| 620 | /// Create a new SPI slave driver. | ||
| 621 | pub fn new_slave<T: Instance, #[cfg(afio)] A>( | ||
| 622 | peri: Peri<'d, T>, | ||
| 623 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 624 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 625 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 626 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 627 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 628 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 629 | config: Config, | ||
| 630 | ) -> Self { | ||
| 631 | Self::new_inner( | ||
| 632 | peri, | ||
| 633 | new_pin!(sck, config.sck_af()), | ||
| 634 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 635 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 636 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 637 | new_dma!(tx_dma), | ||
| 638 | new_dma!(rx_dma), | ||
| 543 | config, | 639 | config, |
| 544 | ) | 640 | ) |
| 545 | } | 641 | } |
| 546 | } | 642 | } |
| 547 | 643 | ||
| 548 | impl<'d> Spi<'d, Async> { | 644 | impl<'d> Spi<'d, Async, Master> { |
| 549 | /// Create a new SPI driver. | 645 | /// Create a new SPI driver. |
| 550 | pub fn new<T: Instance, #[cfg(afio)] A>( | 646 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 551 | peri: Peri<'d, T>, | 647 | peri: Peri<'d, T>, |
| @@ -561,6 +657,7 @@ impl<'d> Spi<'d, Async> { | |||
| 561 | new_pin!(sck, config.sck_af()), | 657 | new_pin!(sck, config.sck_af()), |
| 562 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 658 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 563 | new_pin!(miso, AfType::input(config.miso_pull)), | 659 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 660 | None, | ||
| 564 | new_dma!(tx_dma), | 661 | new_dma!(tx_dma), |
| 565 | new_dma!(rx_dma), | 662 | new_dma!(rx_dma), |
| 566 | config, | 663 | config, |
| @@ -581,6 +678,7 @@ impl<'d> Spi<'d, Async> { | |||
| 581 | new_pin!(sck, config.sck_af()), | 678 | new_pin!(sck, config.sck_af()), |
| 582 | None, | 679 | None, |
| 583 | new_pin!(miso, AfType::input(config.miso_pull)), | 680 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 681 | None, | ||
| 584 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 682 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 585 | new_dma!(tx_dma), | 683 | new_dma!(tx_dma), |
| 586 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 684 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| @@ -603,6 +701,7 @@ impl<'d> Spi<'d, Async> { | |||
| 603 | new_pin!(sck, config.sck_af()), | 701 | new_pin!(sck, config.sck_af()), |
| 604 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 702 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 605 | None, | 703 | None, |
| 704 | None, | ||
| 606 | new_dma!(tx_dma), | 705 | new_dma!(tx_dma), |
| 607 | None, | 706 | None, |
| 608 | config, | 707 | config, |
| @@ -623,6 +722,7 @@ impl<'d> Spi<'d, Async> { | |||
| 623 | None, | 722 | None, |
| 624 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 723 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 625 | None, | 724 | None, |
| 725 | None, | ||
| 626 | new_dma!(tx_dma), | 726 | new_dma!(tx_dma), |
| 627 | None, | 727 | None, |
| 628 | config, | 728 | config, |
| @@ -646,7 +746,7 @@ impl<'d> Spi<'d, Async> { | |||
| 646 | config.bit_order = BitOrder::MsbFirst; | 746 | config.bit_order = BitOrder::MsbFirst; |
| 647 | config.frequency = freq; | 747 | config.frequency = freq; |
| 648 | 748 | ||
| 649 | Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) | 749 | Self::new_inner(peri, None, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) |
| 650 | } | 750 | } |
| 651 | 751 | ||
| 652 | #[allow(dead_code)] | 752 | #[allow(dead_code)] |
| @@ -656,9 +756,11 @@ impl<'d> Spi<'d, Async> { | |||
| 656 | rx_dma: Option<ChannelAndRequest<'d>>, | 756 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 657 | config: Config, | 757 | config: Config, |
| 658 | ) -> Self { | 758 | ) -> Self { |
| 659 | Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) | 759 | Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config) |
| 660 | } | 760 | } |
| 761 | } | ||
| 661 | 762 | ||
| 763 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | ||
| 662 | /// SPI write, using DMA. | 764 | /// SPI write, using DMA. |
| 663 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 765 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
| 664 | if data.is_empty() { | 766 | if data.is_empty() { |
| @@ -888,11 +990,12 @@ impl<'d> Spi<'d, Async> { | |||
| 888 | } | 990 | } |
| 889 | } | 991 | } |
| 890 | 992 | ||
| 891 | impl<'d, M: PeriMode> Drop for Spi<'d, M> { | 993 | impl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> { |
| 892 | fn drop(&mut self) { | 994 | fn drop(&mut self) { |
| 893 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | 995 | self.sck.as_ref().map(|x| x.set_as_disconnected()); |
| 894 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); | 996 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); |
| 895 | self.miso.as_ref().map(|x| x.set_as_disconnected()); | 997 | self.miso.as_ref().map(|x| x.set_as_disconnected()); |
| 998 | self.nss.as_ref().map(|x| x.set_as_disconnected()); | ||
| 896 | 999 | ||
| 897 | self.info.rcc.disable(); | 1000 | self.info.rcc.disable(); |
| 898 | } | 1001 | } |
| @@ -1127,7 +1230,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> { | |||
| 1127 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 | 1230 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 |
| 1128 | macro_rules! impl_blocking { | 1231 | macro_rules! impl_blocking { |
| 1129 | ($w:ident) => { | 1232 | ($w:ident) => { |
| 1130 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { | 1233 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> { |
| 1131 | type Error = Error; | 1234 | type Error = Error; |
| 1132 | 1235 | ||
| 1133 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { | 1236 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { |
| @@ -1135,7 +1238,7 @@ macro_rules! impl_blocking { | |||
| 1135 | } | 1238 | } |
| 1136 | } | 1239 | } |
| 1137 | 1240 | ||
| 1138 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { | 1241 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> { |
| 1139 | type Error = Error; | 1242 | type Error = Error; |
| 1140 | 1243 | ||
| 1141 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { | 1244 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { |
| @@ -1149,11 +1252,11 @@ macro_rules! impl_blocking { | |||
| 1149 | impl_blocking!(u8); | 1252 | impl_blocking!(u8); |
| 1150 | impl_blocking!(u16); | 1253 | impl_blocking!(u16); |
| 1151 | 1254 | ||
| 1152 | impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { | 1255 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> { |
| 1153 | type Error = Error; | 1256 | type Error = Error; |
| 1154 | } | 1257 | } |
| 1155 | 1258 | ||
| 1156 | impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { | 1259 | impl<'d, W: Word, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M, CM> { |
| 1157 | fn flush(&mut self) -> Result<(), Self::Error> { | 1260 | fn flush(&mut self) -> Result<(), Self::Error> { |
| 1158 | Ok(()) | 1261 | Ok(()) |
| 1159 | } | 1262 | } |
| @@ -1186,7 +1289,7 @@ impl embedded_hal_1::spi::Error for Error { | |||
| 1186 | } | 1289 | } |
| 1187 | } | 1290 | } |
| 1188 | 1291 | ||
| 1189 | impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | 1292 | impl<'d, W: Word, CM: CommunicationMode> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async, CM> { |
| 1190 | async fn flush(&mut self) -> Result<(), Self::Error> { | 1293 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1191 | Ok(()) | 1294 | Ok(()) |
| 1192 | } | 1295 | } |
| @@ -1328,7 +1431,7 @@ foreach_peripheral!( | |||
| 1328 | }; | 1431 | }; |
| 1329 | ); | 1432 | ); |
| 1330 | 1433 | ||
| 1331 | impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { | 1434 | impl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> { |
| 1332 | type Config = Config; | 1435 | type Config = Config; |
| 1333 | type ConfigError = (); | 1436 | type ConfigError = (); |
| 1334 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1437 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 74b10a183..7db51d72e 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #![allow(non_snake_case)] | 1 | #![allow(non_snake_case)] |
| 2 | 2 | ||
| 3 | use core::cell::{Cell, RefCell}; | 3 | use core::cell::{Cell, RefCell}; |
| 4 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 5 | use core::sync::atomic::AtomicU16; | ||
| 4 | use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; | 6 | use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; |
| 5 | 7 | ||
| 6 | use critical_section::CriticalSection; | 8 | use critical_section::CriticalSection; |
| @@ -213,7 +215,13 @@ pub(crate) struct RtcDriver { | |||
| 213 | period: AtomicU32, | 215 | period: AtomicU32, |
| 214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, | 216 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 215 | #[cfg(feature = "low-power")] | 217 | #[cfg(feature = "low-power")] |
| 216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, | 218 | pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, |
| 219 | #[cfg(feature = "low-power")] | ||
| 220 | /// The minimum pause time beyond which the executor will enter a low-power state. | ||
| 221 | min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>, | ||
| 222 | /// Saved count for the timer (its value is lost when entering STOP2) | ||
| 223 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 224 | saved_count: AtomicU16, | ||
| 217 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, | 225 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, |
| 218 | } | 226 | } |
| 219 | 227 | ||
| @@ -221,12 +229,18 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | |||
| 221 | period: AtomicU32::new(0), | 229 | period: AtomicU32::new(0), |
| 222 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), | 230 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 223 | #[cfg(feature = "low-power")] | 231 | #[cfg(feature = "low-power")] |
| 224 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | 232 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)), |
| 233 | #[cfg(feature = "low-power")] | ||
| 234 | min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))), | ||
| 235 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 236 | saved_count: AtomicU16::new(0), | ||
| 225 | queue: Mutex::new(RefCell::new(Queue::new())) | 237 | queue: Mutex::new(RefCell::new(Queue::new())) |
| 226 | }); | 238 | }); |
| 227 | 239 | ||
| 228 | impl RtcDriver { | 240 | impl RtcDriver { |
| 229 | fn init(&'static self, cs: critical_section::CriticalSection) { | 241 | /// initialize the timer, but don't start it. Used for chips like stm32wle5 |
| 242 | /// for low power where the timer config is lost in STOP2. | ||
| 243 | fn init_timer(&'static self, cs: critical_section::CriticalSection) { | ||
| 230 | let r = regs_gp16(); | 244 | let r = regs_gp16(); |
| 231 | 245 | ||
| 232 | rcc::enable_and_reset_with_cs::<T>(cs); | 246 | rcc::enable_and_reset_with_cs::<T>(cs); |
| @@ -259,10 +273,16 @@ impl RtcDriver { | |||
| 259 | w.set_ccie(0, true); | 273 | w.set_ccie(0, true); |
| 260 | }); | 274 | }); |
| 261 | 275 | ||
| 276 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 277 | r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); | ||
| 278 | |||
| 262 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); | 279 | <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); |
| 263 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; | 280 | unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; |
| 281 | } | ||
| 264 | 282 | ||
| 265 | r.cr1().modify(|w| w.set_cen(true)); | 283 | fn init(&'static self, cs: CriticalSection) { |
| 284 | self.init_timer(cs); | ||
| 285 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | ||
| 266 | } | 286 | } |
| 267 | 287 | ||
| 268 | fn on_interrupt(&self) { | 288 | fn on_interrupt(&self) { |
| @@ -379,27 +399,26 @@ impl RtcDriver { | |||
| 379 | #[cfg(feature = "low-power")] | 399 | #[cfg(feature = "low-power")] |
| 380 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 400 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 381 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { | 401 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 382 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { | 402 | if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) { |
| 383 | self.add_time(offset, cs); | 403 | self.add_time(offset, cs); |
| 384 | } | 404 | } |
| 385 | } | 405 | } |
| 386 | 406 | ||
| 387 | /* | 407 | /* |
| 388 | Low-power public functions: all create a critical section | 408 | Low-power public functions: all create or require a critical section |
| 389 | */ | 409 | */ |
| 390 | #[cfg(feature = "low-power")] | 410 | #[cfg(feature = "low-power")] |
| 391 | /// Set the rtc but panic if it's already been set | 411 | pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { |
| 392 | pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { | 412 | self.min_stop_pause.borrow(cs).replace(min_stop_pause); |
| 393 | critical_section::with(|cs| { | ||
| 394 | rtc.stop_wakeup_alarm(cs); | ||
| 395 | |||
| 396 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()) | ||
| 397 | }); | ||
| 398 | } | 413 | } |
| 399 | 414 | ||
| 400 | #[cfg(feature = "low-power")] | 415 | #[cfg(feature = "low-power")] |
| 401 | /// The minimum pause time beyond which the executor will enter a low-power state. | 416 | /// Set the rtc but panic if it's already been set |
| 402 | pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); | 417 | pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) { |
| 418 | rtc.stop_wakeup_alarm(cs); | ||
| 419 | |||
| 420 | assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); | ||
| 421 | } | ||
| 403 | 422 | ||
| 404 | #[cfg(feature = "low-power")] | 423 | #[cfg(feature = "low-power")] |
| 405 | /// Pause the timer if ready; return err if not | 424 | /// Pause the timer if ready; return err if not |
| @@ -413,17 +432,25 @@ impl RtcDriver { | |||
| 413 | self.stop_wakeup_alarm(cs); | 432 | self.stop_wakeup_alarm(cs); |
| 414 | 433 | ||
| 415 | let time_until_next_alarm = self.time_until_next_alarm(cs); | 434 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
| 416 | if time_until_next_alarm < Self::MIN_STOP_PAUSE { | 435 | if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { |
| 436 | trace!( | ||
| 437 | "time_until_next_alarm < self.min_stop_pause ({})", | ||
| 438 | time_until_next_alarm | ||
| 439 | ); | ||
| 417 | Err(()) | 440 | Err(()) |
| 418 | } else { | 441 | } else { |
| 419 | self.rtc | 442 | self.rtc |
| 420 | .borrow(cs) | 443 | .borrow(cs) |
| 421 | .get() | 444 | .borrow_mut() |
| 445 | .as_mut() | ||
| 422 | .unwrap() | 446 | .unwrap() |
| 423 | .start_wakeup_alarm(time_until_next_alarm, cs); | 447 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 424 | 448 | ||
| 425 | regs_gp16().cr1().modify(|w| w.set_cen(false)); | 449 | regs_gp16().cr1().modify(|w| w.set_cen(false)); |
| 426 | 450 | // save the count for the timer as its lost in STOP2 for stm32wlex | |
| 451 | #[cfg(stm32wlex)] | ||
| 452 | self.saved_count | ||
| 453 | .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); | ||
| 427 | Ok(()) | 454 | Ok(()) |
| 428 | } | 455 | } |
| 429 | }) | 456 | }) |
| @@ -431,18 +458,16 @@ impl RtcDriver { | |||
| 431 | 458 | ||
| 432 | #[cfg(feature = "low-power")] | 459 | #[cfg(feature = "low-power")] |
| 433 | /// Resume the timer with the given offset | 460 | /// Resume the timer with the given offset |
| 434 | pub(crate) fn resume_time(&self) { | 461 | pub(crate) fn resume_time(&self, cs: CriticalSection) { |
| 435 | if regs_gp16().cr1().read().cen() { | 462 | if regs_gp16().cr1().read().cen() { |
| 436 | // Time isn't currently stopped | 463 | // Time isn't currently stopped |
| 437 | 464 | ||
| 438 | return; | 465 | return; |
| 439 | } | 466 | } |
| 440 | 467 | ||
| 441 | critical_section::with(|cs| { | 468 | self.stop_wakeup_alarm(cs); |
| 442 | self.stop_wakeup_alarm(cs); | ||
| 443 | 469 | ||
| 444 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 470 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 445 | }) | ||
| 446 | } | 471 | } |
| 447 | 472 | ||
| 448 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { | 473 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| @@ -514,10 +539,15 @@ impl Driver for RtcDriver { | |||
| 514 | } | 539 | } |
| 515 | 540 | ||
| 516 | #[cfg(feature = "low-power")] | 541 | #[cfg(feature = "low-power")] |
| 517 | pub(crate) fn get_driver() -> &'static RtcDriver { | 542 | pub(crate) const fn get_driver() -> &'static RtcDriver { |
| 518 | &DRIVER | 543 | &DRIVER |
| 519 | } | 544 | } |
| 520 | 545 | ||
| 521 | pub(crate) fn init(cs: CriticalSection) { | 546 | pub(crate) fn init(cs: CriticalSection) { |
| 522 | DRIVER.init(cs) | 547 | DRIVER.init(cs) |
| 523 | } | 548 | } |
| 549 | |||
| 550 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 551 | pub(crate) fn init_timer(cs: CriticalSection) { | ||
| 552 | DRIVER.init_timer(cs) | ||
| 553 | } | ||
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 75a83629c..9a56a41fb 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr}; | 5 | pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr}; |
| 6 | 6 | ||
| 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| 8 | use super::simple_pwm::PwmPin; | 8 | use super::simple_pwm::PwmPin; |
| @@ -136,6 +136,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 136 | self.inner.get_moe() | 136 | self.inner.get_moe() |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | /// Set Master Slave Mode 2 | ||
| 140 | pub fn set_mms2(&mut self, mms2: Mms2) { | ||
| 141 | self.inner.set_mms2_selection(mms2); | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Set Repetition Counter | ||
| 145 | pub fn set_repetition_counter(&mut self, val: u16) { | ||
| 146 | self.inner.set_repetition_counter(val); | ||
| 147 | } | ||
| 148 | |||
| 139 | /// Enable the given channel. | 149 | /// Enable the given channel. |
| 140 | pub fn enable(&mut self, channel: Channel) { | 150 | pub fn enable(&mut self, channel: Channel) { |
| 141 | self.inner.enable_channel(channel, true); | 151 | self.inner.enable_channel(channel, true); |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index ac039bb0d..0122fe4f7 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -10,7 +10,7 @@ use core::mem::ManuallyDrop; | |||
| 10 | 10 | ||
| 11 | use embassy_hal_internal::Peri; | 11 | use embassy_hal_internal::Peri; |
| 12 | // Re-export useful enums | 12 | // Re-export useful enums |
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::pac::timer::vals; | 16 | use crate::pac::timer::vals; |
| @@ -143,20 +143,69 @@ pub enum OutputCompareMode { | |||
| 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as | 143 | /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as |
| 144 | /// TIMx_CNT>TIMx_CCRx else inactive. | 144 | /// TIMx_CNT>TIMx_CCRx else inactive. |
| 145 | PwmMode2, | 145 | PwmMode2, |
| 146 | // TODO: there's more modes here depending on the chip family. | 146 | |
| 147 | #[cfg(timer_v2)] | ||
| 148 | /// In up-counting mode, the channel is active until a trigger | ||
| 149 | /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM | ||
| 150 | /// mode 1 and the channels becomes active again at the next update. In down-counting | ||
| 151 | /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal). | ||
| 152 | /// Then, a comparison is performed as in PWM mode 1 and the channels becomes | ||
| 153 | /// inactive again at the next update. | ||
| 154 | OnePulseMode1, | ||
| 155 | |||
| 156 | #[cfg(timer_v2)] | ||
| 157 | /// In up-counting mode, the channel is inactive until a | ||
| 158 | /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in | ||
| 159 | /// PWM mode 2 and the channels becomes inactive again at the next update. In down | ||
| 160 | /// counting mode, the channel is active until a trigger event is detected (on tim_trgi | ||
| 161 | /// signal). Then, a comparison is performed as in PWM mode 1 and the channels | ||
| 162 | /// becomes active again at the next update. | ||
| 163 | OnePulseMode2, | ||
| 164 | |||
| 165 | #[cfg(timer_v2)] | ||
| 166 | /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1. | ||
| 167 | /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref. | ||
| 168 | CombinedPwmMode1, | ||
| 169 | |||
| 170 | #[cfg(timer_v2)] | ||
| 171 | /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2. | ||
| 172 | /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref. | ||
| 173 | CombinedPwmMode2, | ||
| 174 | |||
| 175 | #[cfg(timer_v2)] | ||
| 176 | /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref | ||
| 177 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 178 | AsymmetricPwmMode1, | ||
| 179 | |||
| 180 | #[cfg(timer_v2)] | ||
| 181 | /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref | ||
| 182 | /// when the counter is counting up, tim_oc2ref when it is counting down. | ||
| 183 | AsymmetricPwmMode2, | ||
| 147 | } | 184 | } |
| 148 | 185 | ||
| 149 | impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | 186 | impl From<OutputCompareMode> for crate::pac::timer::vals::Ocm { |
| 150 | fn from(mode: OutputCompareMode) -> Self { | 187 | fn from(mode: OutputCompareMode) -> Self { |
| 151 | match mode { | 188 | match mode { |
| 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, | 189 | OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN, |
| 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, | 190 | OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, | 191 | OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, | 192 | OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE, |
| 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, | 193 | OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE, |
| 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, | 194 | OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE, |
| 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, | 195 | OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1, |
| 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, | 196 | OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2, |
| 197 | #[cfg(timer_v2)] | ||
| 198 | OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1, | ||
| 199 | #[cfg(timer_v2)] | ||
| 200 | OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2, | ||
| 201 | #[cfg(timer_v2)] | ||
| 202 | OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1, | ||
| 203 | #[cfg(timer_v2)] | ||
| 204 | OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2, | ||
| 205 | #[cfg(timer_v2)] | ||
| 206 | OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1, | ||
| 207 | #[cfg(timer_v2)] | ||
| 208 | OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2, | ||
| 160 | } | 209 | } |
| 161 | } | 210 | } |
| 162 | } | 211 | } |
| @@ -640,6 +689,11 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { | |||
| 640 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) | 689 | self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) |
| 641 | } | 690 | } |
| 642 | 691 | ||
| 692 | /// Set Timer Master Mode | ||
| 693 | pub fn set_master_mode(&self, mms: MasterMode) { | ||
| 694 | self.regs_gp16().cr2().modify(|w| w.set_mms(mms)); | ||
| 695 | } | ||
| 696 | |||
| 643 | /// Set Timer Slave Mode | 697 | /// Set Timer Slave Mode |
| 644 | pub fn set_slave_mode(&self, sms: SlaveMode) { | 698 | pub fn set_slave_mode(&self, sms: SlaveMode) { |
| 645 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); | 699 | self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); |
| @@ -760,6 +814,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { | |||
| 760 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); | 814 | self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); |
| 761 | } | 815 | } |
| 762 | 816 | ||
| 817 | /// Set master mode selection 2 | ||
| 818 | pub fn set_mms2_selection(&self, mms2: vals::Mms2) { | ||
| 819 | self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2)); | ||
| 820 | } | ||
| 821 | |||
| 822 | /// Set repetition counter | ||
| 823 | pub fn set_repetition_counter(&self, val: u16) { | ||
| 824 | self.regs_advanced().rcr().modify(|w| w.set_rep(val)); | ||
| 825 | } | ||
| 826 | |||
| 763 | /// Trigger software break 1 or 2 | 827 | /// Trigger software break 1 or 2 |
| 764 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. | 828 | /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. |
| 765 | pub fn trigger_software_break(&self, n: usize) { | 829 | pub fn trigger_software_break(&self, n: usize) { |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b09bc7166..804d1ef37 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -399,7 +399,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> { | |||
| 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { | 399 | impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { |
| 400 | unsafe fn on_interrupt() { | 400 | unsafe fn on_interrupt() { |
| 401 | #[cfg(feature = "low-power")] | 401 | #[cfg(feature = "low-power")] |
| 402 | crate::low_power::on_wakeup_irq(); | 402 | crate::low_power::Executor::on_wakeup_irq(); |
| 403 | 403 | ||
| 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); | 404 | let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); |
| 405 | 405 | ||
| @@ -429,7 +429,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare | |||
| 429 | { | 429 | { |
| 430 | unsafe fn on_interrupt() { | 430 | unsafe fn on_interrupt() { |
| 431 | #[cfg(feature = "low-power")] | 431 | #[cfg(feature = "low-power")] |
| 432 | crate::low_power::on_wakeup_irq(); | 432 | crate::low_power::Executor::on_wakeup_irq(); |
| 433 | 433 | ||
| 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); | 434 | let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); |
| 435 | 435 | ||
diff --git a/embassy-stm32/src/uid.rs b/embassy-stm32/src/uid.rs index 5e38532bd..2d3e2b972 100644 --- a/embassy-stm32/src/uid.rs +++ b/embassy-stm32/src/uid.rs | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | //! Unique ID (UID) | 1 | //! Unique ID (UID) |
| 2 | 2 | ||
| 3 | /// Get this device's unique 96-bit ID. | 3 | /// Get this device's unique 96-bit ID. |
| 4 | pub fn uid() -> &'static [u8; 12] { | 4 | pub fn uid() -> [u8; 12] { |
| 5 | unsafe { &*crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } | 5 | unsafe { *crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. | 8 | /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 20bfefd9e..bac570d27 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -7,7 +7,9 @@ use embassy_embedded_hal::SetConfig; | |||
| 7 | use embedded_io_async::ReadReady; | 7 | use embedded_io_async::ReadReady; |
| 8 | use futures_util::future::{Either, select}; | 8 | use futures_util::future::{Either, select}; |
| 9 | 9 | ||
| 10 | use super::{Config, ConfigError, Error, Info, State, UartRx, rdr, reconfigure, set_baudrate, sr}; | 10 | use super::{ |
| 11 | Config, ConfigError, Error, Info, State, UartRx, clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, | ||
| 12 | }; | ||
| 11 | use crate::Peri; | 13 | use crate::Peri; |
| 12 | use crate::dma::ReadableRingBuffer; | 14 | use crate::dma::ReadableRingBuffer; |
| 13 | use crate::gpio::{AnyPin, SealedPin as _}; | 15 | use crate::gpio::{AnyPin, SealedPin as _}; |
| @@ -338,26 +340,16 @@ impl Drop for RingBufferedUartRx<'_> { | |||
| 338 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | 340 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags |
| 339 | /// are cleared by a single read to the RDR register. | 341 | /// are cleared by a single read to the RDR register. |
| 340 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | 342 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { |
| 341 | // Critical section is required so that the flags aren't set after read and before clear | 343 | // SAFETY: read only and we only use Rx related flags |
| 342 | let sr = critical_section::with(|_| { | 344 | let sr = sr(r).read(); |
| 343 | // SAFETY: read only and we only use Rx related flags | 345 | |
| 344 | let sr = sr(r).read(); | 346 | #[cfg(not(any(usart_v3, usart_v4)))] |
| 345 | 347 | unsafe { | |
| 346 | #[cfg(any(usart_v3, usart_v4))] | 348 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) |
| 347 | r.icr().write(|w| { | 349 | rdr(r).read_volatile() |
| 348 | w.set_idle(true); | 350 | }; |
| 349 | w.set_pe(true); | 351 | clear_interrupt_flags(r, sr); |
| 350 | w.set_fe(true); | 352 | |
| 351 | w.set_ne(true); | ||
| 352 | w.set_ore(true); | ||
| 353 | }); | ||
| 354 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 355 | unsafe { | ||
| 356 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) | ||
| 357 | rdr(r).read_volatile() | ||
| 358 | }; | ||
| 359 | sr | ||
| 360 | }); | ||
| 361 | if sr.pe() { | 353 | if sr.pe() { |
| 362 | Err(Error::Parity) | 354 | Err(Error::Parity) |
| 363 | } else if sr.fe() { | 355 | } else if sr.fe() { |
diff --git a/embassy-stm32/src/vrefbuf/mod.rs b/embassy-stm32/src/vrefbuf/mod.rs index b061306a0..43dd9c800 100644 --- a/embassy-stm32/src/vrefbuf/mod.rs +++ b/embassy-stm32/src/vrefbuf/mod.rs | |||
| @@ -14,10 +14,10 @@ pub struct VoltageReferenceBuffer<'d, T: Instance> { | |||
| 14 | #[cfg(rcc_wba)] | 14 | #[cfg(rcc_wba)] |
| 15 | fn get_refbuf_trim(voltage_scale: Vrs) -> usize { | 15 | fn get_refbuf_trim(voltage_scale: Vrs) -> usize { |
| 16 | match voltage_scale { | 16 | match voltage_scale { |
| 17 | Vrs::VREF0 => 0x0BFA_07A8usize, | 17 | Vrs::VREF0 => 0x0BFA_07ABusize, |
| 18 | Vrs::VREF1 => 0x0BFA_07A9usize, | 18 | Vrs::VREF1 => 0x0BFA_07AAusize, |
| 19 | Vrs::VREF2 => 0x0BFA_07AAusize, | 19 | Vrs::VREF2 => 0x0BFA_07A9usize, |
| 20 | Vrs::VREF3 => 0x0BFA_07ABusize, | 20 | Vrs::VREF3 => 0x0BFA_07A8usize, |
| 21 | _ => panic!("Incorrect Vrs setting!"), | 21 | _ => panic!("Incorrect Vrs setting!"), |
| 22 | } | 22 | } |
| 23 | } | 23 | } |
