diff --git a/README.md b/README.md index 24347a43f..b6f667f75 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries. -## Documentation - API reference - Website - Chat +## Documentation - API reference - Website - Chat ## Rust + async ❤️ embedded The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. diff --git a/ci.sh b/ci.sh index df9e09848..58a288441 100755 --- a/ci.sh +++ b/ci.sh @@ -23,6 +23,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers,rtos-trace \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ @@ -194,7 +196,8 @@ cargo batch \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ - --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf \ + --- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf --features embassy-stm32/stm32wb55rg \ + --- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabihf --features embassy-stm32/stm32h747xi-cm7 \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \ diff --git a/docs/modules/ROOT/pages/best_practices.adoc b/docs/modules/ROOT/pages/best_practices.adoc index 1e02f9ba9..bfcedec06 100644 --- a/docs/modules/ROOT/pages/best_practices.adoc +++ b/docs/modules/ROOT/pages/best_practices.adoc @@ -3,8 +3,10 @@ Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework. == Passing Buffers by Reference -It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack. -This, however, can easily blow up your stack if you are not careful. +It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], +to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't +want to spend resources on an allocator and end up placing buffers on the stack. This, however, can easily blow up +your stack if you are not careful. Consider the following example: [,rust] diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index 05ff7c598..7fb81e2ca 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -29,11 +29,10 @@ If you see an error like this: You are likely missing some features of the `embassy-executor` crate. -For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate: +For Cortex-M targets, check whether ALL of the following features are enabled in your `Cargo.toml` for the `embassy-executor` crate: * `arch-cortex-m` * `executor-thread` -* `nightly` For ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate]. @@ -125,15 +124,18 @@ You have multiple versions of the same crate in your dependency tree. This means embassy crates are coming from crates.io, and some from git, each of them pulling in a different set of dependencies. -To resolve this issue, make sure to only use a single source for all your embassy crates! To do this, -you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`. +To resolve this issue, make sure to only use a single source for all your embassy crates! +To do this, you should patch your dependencies to use git sources using `[patch.crates.io]` +and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`. Example: [source,toml] ---- [patch.crates-io] -embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } +embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } +embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } +# embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" } ---- Note that the git revision should match any other embassy patches or git dependencies that you are using! diff --git a/docs/modules/ROOT/pages/new_project.adoc b/docs/modules/ROOT/pages/new_project.adoc index ce139ed8d..320966bb6 100644 --- a/docs/modules/ROOT/pages/new_project.adoc +++ b/docs/modules/ROOT/pages/new_project.adoc @@ -1,6 +1,17 @@ = Starting a new Embassy project -Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. The easiest way to do this is to adapt an example for a similar chip to the one you’re targeting. +Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. + +There are some tools for generating Embassy projects: (WIP) + +==== CLI +- link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF) + +==== cargo-generate +- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP) +- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP) + +But if you want to start from scratch: As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes. @@ -166,13 +177,13 @@ should result in a blinking LED (if there’s one attached to the pin in `src/ma Erasing sectors ✔ [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s ) Programming pages ✔ [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s ) Finished in 0.817s 0.000000 TRACE BDCR configured: 00008200 -└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117 +└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117 0.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) } -└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130 +└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130 0.000000 INFO Hello World! -└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14 +└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14 0.000091 INFO high -└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19 +└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19 0.300201 INFO low -└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23 ----- \ No newline at end of file +└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23 +---- diff --git a/docs/modules/ROOT/pages/project_structure.adoc b/docs/modules/ROOT/pages/project_structure.adoc index 61ffd05a6..2adfcc1df 100644 --- a/docs/modules/ROOT/pages/project_structure.adoc +++ b/docs/modules/ROOT/pages/project_structure.adoc @@ -38,13 +38,18 @@ DEFMT_LOG = "trace" # <- can change to info, warn, or error == build.rs -This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example]. +This is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example]. == Cargo.toml This is your manifest file, where you can configure all of the embassy components to use the features you need. -TODO: someone should exhaustively describe every feature for every component! +==== Features +===== Time +- tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes. +- defmt-timestamp-uptime: defmt log entries will display the uptime in seconds. + +...more to come == memory.x diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index e568001bc..ca1a1b10c 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs @@ -49,16 +49,51 @@ pub struct BootLoaderConfig { pub state: STATE, } -impl<'a, FLASH: NorFlash> +impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoaderConfig< - BlockingPartition<'a, NoopRawMutex, FLASH>, - BlockingPartition<'a, NoopRawMutex, FLASH>, - BlockingPartition<'a, NoopRawMutex, FLASH>, + BlockingPartition<'a, NoopRawMutex, ACTIVE>, + BlockingPartition<'a, NoopRawMutex, DFU>, + BlockingPartition<'a, NoopRawMutex, STATE>, > { - /// Create a bootloader config from the flash and address symbols defined in the linkerfile + /// Constructs a `BootLoaderConfig` instance from flash memory and address symbols defined in the linker file. + /// + /// This method initializes `BlockingPartition` instances for the active, DFU (Device Firmware Update), + /// and state partitions, leveraging start and end addresses specified by the linker. These partitions + /// are critical for managing firmware updates, application state, and boot operations within the bootloader. + /// + /// # Parameters + /// - `active_flash`: A reference to a mutex-protected `RefCell` for the active partition's flash interface. + /// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface. + /// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface. + /// + /// # Safety + /// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses + /// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined + /// in the memory.x file to prevent undefined behavior. + /// + /// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory + /// interfaces provided are compatible with these regions. + /// + /// # Returns + /// A `BootLoaderConfig` instance with `BlockingPartition` instances for the active, DFU, and state partitions. + /// + /// # Example + /// ```ignore + /// // Assume `active_flash`, `dfu_flash`, and `state_flash` all share the same flash memory interface. + /// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); + /// let flash = Mutex::new(RefCell::new(layout.bank1_region)); + /// + /// let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash); + /// // `config` can now be used to create a `BootLoader` instance for managing boot operations. + /// ``` + /// Working examples can be found in the bootloader examples folder. // #[cfg(target_os = "none")] - pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> Self { + pub fn from_linkerfile_blocking( + active_flash: &'a Mutex>, + dfu_flash: &'a Mutex>, + state_flash: &'a Mutex>, + ) -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; @@ -73,21 +108,21 @@ impl<'a, FLASH: NorFlash> let end = &__bootloader_active_end as *const u32 as u32; trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end); - BlockingPartition::new(flash, start, end - start) + BlockingPartition::new(active_flash, start, end - start) }; let dfu = unsafe { let start = &__bootloader_dfu_start as *const u32 as u32; let end = &__bootloader_dfu_end as *const u32 as u32; trace!("DFU: 0x{:x} - 0x{:x}", start, end); - BlockingPartition::new(flash, start, end - start) + BlockingPartition::new(dfu_flash, start, end - start) }; let state = unsafe { let start = &__bootloader_state_start as *const u32 as u32; let end = &__bootloader_state_end as *const u32 as u32; trace!("STATE: 0x{:x} - 0x{:x}", start, end); - BlockingPartition::new(flash, start, end - start) + BlockingPartition::new(state_flash, start, end - start) }; Self { active, dfu, state } diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 2e43e1cc1..668f16f16 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -16,11 +16,14 @@ pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { } #[cfg(target_os = "none")] -impl<'a, FLASH: NorFlash> - FirmwareUpdaterConfig, Partition<'a, NoopRawMutex, FLASH>> +impl<'a, DFU: NorFlash, STATE: NorFlash> + FirmwareUpdaterConfig, Partition<'a, NoopRawMutex, STATE>> { /// Create a firmware updater config from the flash and address symbols defined in the linkerfile - pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex) -> Self { + pub fn from_linkerfile( + dfu_flash: &'a embassy_sync::mutex::Mutex, + state_flash: &'a embassy_sync::mutex::Mutex, + ) -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; @@ -33,14 +36,14 @@ impl<'a, FLASH: NorFlash> let end = &__bootloader_dfu_end as *const u32 as u32; trace!("DFU: 0x{:x} - 0x{:x}", start, end); - Partition::new(flash, start, end - start) + Partition::new(dfu_flash, start, end - start) }; let state = unsafe { let start = &__bootloader_state_start as *const u32 as u32; let end = &__bootloader_state_end as *const u32 as u32; trace!("STATE: 0x{:x} - 0x{:x}", start, end); - Partition::new(flash, start, end - start) + Partition::new(state_flash, start, end - start) }; Self { dfu, state } diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 514070639..3e83366af 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -16,12 +16,43 @@ pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { } #[cfg(target_os = "none")] -impl<'a, FLASH: NorFlash> - FirmwareUpdaterConfig, BlockingPartition<'a, NoopRawMutex, FLASH>> +impl<'a, DFU: NorFlash, STATE: NorFlash> + FirmwareUpdaterConfig, BlockingPartition<'a, NoopRawMutex, STATE>> { - /// Create a firmware updater config from the flash and address symbols defined in the linkerfile + /// Constructs a `FirmwareUpdaterConfig` instance from flash memory and address symbols defined in the linker file. + /// + /// This method initializes `BlockingPartition` instances for the DFU (Device Firmware Update), and state + /// partitions, leveraging start and end addresses specified by the linker. These partitions are critical + /// for managing firmware updates, application state, and boot operations within the bootloader. + /// + /// # Parameters + /// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface. + /// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface. + /// + /// # Safety + /// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses + /// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined + /// in the memory.x file to prevent undefined behavior. + /// + /// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory + /// interfaces provided are compatible with these regions. + /// + /// # Returns + /// A `FirmwareUpdaterConfig` instance with `BlockingPartition` instances for the DFU, and state partitions. + /// + /// # Example + /// ```ignore + /// // Assume `dfu_flash`, and `state_flash` share the same flash memory interface. + /// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); + /// let flash = Mutex::new(RefCell::new(layout.bank1_region)); + /// + /// let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash); + /// // `config` can now be used to create a `FirmwareUpdater` instance for managing boot operations. + /// ``` + /// Working examples can be found in the bootloader examples folder. pub fn from_linkerfile_blocking( - flash: &'a embassy_sync::blocking_mutex::Mutex>, + dfu_flash: &'a embassy_sync::blocking_mutex::Mutex>, + state_flash: &'a embassy_sync::blocking_mutex::Mutex>, ) -> Self { extern "C" { static __bootloader_state_start: u32; @@ -35,14 +66,14 @@ impl<'a, FLASH: NorFlash> let end = &__bootloader_dfu_end as *const u32 as u32; trace!("DFU: 0x{:x} - 0x{:x}", start, end); - BlockingPartition::new(flash, start, end - start) + BlockingPartition::new(dfu_flash, start, end - start) }; let state = unsafe { let start = &__bootloader_state_start as *const u32 as u32; let end = &__bootloader_state_end as *const u32 as u32; trace!("STATE: 0x{:x} - 0x{:x}", start, end); - BlockingPartition::new(flash, start, end - start) + BlockingPartition::new(state_flash, start, end - start) }; Self { dfu, state } diff --git a/embassy-boot/src/firmware_updater/mod.rs b/embassy-boot/src/firmware_updater/mod.rs index 4814786bf..4c4f4f10b 100644 --- a/embassy-boot/src/firmware_updater/mod.rs +++ b/embassy-boot/src/firmware_updater/mod.rs @@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; /// Firmware updater flash configuration holding the two flashes used by the updater /// /// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. -/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition +/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile_blocking`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition /// the provided flash according to symbols defined in the linkerfile. pub struct FirmwareUpdaterConfig { /// The dfu flash partition diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 3f00be4a8..3d5e3ab9f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -581,6 +581,15 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue { #[cfg(feature = "integrated-timers")] embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); +#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))] +const fn gcd(a: u64, b: u64) -> u64 { + if b == 0 { + a + } else { + gcd(b, a % b) + } +} + #[cfg(feature = "rtos-trace")] impl rtos_trace::RtosTraceOSCallbacks for Executor { fn task_list() { @@ -588,7 +597,8 @@ impl rtos_trace::RtosTraceOSCallbacks for Executor { } #[cfg(feature = "integrated-timers")] fn time() -> u64 { - Instant::now().as_micros() + const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000); + embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M) } #[cfg(not(feature = "integrated-timers"))] fn time() -> u64 { diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 6b6f79188..8937159df 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -13,7 +13,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO pub use pac::spim0::config::ORDER_A as BitOrder; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; -use crate::chip::FORCE_COPY_BUFFER_SIZE; +use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::interrupt::typelevel::Interrupt; @@ -25,9 +25,9 @@ use crate::{interrupt, pac, Peripheral}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { - /// TX buffer was too long. + /// Supplied TX buffer overflows EasyDMA transmit buffer TxBufferTooLong, - /// RX buffer was too long. + /// Supplied RX buffer overflows EasyDMA receive buffer RxBufferTooLong, /// EasyDMA can only read from data memory, read only buffers in flash will fail. BufferNotInRAM, @@ -220,11 +220,19 @@ impl<'d, T: Instance> Spim<'d, T> { // Set up the DMA write. let (ptr, tx_len) = slice_ptr_parts(tx); + if tx_len > EASY_DMA_SIZE { + return Err(Error::TxBufferTooLong); + } + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); // Set up the DMA read. let (ptr, rx_len) = slice_ptr_parts_mut(rx); + if rx_len > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 60f4c9865..772ca40cc 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -11,7 +11,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use pac::spis0::config::ORDER_A as BitOrder; -use crate::chip::FORCE_COPY_BUFFER_SIZE; +use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; @@ -227,11 +227,17 @@ impl<'d, T: Instance> Spis<'d, T> { // Set up the DMA write. let (ptr, len) = slice_ptr_parts(tx); + if len > EASY_DMA_SIZE { + return Err(Error::TxBufferTooLong); + } r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); // Set up the DMA read. let (ptr, len) = slice_ptr_parts_mut(rx); + if len > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8f0fc1c59..3f5f12f06 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e702b4d564bc9e3c8a5c0141a11efdc5f7ee8f24" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e702b4d564bc9e3c8a5c0141a11efdc5f7ee8f24", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index d21c3053f..51b4b5fcc 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -8,6 +8,7 @@ #[cfg_attr(adc_f3, path = "f3.rs")] #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] +#[cfg_attr(adc_l0, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] #[cfg_attr(adc_v4, path = "v4.rs")] @@ -36,15 +37,15 @@ pub struct Adc<'d, T: Instance> { } pub(crate) mod sealed { - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] use embassy_sync::waitqueue::AtomicWaker; - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] pub struct State { pub waker: AtomicWaker, } - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] impl State { pub const fn new() -> Self { Self { @@ -59,14 +60,14 @@ pub(crate) mod sealed { pub trait Instance: InterruptableInstance { fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] fn state() -> &'static State; } pub trait AdcPin { - #[cfg(any(adc_v1, adc_v2))] + #[cfg(any(adc_v1, adc_l0, adc_v2))] fn set_as_analog(&mut self) {} fn channel(&self) -> u8; @@ -78,10 +79,10 @@ pub(crate) mod sealed { } /// ADC instance. -#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))] +#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))] pub trait Instance: sealed::Instance + crate::Peripheral

