mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 00:02:28 +00:00
Merge branch 'embassy-rs:main' into usb-dfu-erase-then-write
This commit is contained in:
commit
2a09996a78
@ -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.
|
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.
|
||||||
|
|
||||||
## <a href="https://embassy.dev/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
## <a href="https://embassy.dev/book/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||||
## Rust + async ❤️ embedded
|
## 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.
|
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.
|
||||||
|
5
ci.sh
5
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 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 \
|
||||||
--- 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,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 \
|
||||||
--- 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-thread,integrated-timers \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
|
--- 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/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/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/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 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 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 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||||
|
@ -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.
|
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
|
== 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.
|
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`],
|
||||||
This, however, can easily blow up your stack if you are not careful.
|
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:
|
Consider the following example:
|
||||||
[,rust]
|
[,rust]
|
||||||
|
@ -29,11 +29,10 @@ If you see an error like this:
|
|||||||
|
|
||||||
You are likely missing some features of the `embassy-executor` crate.
|
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`
|
* `arch-cortex-m`
|
||||||
* `executor-thread`
|
* `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].
|
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
|
embassy crates are coming from crates.io, and some from git, each of them pulling in a different set
|
||||||
of dependencies.
|
of dependencies.
|
||||||
|
|
||||||
To resolve this issue, make sure to only use a single source for all your embassy crates! To do this,
|
To resolve this issue, make sure to only use a single source for all your embassy crates!
|
||||||
you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
|
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:
|
Example:
|
||||||
|
|
||||||
[source,toml]
|
[source,toml]
|
||||||
----
|
----
|
||||||
[patch.crates-io]
|
[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!
|
Note that the git revision should match any other embassy patches or git dependencies that you are using!
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
= Starting a new Embassy project
|
= 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.
|
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 )
|
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
|
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
|
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)) }
|
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!
|
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
|
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
|
0.300201 INFO low
|
||||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
|
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
|
||||||
----
|
----
|
||||||
|
@ -38,13 +38,18 @@ DEFMT_LOG = "trace" # <- can change to info, warn, or error
|
|||||||
|
|
||||||
== build.rs
|
== 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
|
== Cargo.toml
|
||||||
|
|
||||||
This is your manifest file, where you can configure all of the embassy components to use the features you need.
|
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
|
== memory.x
|
||||||
|
|
||||||
|
@ -49,16 +49,51 @@ pub struct BootLoaderConfig<ACTIVE, DFU, STATE> {
|
|||||||
pub state: STATE,
|
pub state: STATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, FLASH: NorFlash>
|
impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>
|
||||||
BootLoaderConfig<
|
BootLoaderConfig<
|
||||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
BlockingPartition<'a, NoopRawMutex, ACTIVE>,
|
||||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
BlockingPartition<'a, NoopRawMutex, DFU>,
|
||||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
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")]
|
// #[cfg(target_os = "none")]
|
||||||
pub fn from_linkerfile_blocking(flash: &'a Mutex<NoopRawMutex, RefCell<FLASH>>) -> Self {
|
pub fn from_linkerfile_blocking(
|
||||||
|
active_flash: &'a Mutex<NoopRawMutex, RefCell<ACTIVE>>,
|
||||||
|
dfu_flash: &'a Mutex<NoopRawMutex, RefCell<DFU>>,
|
||||||
|
state_flash: &'a Mutex<NoopRawMutex, RefCell<STATE>>,
|
||||||
|
) -> Self {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static __bootloader_state_start: u32;
|
static __bootloader_state_start: u32;
|
||||||
static __bootloader_state_end: u32;
|
static __bootloader_state_end: u32;
|
||||||
@ -73,21 +108,21 @@ impl<'a, FLASH: NorFlash>
|
|||||||
let end = &__bootloader_active_end as *const u32 as u32;
|
let end = &__bootloader_active_end as *const u32 as u32;
|
||||||
trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end);
|
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 dfu = unsafe {
|
||||||
let start = &__bootloader_dfu_start as *const u32 as u32;
|
let start = &__bootloader_dfu_start as *const u32 as u32;
|
||||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
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 state = unsafe {
|
||||||
let start = &__bootloader_state_start as *const u32 as u32;
|
let start = &__bootloader_state_start as *const u32 as u32;
|
||||||
let end = &__bootloader_state_end as *const u32 as u32;
|
let end = &__bootloader_state_end as *const u32 as u32;
|
||||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
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 }
|
Self { active, dfu, state }
|
||||||
|
@ -16,11 +16,14 @@ pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[cfg(target_os = "none")]
|
||||||
impl<'a, FLASH: NorFlash>
|
impl<'a, DFU: NorFlash, STATE: NorFlash>
|
||||||
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>>
|
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, DFU>, Partition<'a, NoopRawMutex, STATE>>
|
||||||
{
|
{
|
||||||
/// Create a firmware updater config from the flash and address symbols defined in the linkerfile
|
/// 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<NoopRawMutex, FLASH>) -> Self {
|
pub fn from_linkerfile(
|
||||||
|
dfu_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, DFU>,
|
||||||
|
state_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, STATE>,
|
||||||
|
) -> Self {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static __bootloader_state_start: u32;
|
static __bootloader_state_start: u32;
|
||||||
static __bootloader_state_end: u32;
|
static __bootloader_state_end: u32;
|
||||||
@ -33,14 +36,14 @@ impl<'a, FLASH: NorFlash>
|
|||||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
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 state = unsafe {
|
||||||
let start = &__bootloader_state_start as *const u32 as u32;
|
let start = &__bootloader_state_start as *const u32 as u32;
|
||||||
let end = &__bootloader_state_end as *const u32 as u32;
|
let end = &__bootloader_state_end as *const u32 as u32;
|
||||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||||
|
|
||||||
Partition::new(flash, start, end - start)
|
Partition::new(state_flash, start, end - start)
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { dfu, state }
|
Self { dfu, state }
|
||||||
|
@ -16,12 +16,43 @@ pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[cfg(target_os = "none")]
|
||||||
impl<'a, FLASH: NorFlash>
|
impl<'a, DFU: NorFlash, STATE: NorFlash>
|
||||||
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>>
|
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, DFU>, 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(
|
pub fn from_linkerfile_blocking(
|
||||||
flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<FLASH>>,
|
dfu_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<DFU>>,
|
||||||
|
state_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<STATE>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static __bootloader_state_start: u32;
|
static __bootloader_state_start: u32;
|
||||||
@ -35,14 +66,14 @@ impl<'a, FLASH: NorFlash>
|
|||||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
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 state = unsafe {
|
||||||
let start = &__bootloader_state_start as *const u32 as u32;
|
let start = &__bootloader_state_start as *const u32 as u32;
|
||||||
let end = &__bootloader_state_end as *const u32 as u32;
|
let end = &__bootloader_state_end as *const u32 as u32;
|
||||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||||
|
|
||||||
BlockingPartition::new(flash, start, end - start)
|
BlockingPartition::new(state_flash, start, end - start)
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { dfu, state }
|
Self { dfu, state }
|
||||||
|
@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
|||||||
/// Firmware updater flash configuration holding the two flashes used by the updater
|
/// 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.
|
/// 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.
|
/// the provided flash according to symbols defined in the linkerfile.
|
||||||
pub struct FirmwareUpdaterConfig<DFU, STATE> {
|
pub struct FirmwareUpdaterConfig<DFU, STATE> {
|
||||||
/// The dfu flash partition
|
/// The dfu flash partition
|
||||||
|
@ -581,6 +581,15 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue {
|
|||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
|
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")]
|
#[cfg(feature = "rtos-trace")]
|
||||||
impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
||||||
fn task_list() {
|
fn task_list() {
|
||||||
@ -588,7 +597,8 @@ impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
fn time() -> u64 {
|
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"))]
|
#[cfg(not(feature = "integrated-timers"))]
|
||||||
fn time() -> u64 {
|
fn time() -> u64 {
|
||||||
|
@ -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::config::ORDER_A as BitOrder;
|
||||||
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
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::sealed::Pin as _;
|
||||||
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
|
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
@ -25,9 +25,9 @@ use crate::{interrupt, pac, Peripheral};
|
|||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// TX buffer was too long.
|
/// Supplied TX buffer overflows EasyDMA transmit buffer
|
||||||
TxBufferTooLong,
|
TxBufferTooLong,
|
||||||
/// RX buffer was too long.
|
/// Supplied RX buffer overflows EasyDMA receive buffer
|
||||||
RxBufferTooLong,
|
RxBufferTooLong,
|
||||||
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||||
BufferNotInRAM,
|
BufferNotInRAM,
|
||||||
@ -220,11 +220,19 @@ impl<'d, T: Instance> Spim<'d, T> {
|
|||||||
|
|
||||||
// Set up the DMA write.
|
// Set up the DMA write.
|
||||||
let (ptr, tx_len) = slice_ptr_parts(tx);
|
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.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
|
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
|
||||||
|
|
||||||
// Set up the DMA read.
|
// Set up the DMA read.
|
||||||
let (ptr, rx_len) = slice_ptr_parts_mut(rx);
|
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.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
|
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
|
||||||
|
|
||||||
|
@ -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 embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||||
pub use pac::spis0::config::ORDER_A as BitOrder;
|
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::sealed::Pin as _;
|
||||||
use crate::gpio::{self, AnyPin, Pin as GpioPin};
|
use crate::gpio::{self, AnyPin, Pin as GpioPin};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
@ -227,11 +227,17 @@ impl<'d, T: Instance> Spis<'d, T> {
|
|||||||
|
|
||||||
// Set up the DMA write.
|
// Set up the DMA write.
|
||||||
let (ptr, len) = slice_ptr_parts(tx);
|
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.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||||
|
|
||||||
// Set up the DMA read.
|
// Set up the DMA read.
|
||||||
let (ptr, len) = slice_ptr_parts_mut(rx);
|
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.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ rand_core = "0.6.3"
|
|||||||
sdio-host = "0.5.0"
|
sdio-host = "0.5.0"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
#stm32-metapac = { version = "15" }
|
#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"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
quote = "1.0.15"
|
||||||
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
|
#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]
|
[features]
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#[cfg_attr(adc_f3, path = "f3.rs")]
|
#[cfg_attr(adc_f3, path = "f3.rs")]
|
||||||
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
|
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
|
||||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||||
|
#[cfg_attr(adc_l0, path = "v1.rs")]
|
||||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||||
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
||||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||||
@ -36,15 +37,15 @@ pub struct Adc<'d, T: Instance> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
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;
|
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 struct State {
|
||||||
pub waker: AtomicWaker,
|
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 {
|
impl State {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -59,14 +60,14 @@ pub(crate) mod sealed {
|
|||||||
|
|
||||||
pub trait Instance: InterruptableInstance {
|
pub trait Instance: InterruptableInstance {
|
||||||
fn regs() -> crate::pac::adc::Adc;
|
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;
|
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;
|
fn state() -> &'static State;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AdcPin<T: Instance> {
|
pub trait AdcPin<T: Instance> {
|
||||||
#[cfg(any(adc_v1, adc_v2))]
|
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||||
fn set_as_analog(&mut self) {}
|
fn set_as_analog(&mut self) {}
|
||||||
|
|
||||||
fn channel(&self) -> u8;
|
fn channel(&self) -> u8;
|
||||||
@ -78,10 +79,10 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ADC instance.
|
/// 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<P = Self> {}
|
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||||
/// ADC instance.
|
/// 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<P = Self> + crate::rcc::RccPeripheral {}
|
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||||
|
|
||||||
/// ADC pin.
|
/// ADC pin.
|
||||||
@ -96,12 +97,12 @@ foreach_adc!(
|
|||||||
crate::pac::$inst
|
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 {
|
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||||
return crate::pac::$common_inst
|
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 {
|
fn state() -> &'static sealed::State {
|
||||||
static STATE: sealed::State = sealed::State::new();
|
static STATE: sealed::State = sealed::State::new();
|
||||||
&STATE
|
&STATE
|
||||||
@ -125,7 +126,7 @@ macro_rules! impl_adc_pin {
|
|||||||
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
|
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||||
|
|
||||||
impl crate::adc::sealed::AdcPin<peripherals::$inst> for crate::peripherals::$pin {
|
impl crate::adc::sealed::AdcPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||||
#[cfg(any(adc_v1, adc_v2))]
|
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||||
fn set_as_analog(&mut self) {
|
fn set_as_analog(&mut self) {
|
||||||
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
|
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/// ADC resolution
|
/// ADC resolution
|
||||||
#[allow(missing_docs)]
|
#[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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Resolution {
|
pub enum Resolution {
|
||||||
@ -25,7 +25,7 @@ pub enum Resolution {
|
|||||||
|
|
||||||
impl Default for Resolution {
|
impl Default for Resolution {
|
||||||
fn default() -> Self {
|
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
|
Self::TwelveBit
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
|
|||||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
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,
|
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ impl Resolution {
|
|||||||
Resolution::TwelveBit => (1 << 12) - 1,
|
Resolution::TwelveBit => (1 << 12) - 1,
|
||||||
Resolution::TenBit => (1 << 10) - 1,
|
Resolution::TenBit => (1 << 10) - 1,
|
||||||
Resolution::EightBit => (1 << 8) - 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,
|
Resolution::SixBit => (1 << 6) - 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ impl_sample_time!(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(adc_g0)]
|
#[cfg(any(adc_l0, adc_g0))]
|
||||||
impl_sample_time!(
|
impl_sample_time!(
|
||||||
"1.5",
|
"1.5",
|
||||||
Cycles1_5,
|
Cycles1_5,
|
||||||
|
@ -4,6 +4,8 @@ use core::task::Poll;
|
|||||||
|
|
||||||
use embassy_hal_internal::into_ref;
|
use embassy_hal_internal::into_ref;
|
||||||
use embedded_hal_02::blocking::delay::DelayUs;
|
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::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
@ -30,8 +32,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(adc_l0))]
|
||||||
pub struct Vbat;
|
pub struct Vbat;
|
||||||
|
|
||||||
|
#[cfg(not(adc_l0))]
|
||||||
impl AdcPin<ADC> for Vbat {}
|
impl AdcPin<ADC> for Vbat {}
|
||||||
|
|
||||||
|
#[cfg(not(adc_l0))]
|
||||||
impl super::sealed::AdcPin<ADC> for Vbat {
|
impl super::sealed::AdcPin<ADC> for Vbat {
|
||||||
fn channel(&self) -> u8 {
|
fn channel(&self) -> u8 {
|
||||||
18
|
18
|
||||||
@ -69,9 +76,18 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
// tstab = 14 * 1/fadc
|
// tstab = 14 * 1/fadc
|
||||||
delay.delay_us(1);
|
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
|
// A.7.1 ADC calibration code example
|
||||||
T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
|
T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
|
||||||
T::regs().cr().modify(|reg| reg.set_adcal(true));
|
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() {}
|
while T::regs().cr().read().adcal() {}
|
||||||
|
|
||||||
// A.7.2 ADC enable sequence code example
|
// 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<u32>) -> Vbat {
|
pub fn enable_vbat(&self, _delay: &mut impl DelayUs<u32>) -> Vbat {
|
||||||
// SMP must be ≥ 56 ADC clock cycles when using HSI14.
|
// 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()));
|
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<T>) -> u16 {
|
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||||
let channel = pin.channel();
|
let channel = pin.channel();
|
||||||
pin.set_as_analog();
|
pin.set_as_analog();
|
||||||
|
@ -664,6 +664,13 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
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
|
/// Write elements to the ring buffer
|
||||||
/// Return a tuple of the length written and the length remaining in the 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> {
|
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||||
|
@ -934,6 +934,13 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
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
|
/// Write elements from the ring buffer
|
||||||
/// Return a tuple of the length written and the length remaining in the 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> {
|
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||||
|
@ -37,6 +37,7 @@ pub struct ReadableDmaRingBuffer<'a, W: Word> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct OverrunError;
|
pub struct OverrunError;
|
||||||
|
|
||||||
pub trait DmaCtrl {
|
pub trait DmaCtrl {
|
||||||
@ -263,6 +264,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
|
|||||||
self.cap() - dma.get_remaining_transfers()
|
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.
|
/// Write an exact number of elements to the ringbuffer.
|
||||||
pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> {
|
pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> {
|
||||||
let mut written_data = 0;
|
let mut written_data = 0;
|
||||||
|
@ -658,6 +658,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
#[cfg(stm32h5)]
|
#[cfg(stm32h5)]
|
||||||
audioclk: None,
|
audioclk: None,
|
||||||
per: None,
|
per: None,
|
||||||
|
i2s_ckin: None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,6 +477,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
pll3_p: None,
|
pll3_p: None,
|
||||||
pll3_q: None,
|
pll3_q: None,
|
||||||
pll3_r: None,
|
pll3_r: None,
|
||||||
|
iclk: None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ use crate::pac::timer::vals;
|
|||||||
use crate::rcc::sealed::RccPeripheral;
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
use crate::rtc::Rtc;
|
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};
|
use crate::{interrupt, peripherals};
|
||||||
|
|
||||||
// NOTE regarding ALARM_COUNT:
|
// NOTE regarding ALARM_COUNT:
|
||||||
@ -234,8 +234,8 @@ impl RtcDriver {
|
|||||||
w.set_ccie(0, true);
|
w.set_ccie(0, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
<T as BasicInstance>::Interrupt::unpend();
|
<T as CoreInstance>::Interrupt::unpend();
|
||||||
unsafe { <T as BasicInstance>::Interrupt::enable() };
|
unsafe { <T as CoreInstance>::Interrupt::enable() };
|
||||||
|
|
||||||
r.cr1().modify(|w| w.set_cen(true));
|
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.
|
// 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
|
// Other approaches such as writing all zeros, or RMWing won't work, they can
|
||||||
// miss interrupts.
|
// miss interrupts.
|
||||||
r.sr().write_value(regs::SrGp(!sr.0));
|
r.sr().write_value(regs::SrGp16(!sr.0));
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
if sr.uif() {
|
if sr.uif() {
|
||||||
|
@ -23,7 +23,7 @@ pub struct ComplementaryPwmPin<'d, T, C> {
|
|||||||
|
|
||||||
macro_rules! complementary_channel_impl {
|
macro_rules! complementary_channel_impl {
|
||||||
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
($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.")]
|
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
|
||||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
|
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
|
||||||
into_ref!(pin);
|
into_ref!(pin);
|
||||||
@ -84,14 +84,13 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
|||||||
|
|
||||||
this.inner.enable_outputs();
|
this.inner.enable_outputs();
|
||||||
|
|
||||||
this.inner
|
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
|
||||||
.set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
|
.iter()
|
||||||
this.inner
|
.for_each(|&channel| {
|
||||||
.set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
|
this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
|
||||||
this.inner
|
this.inner.set_output_compare_preload(channel, true);
|
||||||
.set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
|
});
|
||||||
this.inner
|
|
||||||
.set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,37 @@
|
|||||||
//! Timers, PWM, quadrature decoder.
|
//! 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 complementary_pwm;
|
||||||
pub mod qei;
|
pub mod qei;
|
||||||
pub mod simple_pwm;
|
pub mod simple_pwm;
|
||||||
@ -19,32 +51,32 @@ pub mod low_level {
|
|||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Basic 16-bit timer instance.
|
/// Virtual Core 16-bit timer instance.
|
||||||
pub trait Basic16bitInstance: RccPeripheral {
|
pub trait CoreInstance: RccPeripheral {
|
||||||
/// Interrupt for this timer.
|
/// Interrupt for this timer.
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
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
|
/// 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 the less capable timers are a subset. This allows writing a driver
|
||||||
/// for a given set of capabilities, and having it transparently work with
|
/// for a given set of capabilities, and having it transparently work with
|
||||||
/// more capable timers.
|
/// more capable timers.
|
||||||
fn regs() -> crate::pac::timer::TimBasic;
|
fn regs_core() -> crate::pac::timer::TimCore;
|
||||||
|
|
||||||
/// Start the timer.
|
/// Start the timer.
|
||||||
fn start(&mut self) {
|
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.
|
/// Stop the timer.
|
||||||
fn stop(&mut self) {
|
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
|
/// Reset the counter value to 0
|
||||||
fn reset(&mut self) {
|
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.
|
/// 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`
|
// the timer counts `0..=arr`, we want it to count `0..divide_by`
|
||||||
let arr = unwrap!(u16::try_from(divide_by - 1));
|
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.psc().write(|r| r.set_psc(psc));
|
||||||
regs.arr().write(|r| r.set_arr(arr));
|
regs.arr().write(|r| r.set_arr(arr));
|
||||||
|
|
||||||
@ -77,7 +109,7 @@ pub(crate) mod sealed {
|
|||||||
///
|
///
|
||||||
/// Returns whether the update interrupt flag was set.
|
/// Returns whether the update interrupt flag was set.
|
||||||
fn clear_update_interrupt(&mut self) -> bool {
|
fn clear_update_interrupt(&mut self) -> bool {
|
||||||
let regs = Self::regs();
|
let regs = Self::regs_core();
|
||||||
let sr = regs.sr().read();
|
let sr = regs.sr().read();
|
||||||
if sr.uif() {
|
if sr.uif() {
|
||||||
regs.sr().modify(|r| {
|
regs.sr().modify(|r| {
|
||||||
@ -91,29 +123,19 @@ pub(crate) mod sealed {
|
|||||||
|
|
||||||
/// Enable/disable the update interrupt.
|
/// Enable/disable the update interrupt.
|
||||||
fn enable_update_interrupt(&mut self, enable: bool) {
|
fn enable_update_interrupt(&mut self, enable: bool) {
|
||||||
Self::regs().dier().modify(|r| r.set_uie(enable));
|
Self::regs_core().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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable/disable autoreload preload.
|
/// Enable/disable autoreload preload.
|
||||||
fn set_autoreload_preload(&mut self, enable: bool) {
|
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.
|
/// Get the timer frequency.
|
||||||
fn get_frequency(&self) -> Hertz {
|
fn get_frequency(&self) -> Hertz {
|
||||||
let timer_f = Self::frequency();
|
let timer_f = Self::frequency();
|
||||||
|
|
||||||
let regs = Self::regs();
|
let regs = Self::regs_core();
|
||||||
let arr = regs.arr().read().arr();
|
let arr = regs.arr().read().arr();
|
||||||
let psc = regs.psc().read().psc();
|
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.
|
/// Gneral-purpose 16-bit timer instance.
|
||||||
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
pub trait GeneralPurpose16bitInstance: BasicInstance + GeneralPurpose2ChannelInstance {
|
||||||
/// Get access to the general purpose 16bit timer registers.
|
/// Get access to the general purpose 16bit timer registers.
|
||||||
///
|
///
|
||||||
/// Note: This works even if the timer is more capable, because 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) {
|
fn set_counting_mode(&mut self, mode: CountingMode) {
|
||||||
let (cms, dir) = mode.into();
|
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 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.
|
// Changing direction is discouraged while the timer is running.
|
||||||
assert!(!timer_enabled);
|
assert!(!timer_enabled);
|
||||||
@ -150,62 +236,8 @@ pub(crate) mod sealed {
|
|||||||
(cr1.cms(), cr1.dir()).into()
|
(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.
|
/// 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();
|
let raw_channel = channel.index();
|
||||||
Self::regs_gp16()
|
Self::regs_gp16()
|
||||||
.ccmr_input(raw_channel / 2)
|
.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.
|
/// Set output compare mode.
|
||||||
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
|
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
|
||||||
let r = Self::regs_gp16();
|
|
||||||
let raw_channel: usize = channel.index();
|
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()));
|
.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()
|
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.
|
/// Get compare value for a channel.
|
||||||
fn get_compare_value(&self, channel: Channel) -> u16 {
|
fn get_compare_value(&self, channel: Channel) -> u16 {
|
||||||
Self::regs_gp16().ccr(channel.index()).read().ccr()
|
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.
|
#[cfg(not(stm32l0))]
|
||||||
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance {
|
/// Gneral-purpose 32-bit timer instance.
|
||||||
/// Set complementary output polarity.
|
pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
|
||||||
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
/// Get access to the general purpose 32bit timer registers.
|
||||||
Self::regs_advanced()
|
///
|
||||||
.ccer()
|
/// Note: This works even if the timer is more capable, because registers
|
||||||
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
|
/// 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.
|
/// Get timer frequency.
|
||||||
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
|
fn get_frequency(&self) -> Hertz {
|
||||||
Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
|
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.
|
/// Set comapre value for a channel.
|
||||||
fn set_compare_value(&mut self, channel: Channel, value: u32) {
|
fn set_compare_value(&mut self, channel: Channel, value: u32) {
|
||||||
Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value));
|
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()
|
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.
|
/// Timer channel.
|
||||||
@ -572,61 +671,92 @@ impl From<OutputPolarity> for bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Basic 16-bit timer instance.
|
/// 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.
|
// It's just a General-purpose 16-bit timer instance.
|
||||||
pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + Basic16bitInstance + 'static {}
|
/// Capture Compare timer instance.
|
||||||
|
|
||||||
/// 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.
|
|
||||||
pub trait CaptureCompare16bitInstance:
|
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.
|
#[cfg(not(stm32l0))]
|
||||||
pub trait ComplementaryCaptureCompare16bitInstance:
|
// It's just a General-purpose 32-bit timer instance.
|
||||||
sealed::ComplementaryCaptureCompare16bitInstance + CaptureCompare16bitInstance + AdvancedControlInstance + 'static
|
/// Capture Compare 32-bit timer instance.
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Capture/Compare 32-bit timer instance.
|
|
||||||
pub trait CaptureCompare32bitInstance:
|
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!(Channel1Pin, CaptureCompare16bitInstance);
|
||||||
pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance);
|
|
||||||
pin_trait!(Channel2Pin, CaptureCompare16bitInstance);
|
pin_trait!(Channel2Pin, CaptureCompare16bitInstance);
|
||||||
pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance);
|
|
||||||
pin_trait!(Channel3Pin, CaptureCompare16bitInstance);
|
pin_trait!(Channel3Pin, CaptureCompare16bitInstance);
|
||||||
pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance);
|
|
||||||
pin_trait!(Channel4Pin, CaptureCompare16bitInstance);
|
pin_trait!(Channel4Pin, CaptureCompare16bitInstance);
|
||||||
pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance);
|
|
||||||
pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance);
|
pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance);
|
||||||
pin_trait!(BreakInputPin, CaptureCompare16bitInstance);
|
|
||||||
pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance);
|
cfg_if::cfg_if! {
|
||||||
pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance);
|
if #[cfg(not(stm32l0))] {
|
||||||
pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance);
|
pin_trait!(Channel1ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
|
||||||
pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance);
|
pin_trait!(Channel2ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
|
||||||
pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance);
|
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)]
|
#[allow(unused)]
|
||||||
macro_rules! impl_basic_16bit_timer {
|
macro_rules! impl_core_timer {
|
||||||
($inst:ident, $irq:ident) => {
|
($inst:ident, $irq:ident) => {
|
||||||
impl sealed::Basic16bitInstance for crate::peripherals::$inst {
|
impl sealed::CoreInstance for crate::peripherals::$inst {
|
||||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
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()) }
|
unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -634,7 +764,40 @@ macro_rules! impl_basic_16bit_timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[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) => {
|
($inst:ident) => {
|
||||||
impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst {
|
impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst {
|
||||||
fn regs_gp32() -> crate::pac::timer::TimGp32 {
|
fn regs_gp32() -> crate::pac::timer::TimGp32 {
|
||||||
@ -645,83 +808,144 @@ macro_rules! impl_32bit_timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
macro_rules! impl_compare_capable_16bit {
|
macro_rules! impl_1ch_cmp_timer {
|
||||||
($inst:ident) => {
|
($inst:ident) => {
|
||||||
impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst {
|
impl sealed::GeneralPurpose1ChannelComplementaryInstance for crate::peripherals::$inst {
|
||||||
fn enable_outputs(&mut self) {}
|
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! {
|
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 {
|
($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
|
||||||
fn regs_gp16() -> crate::pac::timer::TimGp16 {
|
impl_core_timer!($inst, $irq);
|
||||||
crate::pac::$inst
|
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) => {
|
($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
|
||||||
impl_basic_16bit_timer!($inst, $irq);
|
impl_core_timer!($inst, $irq);
|
||||||
impl_32bit_timer!($inst);
|
impl_basic_no_cr2_timer!($inst);
|
||||||
impl_compare_capable_16bit!($inst);
|
impl_basic_timer!($inst);
|
||||||
impl Basic16bitInstance for crate::peripherals::$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 CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||||
impl CaptureCompare32bitInstance 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) => {
|
($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => {
|
||||||
impl_basic_16bit_timer!($inst, $irq);
|
impl_core_timer!($inst, $irq);
|
||||||
|
impl_basic_no_cr2_timer!($inst);
|
||||||
impl Basic16bitInstance for crate::peripherals::$inst {}
|
impl_basic_timer!($inst);
|
||||||
impl GeneralPurpose16bitInstance for crate::peripherals::$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 CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||||
impl ComplementaryCaptureCompare16bitInstance 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 {
|
($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => {
|
||||||
crate::pac::$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_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
|
// Update Event trigger DMA for every timer
|
||||||
dma_trait!(UpDma, Basic16bitInstance);
|
dma_trait!(UpDma, BasicInstance);
|
||||||
|
|
||||||
dma_trait!(Ch1Dma, CaptureCompare16bitInstance);
|
dma_trait!(Ch1Dma, CaptureCompare16bitInstance);
|
||||||
dma_trait!(Ch2Dma, CaptureCompare16bitInstance);
|
dma_trait!(Ch2Dma, CaptureCompare16bitInstance);
|
||||||
|
@ -84,13 +84,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
this.set_frequency(freq);
|
this.set_frequency(freq);
|
||||||
this.inner.start();
|
this.inner.start();
|
||||||
|
|
||||||
this.inner.enable_outputs();
|
|
||||||
|
|
||||||
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
|
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|&channel| {
|
.for_each(|&channel| {
|
||||||
this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
|
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
|
this
|
||||||
@ -202,7 +201,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
|||||||
&mut dma,
|
&mut dma,
|
||||||
req,
|
req,
|
||||||
duty,
|
duty,
|
||||||
T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _,
|
T::regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
|
||||||
dma_transfer_option,
|
dma_transfer_option,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let nvmc = Nvmc::new(p.NVMC);
|
let nvmc = Nvmc::new(p.NVMC);
|
||||||
let nvmc = Mutex::new(BlockingAsync::new(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 magic = [0; 4];
|
||||||
let mut updater = FirmwareUpdater::new(config, &mut magic);
|
let mut updater = FirmwareUpdater::new(config, &mut magic);
|
||||||
loop {
|
loop {
|
||||||
|
@ -36,7 +36,7 @@ async fn main(_s: Spawner) {
|
|||||||
let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);
|
let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);
|
||||||
let flash = Mutex::new(RefCell::new(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 aligned = AlignedBuffer([0; 1]);
|
||||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
|
let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ MEMORY
|
|||||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
DFU : ORIGIN = 0x08018000, LENGTH = 66K
|
||||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
|
let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||||
button.wait_for_falling_edge().await;
|
button.wait_for_falling_edge().await;
|
||||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
|
let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);
|
||||||
let writer = updater.prepare_update().unwrap();
|
let writer = updater.prepare_update().unwrap();
|
||||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);
|
||||||
let writer = updater.prepare_update().unwrap();
|
let writer = updater.prepare_update().unwrap();
|
||||||
|
@ -3,8 +3,8 @@ MEMORY
|
|||||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
DFU : ORIGIN = 0x08018000, LENGTH = 66K
|
||||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||||
button.wait_for_falling_edge().await;
|
button.wait_for_falling_edge().await;
|
||||||
|
@ -3,8 +3,8 @@ MEMORY
|
|||||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
FLASH : ORIGIN = 0x08008000, LENGTH = 46K
|
||||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
DFU : ORIGIN = 0x08013800, LENGTH = 54K
|
||||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||||
button.wait_for_falling_edge().await;
|
button.wait_for_falling_edge().await;
|
||||||
|
@ -3,8 +3,8 @@ MEMORY
|
|||||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
DFU : ORIGIN = 0x08018000, LENGTH = 68K
|
||||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||||
button.wait_for_falling_edge().await;
|
button.wait_for_falling_edge().await;
|
||||||
|
@ -1,29 +1,9 @@
|
|||||||
# Examples using bootloader
|
# Examples using bootloader
|
||||||
|
|
||||||
Example for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a'
|
Example for STM32WB demonstrating the USB DFU application.
|
||||||
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`
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
# Flash bootloader
|
cargo flash --release --chip STM32WB55RGVx
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let flash = Flash::new_blocking(p.FLASH);
|
let flash = Flash::new_blocking(p.FLASH);
|
||||||
let flash = Mutex::new(RefCell::new(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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0);
|
let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0);
|
||||||
firmware_state.mark_booted().expect("Failed to mark booted");
|
firmware_state.mark_booted().expect("Failed to mark booted");
|
||||||
|
@ -3,8 +3,8 @@ MEMORY
|
|||||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
DFU : ORIGIN = 0x08018000, LENGTH = 68K
|
||||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
|
let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
|
||||||
led.set_high();
|
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 magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||||
button.wait_for_falling_edge().await;
|
button.wait_for_falling_edge().await;
|
||||||
|
@ -31,7 +31,7 @@ fn main() -> ! {
|
|||||||
let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);
|
let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);
|
||||||
let flash = Mutex::new(RefCell::new(flash));
|
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 active_offset = config.active.offset();
|
||||||
let bl: BootLoader = BootLoader::prepare(config);
|
let bl: BootLoader = BootLoader::prepare(config);
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ fn main() -> ! {
|
|||||||
let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8));
|
let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8));
|
||||||
let flash = Mutex::new(RefCell::new(flash));
|
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 active_offset = config.active.offset();
|
||||||
let bl: BootLoader = BootLoader::prepare(config);
|
let bl: BootLoader = BootLoader::prepare(config);
|
||||||
|
|
||||||
|
57
examples/boot/bootloader/stm32-dual-bank/Cargo.toml
Normal file
57
examples/boot/bootloader/stm32-dual-bank/Cargo.toml
Normal file
@ -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
|
44
examples/boot/bootloader/stm32-dual-bank/README.md
Normal file
44
examples/boot/bootloader/stm32-dual-bank/README.md
Normal file
@ -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
|
||||||
|
```
|
27
examples/boot/bootloader/stm32-dual-bank/build.rs
Normal file
27
examples/boot/bootloader/stm32-dual-bank/build.rs
Normal file
@ -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");
|
||||||
|
}
|
||||||
|
}
|
18
examples/boot/bootloader/stm32-dual-bank/memory.x
Normal file
18
examples/boot/bootloader/stm32-dual-bank/memory.x
Normal file
@ -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);
|
53
examples/boot/bootloader/stm32-dual-bank/src/main.rs
Normal file
53
examples/boot/bootloader/stm32-dual-bank/src/main.rs
Normal file
@ -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();
|
||||||
|
}
|
@ -25,7 +25,7 @@ fn main() -> ! {
|
|||||||
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
||||||
let flash = Mutex::new(RefCell::new(layout.bank1_region));
|
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 active_offset = config.active.offset();
|
||||||
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
defmt-rtt = { version = "0.4", 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" }
|
embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
|
||||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||||
embassy-sync = { version = "0.5.0", path = "../../../../embassy-sync" }
|
embassy-sync = { version = "0.5.0", path = "../../../../embassy-sync" }
|
||||||
|
@ -7,5 +7,5 @@ The bootloader uses `embassy-boot` to interact with the flash.
|
|||||||
Flash the bootloader
|
Flash the bootloader
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx
|
cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx
|
||||||
```
|
```
|
||||||
|
@ -35,7 +35,7 @@ fn main() -> ! {
|
|||||||
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
||||||
let flash = Mutex::new(RefCell::new(layout.bank1_region));
|
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 active_offset = config.active.offset();
|
||||||
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
||||||
if bl.state == State::DfuDetach {
|
if bl.state == State::DfuDetach {
|
||||||
@ -45,7 +45,7 @@ fn main() -> ! {
|
|||||||
config.product = Some("USB-DFU Bootloader example");
|
config.product = Some("USB-DFU Bootloader example");
|
||||||
config.serial_number = Some("1235678");
|
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 mut buffer = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);
|
let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);
|
||||||
|
|
||||||
|
@ -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::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||||
use embassy_stm32::time::Hertz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
use embassy_stm32::timer::low_level::BasicInstance;
|
||||||
use micromath::F32Ext;
|
use micromath::F32Ext;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -75,9 +75,9 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
|||||||
dac.enable();
|
dac.enable();
|
||||||
|
|
||||||
TIM6::enable_and_reset();
|
TIM6::enable_and_reset();
|
||||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||||
TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||||
TIM6::regs().cr1().modify(|w| {
|
TIM6::regs_basic().cr1().modify(|w| {
|
||||||
w.set_opm(false);
|
w.set_opm(false);
|
||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
});
|
});
|
||||||
@ -112,9 +112,9 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TIM7::enable_and_reset();
|
TIM7::enable_and_reset();
|
||||||
TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||||
TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||||
TIM7::regs().cr1().modify(|w| {
|
TIM7::regs_basic().cr1().modify(|w| {
|
||||||
w.set_opm(false);
|
w.set_opm(false);
|
||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
});
|
});
|
||||||
|
40
examples/stm32l0/src/bin/adc.rs
Normal file
40
examples/stm32l0/src/bin/adc.rs
Normal file
@ -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<ADC>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
}
|
||||||
|
}
|
@ -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::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||||
use embassy_stm32::time::Hertz;
|
use embassy_stm32::time::Hertz;
|
||||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
use embassy_stm32::timer::low_level::BasicInstance;
|
||||||
use micromath::F32Ext;
|
use micromath::F32Ext;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
@ -46,9 +46,9 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
|||||||
dac.enable();
|
dac.enable();
|
||||||
|
|
||||||
TIM6::enable_and_reset();
|
TIM6::enable_and_reset();
|
||||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||||
TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||||
TIM6::regs().cr1().modify(|w| {
|
TIM6::regs_basic().cr1().modify(|w| {
|
||||||
w.set_opm(false);
|
w.set_opm(false);
|
||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
});
|
});
|
||||||
@ -83,9 +83,9 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TIM7::enable_and_reset();
|
TIM7::enable_and_reset();
|
||||||
TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||||
TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||||
TIM7::regs().cr1().modify(|w| {
|
TIM7::regs_basic().cr1().modify(|w| {
|
||||||
w.set_opm(false);
|
w.set_opm(false);
|
||||||
w.set_cen(true);
|
w.set_cen(true);
|
||||||
});
|
});
|
||||||
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user