{} /// ADC instance. -#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))] +#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))] pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} /// ADC pin. @@ -96,12 +97,12 @@ foreach_adc!( crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { return crate::pac::$common_inst } - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] fn state() -> &'static sealed::State { static STATE: sealed::State = sealed::State::new(); &STATE @@ -125,7 +126,7 @@ macro_rules! impl_adc_pin { impl crate::adc::AdcPin for crate::peripherals::$pin {} impl crate::adc::sealed::AdcPin for crate::peripherals::$pin { - #[cfg(any(adc_v1, adc_v2))] + #[cfg(any(adc_v1, adc_l0, adc_v2))] fn set_as_analog(&mut self) { ::set_as_analog(self); } diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 9513e1df7..0e6c45c65 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,6 +1,6 @@ /// ADC resolution #[allow(missing_docs)] -#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Resolution { @@ -25,7 +25,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))] { Self::TwelveBit } @@ -46,7 +46,7 @@ impl From for crate::pac::adc::vals::Res { Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -65,7 +65,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 5a06f1a5a..f4b22b462 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -83,7 +83,7 @@ impl_sample_time!( ) ); -#[cfg(adc_g0)] +#[cfg(any(adc_l0, adc_g0))] impl_sample_time!( "1.5", Cycles1_5, diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 852b027df..37115dfab 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -4,6 +4,8 @@ use core::task::Poll; use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; +#[cfg(adc_l0)] +use stm32_metapac::adc::vals::Ckmode; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::interrupt::typelevel::Interrupt; @@ -30,8 +32,13 @@ impl interrupt::typelevel::Handler for InterruptHandl } } +#[cfg(not(adc_l0))] pub struct Vbat; + +#[cfg(not(adc_l0))] impl AdcPin for Vbat {} + +#[cfg(not(adc_l0))] impl super::sealed::AdcPin for Vbat { fn channel(&self) -> u8 { 18 @@ -69,9 +76,18 @@ impl<'d, T: Instance> Adc<'d, T> { // tstab = 14 * 1/fadc delay.delay_us(1); + // set default PCKL/2 on L0s because HSI is disabled in the default clock config + #[cfg(adc_l0)] + T::regs().cfgr2().modify(|reg| reg.set_ckmode(Ckmode::PCLK_DIV2)); + // A.7.1 ADC calibration code example T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); T::regs().cr().modify(|reg| reg.set_adcal(true)); + + #[cfg(adc_l0)] + while !T::regs().isr().read().eocal() {} + + #[cfg(not(adc_l0))] while T::regs().cr().read().adcal() {} // A.7.2 ADC enable sequence code example @@ -97,6 +113,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } + #[cfg(not(adc_l0))] pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat { // SMP must be ≥ 56 ADC clock cycles when using HSI14. // @@ -133,6 +150,12 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } + #[cfg(adc_l0)] + pub fn set_ckmode(&mut self, ckmode: Ckmode) { + // set ADC clock mode + T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode)); + } + pub async fn read(&mut self, pin: &mut impl AdcPin) -> u16 { let channel = pin.channel(); pin.set_as_analog(); diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index a2b83716d..077cfdcd9 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -664,6 +664,13 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } + /// Write elements directly to the raw buffer. + /// This can be used to fill the buffer before starting the DMA transfer. + #[allow(dead_code)] + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write_immediate(buf) + } + /// Write elements to the ring buffer /// Return a tuple of the length written and the length remaining in the buffer pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 16d02f273..ef9bb3d78 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -934,6 +934,13 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } + /// Write elements directly to the raw buffer. + /// This can be used to fill the buffer before starting the DMA transfer. + #[allow(dead_code)] + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write_immediate(buf) + } + /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index c9f7a3026..23f1d67d5 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -37,6 +37,7 @@ pub struct ReadableDmaRingBuffer<'a, W: Word> { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OverrunError; pub trait DmaCtrl { @@ -263,6 +264,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { self.cap() - dma.get_remaining_transfers() } + /// Write elements directly to the buffer. This must be done before the DMA is started + /// or after the buffer has been cleared using `clear()`. + pub fn write_immediate(&mut self, buffer: &[W]) -> Result<(usize, usize), OverrunError> { + if self.end != 0 { + return Err(OverrunError); + } + let written = self.copy_from(buffer, 0..self.cap()); + self.end = written % self.cap(); + Ok((written, self.cap() - written)) + } + /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { let mut written_data = 0; diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 352e10816..474c44115 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -658,6 +658,7 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(stm32h5)] audioclk: None, per: None, + i2s_ckin: None, ); } diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 9cec6c96c..20cc3112a 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -477,6 +477,7 @@ pub(crate) unsafe fn init(config: Config) { pll3_p: None, pll3_q: None, pll3_r: None, + iclk: None, ); } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 320b29ddb..29ff4a736 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -14,7 +14,7 @@ use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; #[cfg(feature = "low-power")] use crate::rtc::Rtc; -use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; +use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance}; use crate::{interrupt, peripherals}; // NOTE regarding ALARM_COUNT: @@ -234,8 +234,8 @@ impl RtcDriver { w.set_ccie(0, true); }); - ::Interrupt::unpend(); - unsafe { ::Interrupt::enable() }; + ::Interrupt::unpend(); + unsafe { ::Interrupt::enable() }; r.cr1().modify(|w| w.set_cen(true)); } @@ -251,7 +251,7 @@ impl RtcDriver { // Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT. // Other approaches such as writing all zeros, or RMWing won't work, they can // miss interrupts. - r.sr().write_value(regs::SrGp(!sr.0)); + r.sr().write_value(regs::SrGp16(!sr.0)); // Overflow if sr.uif() { diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index eddce0404..72f1ec864 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -23,7 +23,7 @@ pub struct ComplementaryPwmPin<'d, T, C> { macro_rules! complementary_channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> { + impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); @@ -84,14 +84,13 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { this.inner.enable_outputs(); - this.inner - .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] + .iter() + .for_each(|&channel| { + this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); + this.inner.set_output_compare_preload(channel, true); + }); + this } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 210bf7153..9480d6972 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,5 +1,37 @@ //! Timers, PWM, quadrature decoder. +//! +//! Timer inheritance +//! + +// sealed: +// +// Core -------------------------> 1CH -------------------------> 1CH_CMP +// | | ^ | +// +--> Basic_NoCr2 --> Basic +--> 2CH --> GP16 --> GP32 | +--> 2CH_CMP --> ADV +// | | | ^ | | ^ ^ +// | | +------|--|--------------|-----------+ | +// | +--------------------+ +--------------|-----------|---------+ +// | | | | +// | +--------------------------------------|-----------+ +// +----------------------------------------------------+ + +//! ```text +//! BasicInstance --> CaptureCompare16bitInstance --+--> ComplementaryCaptureCompare16bitInstance +//! | +//! +--> CaptureCompare32bitInstance +//! ``` +//! +//! Mapping: +//! +//! | trait | timer | +//! | :----------------------------------------: | ------------------------------------------------------------------------------------------------- | +//! | [BasicInstance] | Basic Timer | +//! | [CaptureCompare16bitInstance] | 1-channel Timer, 2-channel Timer, General Purpose 16-bit Timer | +//! | [CaptureCompare32bitInstance] | General Purpose 32-bit Timer | +//! | [ComplementaryCaptureCompare16bitInstance] | 1-channel with one complentary Timer, 2-channel with one complentary Timer, Advance Control Timer | + +#[cfg(not(stm32l0))] pub mod complementary_pwm; pub mod qei; pub mod simple_pwm; @@ -19,32 +51,32 @@ pub mod low_level { pub(crate) mod sealed { use super::*; - /// Basic 16-bit timer instance. - pub trait Basic16bitInstance: RccPeripheral { + /// Virtual Core 16-bit timer instance. + pub trait CoreInstance: RccPeripheral { /// Interrupt for this timer. type Interrupt: interrupt::typelevel::Interrupt; - /// Get access to the basic 16bit timer registers. + /// Get access to the virutal core 16bit timer registers. /// /// Note: This works even if the timer is more capable, because registers /// for the less capable timers are a subset. This allows writing a driver /// for a given set of capabilities, and having it transparently work with /// more capable timers. - fn regs() -> crate::pac::timer::TimBasic; + fn regs_core() -> crate::pac::timer::TimCore; /// Start the timer. fn start(&mut self) { - Self::regs().cr1().modify(|r| r.set_cen(true)); + Self::regs_core().cr1().modify(|r| r.set_cen(true)); } /// Stop the timer. fn stop(&mut self) { - Self::regs().cr1().modify(|r| r.set_cen(false)); + Self::regs_core().cr1().modify(|r| r.set_cen(false)); } /// Reset the counter value to 0 fn reset(&mut self) { - Self::regs().cnt().write(|r| r.set_cnt(0)); + Self::regs_core().cnt().write(|r| r.set_cnt(0)); } /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. @@ -64,7 +96,7 @@ pub(crate) mod sealed { // the timer counts `0..=arr`, we want it to count `0..divide_by` let arr = unwrap!(u16::try_from(divide_by - 1)); - let regs = Self::regs(); + let regs = Self::regs_core(); regs.psc().write(|r| r.set_psc(psc)); regs.arr().write(|r| r.set_arr(arr)); @@ -77,7 +109,7 @@ pub(crate) mod sealed { /// /// Returns whether the update interrupt flag was set. fn clear_update_interrupt(&mut self) -> bool { - let regs = Self::regs(); + let regs = Self::regs_core(); let sr = regs.sr().read(); if sr.uif() { regs.sr().modify(|r| { @@ -91,29 +123,19 @@ pub(crate) mod sealed { /// Enable/disable the update interrupt. fn enable_update_interrupt(&mut self, enable: bool) { - Self::regs().dier().modify(|r| r.set_uie(enable)); - } - - /// Enable/disable the update dma. - fn enable_update_dma(&mut self, enable: bool) { - Self::regs().dier().modify(|r| r.set_ude(enable)); - } - - /// Get the update dma enable/disable state. - fn get_update_dma_state(&self) -> bool { - Self::regs().dier().read().ude() + Self::regs_core().dier().modify(|r| r.set_uie(enable)); } /// Enable/disable autoreload preload. fn set_autoreload_preload(&mut self, enable: bool) { - Self::regs().cr1().modify(|r| r.set_arpe(enable)); + Self::regs_core().cr1().modify(|r| r.set_arpe(enable)); } /// Get the timer frequency. fn get_frequency(&self) -> Hertz { let timer_f = Self::frequency(); - let regs = Self::regs(); + let regs = Self::regs_core(); let arr = regs.arr().read().arr(); let psc = regs.psc().read().psc(); @@ -121,8 +143,72 @@ pub(crate) mod sealed { } } + /// Virtual Basic without CR2 16-bit timer instance. + pub trait BasicNoCr2Instance: CoreInstance { + /// Get access to the Baisc 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2; + + /// Enable/disable the update dma. + fn enable_update_dma(&mut self, enable: bool) { + Self::regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable)); + } + + /// Get the update dma enable/disable state. + fn get_update_dma_state(&self) -> bool { + Self::regs_basic_no_cr2().dier().read().ude() + } + } + + /// Basic 16-bit timer instance. + pub trait BasicInstance: BasicNoCr2Instance { + /// Get access to the Baisc 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_basic() -> crate::pac::timer::TimBasic; + } + + /// Gneral-purpose 1 channel 16-bit timer instance. + pub trait GeneralPurpose1ChannelInstance: CoreInstance { + /// Get access to the general purpose 1 channel 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_1ch() -> crate::pac::timer::Tim1ch; + + /// Set clock divider. + fn set_clock_division(&mut self, ckd: vals::Ckd) { + Self::regs_1ch().cr1().modify(|r| r.set_ckd(ckd)); + } + + /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. + fn get_max_compare_value(&self) -> u16 { + Self::regs_1ch().arr().read().arr() + } + } + + /// Gneral-purpose 1 channel 16-bit timer instance. + pub trait GeneralPurpose2ChannelInstance: GeneralPurpose1ChannelInstance { + /// Get access to the general purpose 2 channel 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_2ch() -> crate::pac::timer::Tim2ch; + } + /// Gneral-purpose 16-bit timer instance. - pub trait GeneralPurpose16bitInstance: Basic16bitInstance { + pub trait GeneralPurpose16bitInstance: BasicInstance + GeneralPurpose2ChannelInstance { /// Get access to the general purpose 16bit timer registers. /// /// Note: This works even if the timer is more capable, because registers @@ -135,7 +221,7 @@ pub(crate) mod sealed { fn set_counting_mode(&mut self, mode: CountingMode) { let (cms, dir) = mode.into(); - let timer_enabled = Self::regs().cr1().read().cen(); + let timer_enabled = Self::regs_core().cr1().read().cen(); // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. // Changing direction is discouraged while the timer is running. assert!(!timer_enabled); @@ -150,62 +236,8 @@ pub(crate) mod sealed { (cr1.cms(), cr1.dir()).into() } - /// Set clock divider. - fn set_clock_division(&mut self, ckd: vals::Ckd) { - Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd)); - } - } - - /// Gneral-purpose 32-bit timer instance. - pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { - /// Get access to the general purpose 32bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_gp32() -> crate::pac::timer::TimGp32; - - /// Set timer frequency. - fn set_frequency(&mut self, frequency: Hertz) { - let f = frequency.0; - assert!(f > 0); - let timer_f = Self::frequency().0; - let pclk_ticks_per_timer_period = (timer_f / f) as u64; - let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); - let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()); - - let regs = Self::regs_gp32(); - regs.psc().write(|r| r.set_psc(psc)); - regs.arr().write(|r| r.set_arr(arr)); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - /// Get timer frequency. - fn get_frequency(&self) -> Hertz { - let timer_f = Self::frequency(); - - let regs = Self::regs_gp32(); - let arr = regs.arr().read().arr(); - let psc = regs.psc().read().psc(); - - timer_f / arr / (psc + 1) - } - } - - /// Advanced control timer instance. - pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { - /// Get access to the advanced timer registers. - fn regs_advanced() -> crate::pac::timer::TimAdv; - } - - /// Capture/Compare 16-bit timer instance. - pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { /// Set input capture filter. - fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) { + fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::FilterValue) { let raw_channel = channel.index(); Self::regs_gp16() .ccmr_input(raw_channel / 2) @@ -256,14 +288,11 @@ pub(crate) mod sealed { }); } - /// Enable timer outputs. - fn enable_outputs(&mut self); - /// Set output compare mode. fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { - let r = Self::regs_gp16(); let raw_channel: usize = channel.index(); - r.ccmr_output(raw_channel / 2) + Self::regs_gp16() + .ccmr_output(raw_channel / 2) .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } @@ -294,11 +323,6 @@ pub(crate) mod sealed { Self::regs_gp16().ccr(channel.index()).read().ccr() } - /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - fn get_max_compare_value(&self) -> u16 { - Self::regs_gp16().arr().read().arr() - } - /// Get compare value for a channel. fn get_compare_value(&self, channel: Channel) -> u16 { Self::regs_gp16().ccr(channel.index()).read().ccr() @@ -333,35 +357,46 @@ pub(crate) mod sealed { } } - /// Capture/Compare 16-bit timer instance with complementary pin support. - pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance { - /// Set complementary output polarity. - fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccnp(channel.index(), polarity.into())); + #[cfg(not(stm32l0))] + /// Gneral-purpose 32-bit timer instance. + pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { + /// Get access to the general purpose 32bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_gp32() -> crate::pac::timer::TimGp32; + + /// Set timer frequency. + fn set_frequency(&mut self, frequency: Hertz) { + let f = frequency.0; + assert!(f > 0); + let timer_f = Self::frequency().0; + let pclk_ticks_per_timer_period = (timer_f / f) as u64; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); + let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()); + + let regs = Self::regs_gp32(); + regs.psc().write(|r| r.set_psc(psc)); + regs.arr().write(|r| r.set_arr(arr)); + + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); } - /// Set clock divider for the dead time. - fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { - Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); + /// Get timer frequency. + fn get_frequency(&self) -> Hertz { + let timer_f = Self::frequency(); + + let regs = Self::regs_gp32(); + let arr = regs.arr().read().arr(); + let psc = regs.psc().read().psc(); + + timer_f / arr / (psc + 1) } - /// Set dead time, as a fraction of the max duty value. - fn set_dead_time_value(&mut self, value: u8) { - Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); - } - - /// Enable/disable a complementary channel. - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccne(channel.index(), enable)); - } - } - - /// Capture/Compare 32-bit timer instance. - pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance { /// Set comapre value for a channel. fn set_compare_value(&mut self, channel: Channel, value: u32) { Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value)); @@ -382,6 +417,70 @@ pub(crate) mod sealed { Self::regs_gp32().ccr(channel.index()).read().ccr() } } + + #[cfg(not(stm32l0))] + /// Gneral-purpose 1 channel with one complementary 16-bit timer instance. + pub trait GeneralPurpose1ChannelComplementaryInstance: BasicNoCr2Instance + GeneralPurpose1ChannelInstance { + /// Get access to the general purpose 1 channel with one complementary 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp; + + /// Set clock divider for the dead time. + fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { + Self::regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value)); + } + + /// Set dead time, as a fraction of the max duty value. + fn set_dead_time_value(&mut self, value: u8) { + Self::regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value)); + } + + /// Enable timer outputs. + fn enable_outputs(&mut self) { + Self::regs_1ch_cmp().bdtr().modify(|w| w.set_moe(true)); + } + } + + #[cfg(not(stm32l0))] + /// Gneral-purpose 2 channel with one complementary 16-bit timer instance. + pub trait GeneralPurpose2ChannelComplementaryInstance: + BasicInstance + GeneralPurpose2ChannelInstance + GeneralPurpose1ChannelComplementaryInstance + { + /// Get access to the general purpose 2 channel with one complementary 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp; + } + + #[cfg(not(stm32l0))] + /// Advanced control timer instance. + pub trait AdvancedControlInstance: + GeneralPurpose2ChannelComplementaryInstance + GeneralPurpose16bitInstance + { + /// Get access to the advanced timer registers. + fn regs_advanced() -> crate::pac::timer::TimAdv; + + /// Set complementary output polarity. + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccnp(channel.index(), polarity.into())); + } + + /// Enable/disable a complementary channel. + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.index(), enable)); + } + } } /// Timer channel. @@ -572,61 +671,92 @@ impl From for bool { } /// Basic 16-bit timer instance. -pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} +pub trait BasicInstance: sealed::BasicInstance + sealed::BasicNoCr2Instance + sealed::CoreInstance + 'static {} -/// Gneral-purpose 16-bit timer instance. -pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + Basic16bitInstance + 'static {} - -/// Gneral-purpose 32-bit timer instance. -pub trait GeneralPurpose32bitInstance: - sealed::GeneralPurpose32bitInstance + GeneralPurpose16bitInstance + 'static -{ -} - -/// Advanced control timer instance. -pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + GeneralPurpose16bitInstance + 'static {} - -/// Capture/Compare 16-bit timer instance. +// It's just a General-purpose 16-bit timer instance. +/// Capture Compare timer instance. pub trait CaptureCompare16bitInstance: - sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static + BasicInstance + + sealed::GeneralPurpose2ChannelInstance + + sealed::GeneralPurpose1ChannelInstance + + sealed::GeneralPurpose16bitInstance + + 'static { } -/// Capture/Compare 16-bit timer instance with complementary pin support. -pub trait ComplementaryCaptureCompare16bitInstance: - sealed::ComplementaryCaptureCompare16bitInstance + CaptureCompare16bitInstance + AdvancedControlInstance + 'static -{ -} - -/// Capture/Compare 32-bit timer instance. +#[cfg(not(stm32l0))] +// It's just a General-purpose 32-bit timer instance. +/// Capture Compare 32-bit timer instance. pub trait CaptureCompare32bitInstance: - sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static + CaptureCompare16bitInstance + sealed::GeneralPurpose32bitInstance + 'static +{ +} + +#[cfg(not(stm32l0))] +// It's just a Advanced Control timer instance. +/// Complementary Capture Compare 32-bit timer instance. +pub trait ComplementaryCaptureCompare16bitInstance: + CaptureCompare16bitInstance + + sealed::GeneralPurpose1ChannelComplementaryInstance + + sealed::GeneralPurpose2ChannelComplementaryInstance + + sealed::AdvancedControlInstance + + 'static { } pin_trait!(Channel1Pin, CaptureCompare16bitInstance); -pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); pin_trait!(Channel2Pin, CaptureCompare16bitInstance); -pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); pin_trait!(Channel3Pin, CaptureCompare16bitInstance); -pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); pin_trait!(Channel4Pin, CaptureCompare16bitInstance); -pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); + +cfg_if::cfg_if! { + if #[cfg(not(stm32l0))] { + pin_trait!(Channel1ComplementaryPin, ComplementaryCaptureCompare16bitInstance); + pin_trait!(Channel2ComplementaryPin, ComplementaryCaptureCompare16bitInstance); + pin_trait!(Channel3ComplementaryPin, ComplementaryCaptureCompare16bitInstance); + pin_trait!(Channel4ComplementaryPin, ComplementaryCaptureCompare16bitInstance); + + pin_trait!(BreakInputPin, ComplementaryCaptureCompare16bitInstance); + pin_trait!(BreakInput2Pin, ComplementaryCaptureCompare16bitInstance); + + pin_trait!(BreakInputComparator1Pin, ComplementaryCaptureCompare16bitInstance); + pin_trait!(BreakInputComparator2Pin, ComplementaryCaptureCompare16bitInstance); + + pin_trait!(BreakInput2Comparator1Pin, ComplementaryCaptureCompare16bitInstance); + pin_trait!(BreakInput2Comparator2Pin, ComplementaryCaptureCompare16bitInstance); + } +} #[allow(unused)] -macro_rules! impl_basic_16bit_timer { +macro_rules! impl_core_timer { ($inst:ident, $irq:ident) => { - impl sealed::Basic16bitInstance for crate::peripherals::$inst { + impl sealed::CoreInstance for crate::peripherals::$inst { type Interrupt = crate::interrupt::typelevel::$irq; - fn regs() -> crate::pac::timer::TimBasic { + fn regs_core() -> crate::pac::timer::TimCore { + unsafe { crate::pac::timer::TimCore::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_basic_no_cr2_timer { + ($inst:ident) => { + impl sealed::BasicNoCr2Instance for crate::peripherals::$inst { + fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2 { + unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_basic_timer { + ($inst:ident) => { + impl sealed::BasicInstance for crate::peripherals::$inst { + fn regs_basic() -> crate::pac::timer::TimBasic { unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) } } } @@ -634,7 +764,40 @@ macro_rules! impl_basic_16bit_timer { } #[allow(unused)] -macro_rules! impl_32bit_timer { +macro_rules! impl_1ch_timer { + ($inst:ident) => { + impl sealed::GeneralPurpose1ChannelInstance for crate::peripherals::$inst { + fn regs_1ch() -> crate::pac::timer::Tim1ch { + unsafe { crate::pac::timer::Tim1ch::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_2ch_timer { + ($inst:ident) => { + impl sealed::GeneralPurpose2ChannelInstance for crate::peripherals::$inst { + fn regs_2ch() -> crate::pac::timer::Tim2ch { + unsafe { crate::pac::timer::Tim2ch::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_gp16_timer { + ($inst:ident) => { + impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { + fn regs_gp16() -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_gp32_timer { ($inst:ident) => { impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst { fn regs_gp32() -> crate::pac::timer::TimGp32 { @@ -645,83 +808,144 @@ macro_rules! impl_32bit_timer { } #[allow(unused)] -macro_rules! impl_compare_capable_16bit { +macro_rules! impl_1ch_cmp_timer { ($inst:ident) => { - impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self) {} + impl sealed::GeneralPurpose1ChannelComplementaryInstance for crate::peripherals::$inst { + fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp { + unsafe { crate::pac::timer::Tim1chCmp::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_2ch_cmp_timer { + ($inst:ident) => { + impl sealed::GeneralPurpose2ChannelComplementaryInstance for crate::peripherals::$inst { + fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp { + unsafe { crate::pac::timer::Tim2chCmp::from_ptr(crate::pac::$inst.as_ptr()) } + } + } + }; +} + +#[allow(unused)] +macro_rules! impl_adv_timer { + ($inst:ident) => { + impl sealed::AdvancedControlInstance for crate::peripherals::$inst { + fn regs_advanced() -> crate::pac::timer::TimAdv { + unsafe { crate::pac::timer::TimAdv::from_ptr(crate::pac::$inst.as_ptr()) } + } } }; } foreach_interrupt! { - ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { - impl_basic_16bit_timer!($inst, $irq); - impl Basic16bitInstance for crate::peripherals::$inst {} - }; - ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_basic_16bit_timer!($inst, $irq); - impl_compare_capable_16bit!($inst); - impl Basic16bitInstance for crate::peripherals::$inst {} - impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst {} - impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { - fn regs_gp16() -> crate::pac::timer::TimGp16 { - crate::pac::$inst - } - } + ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} + }; + + ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + }; + + + ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + }; + + ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_basic_16bit_timer!($inst, $irq); - impl_32bit_timer!($inst); - impl_compare_capable_16bit!($inst); - impl Basic16bitInstance for crate::peripherals::$inst {} + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl_gp32_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl CaptureCompare32bitInstance for crate::peripherals::$inst {} - impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} - impl GeneralPurpose32bitInstance for crate::peripherals::$inst {} - impl sealed::CaptureCompare32bitInstance for crate::peripherals::$inst {} - - impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { - fn regs_gp16() -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } - } - } }; - ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst {} - impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl_1ch_cmp_timer!($inst); + impl_2ch_cmp_timer!($inst); + impl_adv_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} - impl AdvancedControlInstance for crate::peripherals::$inst {} - impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - r.bdtr().modify(|w| w.set_moe(true)); - } - } - impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} - impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { - fn regs_gp16() -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } - } - } + }; - impl sealed::AdvancedControlInstance for crate::peripherals::$inst { - fn regs_advanced() -> crate::pac::timer::TimAdv { - crate::pac::$inst - } - } + + ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl_1ch_cmp_timer!($inst); + impl_2ch_cmp_timer!($inst); + impl_adv_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} + }; + + + ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { + impl_core_timer!($inst, $irq); + impl_basic_no_cr2_timer!($inst); + impl_basic_timer!($inst); + impl_1ch_timer!($inst); + impl_2ch_timer!($inst); + impl_gp16_timer!($inst); + impl_1ch_cmp_timer!($inst); + impl_2ch_cmp_timer!($inst); + impl_adv_timer!($inst); + impl BasicInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} }; } // Update Event trigger DMA for every timer -dma_trait!(UpDma, Basic16bitInstance); +dma_trait!(UpDma, BasicInstance); dma_trait!(Ch1Dma, CaptureCompare16bitInstance); dma_trait!(Ch2Dma, CaptureCompare16bitInstance); diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 0b4c1225f..1acba504e 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -84,13 +84,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { this.set_frequency(freq); this.inner.start(); - this.inner.enable_outputs(); - [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() .for_each(|&channel| { this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1); - this.inner.set_output_compare_preload(channel, true) + + this.inner.set_output_compare_preload(channel, true); }); this @@ -202,7 +201,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { &mut dma, req, duty, - T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _, + T::regs_1ch().ccr(channel.index()).as_ptr() as *mut _, dma_transfer_option, ) .await diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index f3abfddbc..851a3d721 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) { let nvmc = Nvmc::new(p.NVMC); let nvmc = Mutex::new(BlockingAsync::new(nvmc)); - let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); + let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); let mut magic = [0; 4]; let mut updater = FirmwareUpdater::new(config, &mut magic); loop { diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 3f0bf90e2..ede0c07da 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -36,7 +36,7 @@ async fn main(_s: Spawner) { let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); - let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash); let mut aligned = AlignedBuffer([0; 1]); let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0); diff --git a/examples/boot/application/stm32f3/memory.x b/examples/boot/application/stm32f3/memory.x index f51875766..02ebe3ecf 100644 --- a/examples/boot/application/stm32f3/memory.x +++ b/examples/boot/application/stm32f3/memory.x @@ -3,8 +3,8 @@ MEMORY /* NOTE 1 K = 1 KiBi = 1024 bytes */ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K - FLASH : ORIGIN = 0x08008000, LENGTH = 32K - DFU : ORIGIN = 0x08010000, LENGTH = 36K + FLASH : ORIGIN = 0x08008000, LENGTH = 64K + DFU : ORIGIN = 0x08018000, LENGTH = 66K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 3f9ebe5c8..8858ae3da 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PA5, Level::Low, Speed::Low); led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index c57c29263..d3df11fe4 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB7, Level::Low, Speed::Low); led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); let writer = updater.prepare_update().unwrap(); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index a00d17408..f61ac1f71 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); let writer = updater.prepare_update().unwrap(); diff --git a/examples/boot/application/stm32l0/memory.x b/examples/boot/application/stm32l0/memory.x index a99330145..8866506a8 100644 --- a/examples/boot/application/stm32l0/memory.x +++ b/examples/boot/application/stm32l0/memory.x @@ -3,8 +3,8 @@ MEMORY /* NOTE 1 K = 1 KiBi = 1024 bytes */ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K - FLASH : ORIGIN = 0x08008000, LENGTH = 32K - DFU : ORIGIN = 0x08010000, LENGTH = 36K + FLASH : ORIGIN = 0x08008000, LENGTH = 64K + DFU : ORIGIN = 0x08018000, LENGTH = 66K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K } diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index dbec49d44..f066c1139 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; diff --git a/examples/boot/application/stm32l1/memory.x b/examples/boot/application/stm32l1/memory.x index a99330145..caa525278 100644 --- a/examples/boot/application/stm32l1/memory.x +++ b/examples/boot/application/stm32l1/memory.x @@ -3,8 +3,8 @@ MEMORY /* NOTE 1 K = 1 KiBi = 1024 bytes */ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K - FLASH : ORIGIN = 0x08008000, LENGTH = 32K - DFU : ORIGIN = 0x08010000, LENGTH = 36K + FLASH : ORIGIN = 0x08008000, LENGTH = 46K + DFU : ORIGIN = 0x08013800, LENGTH = 54K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K } diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index dbec49d44..f066c1139 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; diff --git a/examples/boot/application/stm32l4/memory.x b/examples/boot/application/stm32l4/memory.x index f51875766..e1d4e7fa8 100644 --- a/examples/boot/application/stm32l4/memory.x +++ b/examples/boot/application/stm32l4/memory.x @@ -3,8 +3,8 @@ MEMORY /* NOTE 1 K = 1 KiBi = 1024 bytes */ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K - FLASH : ORIGIN = 0x08008000, LENGTH = 32K - DFU : ORIGIN = 0x08010000, LENGTH = 36K + FLASH : ORIGIN = 0x08008000, LENGTH = 64K + DFU : ORIGIN = 0x08018000, LENGTH = 68K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index e946c3cdf..a0079ee33 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; diff --git a/examples/boot/application/stm32wb-dfu/README.md b/examples/boot/application/stm32wb-dfu/README.md index c8dce0387..7f656cde6 100644 --- a/examples/boot/application/stm32wb-dfu/README.md +++ b/examples/boot/application/stm32wb-dfu/README.md @@ -1,29 +1,9 @@ # Examples using bootloader -Example for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a' -which allows you to press a button to start the DFU process, and 'b' which is the updated -application. - - -## Prerequisites - -* `cargo-binutils` -* `cargo-flash` -* `embassy-boot-stm32` +Example for STM32WB demonstrating the USB DFU application. ## Usage ``` -# Flash bootloader -cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32wl55jc-cm4 --chip STM32WLE5JCIx -# Build 'b' -cargo build --release --bin b -# Generate binary for 'b' -cargo objcopy --release --bin b -- -O binary b.bin -``` - -# Flash `a` (which includes b.bin) - -``` -cargo flash --release --bin a --chip STM32WLE5JCIx +cargo flash --release --chip STM32WB55RGVx ``` diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index b2ccb9e1a..37c3d7d90 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); - let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0); firmware_state.mark_booted().expect("Failed to mark booted"); diff --git a/examples/boot/application/stm32wl/memory.x b/examples/boot/application/stm32wl/memory.x index f51875766..e1d4e7fa8 100644 --- a/examples/boot/application/stm32wl/memory.x +++ b/examples/boot/application/stm32wl/memory.x @@ -3,8 +3,8 @@ MEMORY /* NOTE 1 K = 1 KiBi = 1024 bytes */ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K - FLASH : ORIGIN = 0x08008000, LENGTH = 32K - DFU : ORIGIN = 0x08010000, LENGTH = 36K + FLASH : ORIGIN = 0x08008000, LENGTH = 64K + DFU : ORIGIN = 0x08018000, LENGTH = 68K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index b582d8b25..2fb16bdc4 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB9, Level::Low, Speed::Low); led.set_high(); - let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 74e2e293f..67c700437 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -31,7 +31,7 @@ fn main() -> ! { let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); let flash = Mutex::new(RefCell::new(flash)); - let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash); let active_offset = config.active.offset(); let bl: BootLoader = BootLoader::prepare(config); diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index c0e75d1ea..25b1657b8 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -27,7 +27,7 @@ fn main() -> ! { let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); let flash = Mutex::new(RefCell::new(flash)); - let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash); let active_offset = config.active.offset(); let bl: BootLoader = BootLoader::prepare(config); diff --git a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml new file mode 100644 index 000000000..313187adc --- /dev/null +++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml @@ -0,0 +1,57 @@ +[package] +edition = "2021" +name = "stm32-bootloader-dual-bank-flash-example" +version = "0.1.0" +description = "Example bootloader for dual-bank flash STM32 chips" +license = "MIT OR Apache-2.0" + +[dependencies] +defmt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } + +embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } +embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } +cortex-m = { version = "0.7.6", features = [ + "inline-asm", + "critical-section-single-core", +] } +embassy-sync = { version = "0.5.0", path = "../../../../embassy-sync" } +cortex-m-rt = { version = "0.7" } +embedded-storage = "0.3.1" +embedded-storage-async = "0.4.0" +cfg-if = "1.0.0" + +[features] +defmt = ["dep:defmt", "embassy-boot-stm32/defmt", "embassy-stm32/defmt"] +debug = ["defmt-rtt", "defmt"] + +[profile.dev] +debug = 2 +debug-assertions = true +incremental = false +opt-level = 'z' +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 'z' +overflow-checks = false + +# do not optimize proc-macro crates = faster builds from scratch +[profile.dev.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[profile.release.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false diff --git a/examples/boot/bootloader/stm32-dual-bank/README.md b/examples/boot/bootloader/stm32-dual-bank/README.md new file mode 100644 index 000000000..3de3171cd --- /dev/null +++ b/examples/boot/bootloader/stm32-dual-bank/README.md @@ -0,0 +1,44 @@ +# STM32 dual-bank flash Bootloader + +## Overview + +This bootloader leverages `embassy-boot` to interact with the flash. +This example targets STM32 devices with dual-bank flash memory, with a primary focus on the STM32H747XI series. +Users must modify the `memory.x` configuration file to match with the memory layout of their specific STM32 device. + +Additionally, this example can be extended to utilize external flash memory, such as QSPI, for storing partitions. + +## Memory Configuration + +In this example's `memory.x` file, various symbols are defined to assist in effective memory management within the bootloader environment. +For dual-bank STM32 devices, it's crucial to assign these symbols correctly to their respective memory banks. + +### Symbol Definitions + +The bootloader's state and active symbols are anchored to the flash origin of **bank 1**: + +- `__bootloader_state_start` and `__bootloader_state_end` +- `__bootloader_active_start` and `__bootloader_active_end` + +In contrast, the Device Firmware Upgrade (DFU) symbols are aligned with the DFU flash origin in **bank 2**: + +- `__bootloader_dfu_start` and `__bootloader_dfu_end` + +```rust +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(**FLASH**); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(**FLASH**); + +__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(**FLASH**); +__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(**FLASH**); + +__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(**DFU**); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(**DFU**); +``` + +## Flashing the Bootloader + +To flash the bootloader onto your STM32H747XI device, use the following command: + +```bash +cargo flash --features embassy-stm32/stm32h747xi-cm7 --release --chip STM32H747XIHx +``` diff --git a/examples/boot/bootloader/stm32-dual-bank/build.rs b/examples/boot/bootloader/stm32-dual-bank/build.rs new file mode 100644 index 000000000..fd605991f --- /dev/null +++ b/examples/boot/bootloader/stm32-dual-bank/build.rs @@ -0,0 +1,27 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + if env::var("CARGO_FEATURE_DEFMT").is_ok() { + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + } +} diff --git a/examples/boot/bootloader/stm32-dual-bank/memory.x b/examples/boot/bootloader/stm32-dual-bank/memory.x new file mode 100644 index 000000000..665da7139 --- /dev/null +++ b/examples/boot/bootloader/stm32-dual-bank/memory.x @@ -0,0 +1,18 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x08000000, LENGTH = 128K + BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K + ACTIVE : ORIGIN = 0x08040000, LENGTH = 512K + DFU : ORIGIN = 0x08100000, LENGTH = 640K + RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 512K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH); + +__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH); +__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH); + +__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(DFU); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(DFU); diff --git a/examples/boot/bootloader/stm32-dual-bank/src/main.rs b/examples/boot/bootloader/stm32-dual-bank/src/main.rs new file mode 100644 index 000000000..4d2e82d26 --- /dev/null +++ b/examples/boot/bootloader/stm32-dual-bank/src/main.rs @@ -0,0 +1,53 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use cortex_m_rt::{entry, exception}; +#[cfg(feature = "defmt")] +use defmt_rtt as _; +use embassy_boot_stm32::*; +use embassy_stm32::flash::{Flash, BANK1_REGION}; +use embassy_sync::blocking_mutex::Mutex; + +#[entry] +fn main() -> ! { + let p = embassy_stm32::init(Default::default()); + + // Uncomment this if you are debugging the bootloader with debugger/RTT attached, + // as it prevents a hard fault when accessing flash 'too early' after boot. + /* + for i in 0..10000000 { + cortex_m::asm::nop(); + } + */ + + let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); + let flash_bank1 = Mutex::new(RefCell::new(layout.bank1_region)); + let flash_bank2 = Mutex::new(RefCell::new(layout.bank2_region)); + + let config = BootLoaderConfig::from_linkerfile_blocking(&flash_bank1, &flash_bank2, &flash_bank1); + let active_offset = config.active.offset(); + let bl = BootLoader::prepare::<_, _, _, 2048>(config); + + unsafe { bl.load(BANK1_REGION.base + active_offset) } +} + +#[no_mangle] +#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +unsafe extern "C" fn HardFault() { + cortex_m::peripheral::SCB::sys_reset(); +} + +#[exception] +unsafe fn DefaultHandler(_: i16) -> ! { + const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; + let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + + panic!("DefaultHandler #{:?}", irqn); +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + cortex_m::asm::udf(); +} diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 5fd9ea588..99a7a6a6b 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -25,7 +25,7 @@ fn main() -> ! { let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); let flash = Mutex::new(RefCell::new(layout.bank1_region)); - let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash); let active_offset = config.active.offset(); let bl = BootLoader::prepare::<_, _, _, 2048>(config); diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index 96635afa2..854f94d85 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.4", optional = true } -embassy-stm32 = { path = "../../../../embassy-stm32", features = ["stm32wb55rg"] } +embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } embassy-sync = { version = "0.5.0", path = "../../../../embassy-sync" } diff --git a/examples/boot/bootloader/stm32wb-dfu/README.md b/examples/boot/bootloader/stm32wb-dfu/README.md index a82b730b9..d5c6ea57c 100644 --- a/examples/boot/bootloader/stm32wb-dfu/README.md +++ b/examples/boot/bootloader/stm32wb-dfu/README.md @@ -7,5 +7,5 @@ The bootloader uses `embassy-boot` to interact with the flash. Flash the bootloader ``` -cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx +cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx ``` diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index a7ab813b6..d989fbfdf 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs @@ -35,7 +35,7 @@ fn main() -> ! { let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); let flash = Mutex::new(RefCell::new(layout.bank1_region)); - let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash); let active_offset = config.active.offset(); let bl = BootLoader::prepare::<_, _, _, 2048>(config); if bl.state == State::DfuDetach { @@ -45,7 +45,7 @@ fn main() -> ! { config.product = Some("USB-DFU Bootloader example"); config.serial_number = Some("1235678"); - let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash); let mut buffer = AlignedBuffer([0; WRITE_SIZE]); let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]); diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index 8e5c41a43..d88bd838f 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms; use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; -use embassy_stm32::timer::low_level::Basic16bitInstance; +use embassy_stm32::timer::low_level::BasicInstance; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -75,9 +75,9 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { dac.enable(); TIM6::enable_and_reset(); - TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM6::regs().cr1().modify(|w| { + TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); @@ -112,9 +112,9 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { } TIM7::enable_and_reset(); - TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM7::regs().cr1().modify(|w| { + TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); diff --git a/examples/stm32l0/src/bin/adc.rs b/examples/stm32l0/src/bin/adc.rs new file mode 100644 index 000000000..adeaa208a --- /dev/null +++ b/examples/stm32l0/src/bin/adc.rs @@ -0,0 +1,40 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::peripherals::ADC; +use embassy_stm32::{adc, bind_interrupts}; +use embassy_time::{Delay, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1_COMP => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC, Irqs, &mut Delay); + adc.set_sample_time(SampleTime::Cycles79_5); + let mut pin = p.PA1; + + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read(&mut vrefint).await; + let convert_to_millivolts = |sample| { + // From https://www.st.com/resource/en/datasheet/stm32l051c6.pdf + // 6.3.3 Embedded internal reference voltage + const VREFINT_MV: u32 = 1224; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; + + loop { + let v = adc.read(&mut pin).await; + info!("--> {} - {} mV", v, convert_to_millivolts(v)); + Timer::after_millis(100).await; + } +} diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 8e5098557..f227812cd 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms; use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; -use embassy_stm32::timer::low_level::Basic16bitInstance; +use embassy_stm32::timer::low_level::BasicInstance; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -46,9 +46,9 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { dac.enable(); TIM6::enable_and_reset(); - TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM6::regs().cr1().modify(|w| { + TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); @@ -83,9 +83,9 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { } TIM7::enable_and_reset(); - TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM7::regs().cr1().modify(|w| { + TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); diff --git a/tests/stm32/teleprobe.sh b/tests/stm32/teleprobe.sh deleted file mode 100755 index 6eec6ca93..000000000 --- a/tests/stm32/teleprobe.sh +++ /dev/null @@ -1,12 +0,0 @@ -echo Running target=$1 elf=$2 -STATUSCODE=$( - curl \ - -sS \ - --output /dev/stderr \ - --write-out "%{http_code}" \ - -H "Authorization: Bearer $TELEPROBE_TOKEN" \ - https://teleprobe.embassy.dev/targets/$1/run --data-binary @$2 -) -echo -echo HTTP Status code: $STATUSCODE -test "$STATUSCODE" -eq 200