mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 14:22:33 +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.
|
||||
|
||||
## <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
|
||||
|
||||
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 thumbv7em-none-eabi --features arch-cortex-m \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
|
||||
@ -194,7 +196,8 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf --features embassy-stm32/stm32wb55rg \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabihf --features embassy-stm32/stm32h747xi-cm7 \
|
||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||
|
@ -3,8 +3,10 @@
|
||||
Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework.
|
||||
|
||||
== Passing Buffers by Reference
|
||||
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack.
|
||||
This, however, can easily blow up your stack if you are not careful.
|
||||
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`],
|
||||
to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't
|
||||
want to spend resources on an allocator and end up placing buffers on the stack. This, however, can easily blow up
|
||||
your stack if you are not careful.
|
||||
|
||||
Consider the following example:
|
||||
[,rust]
|
||||
|
@ -29,11 +29,10 @@ If you see an error like this:
|
||||
|
||||
You are likely missing some features of the `embassy-executor` crate.
|
||||
|
||||
For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate:
|
||||
For Cortex-M targets, check whether ALL of the following features are enabled in your `Cargo.toml` for the `embassy-executor` crate:
|
||||
|
||||
* `arch-cortex-m`
|
||||
* `executor-thread`
|
||||
* `nightly`
|
||||
|
||||
For ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
|
||||
|
||||
@ -125,15 +124,18 @@ You have multiple versions of the same crate in your dependency tree. This means
|
||||
embassy crates are coming from crates.io, and some from git, each of them pulling in a different set
|
||||
of dependencies.
|
||||
|
||||
To resolve this issue, make sure to only use a single source for all your embassy crates! To do this,
|
||||
you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
|
||||
To resolve this issue, make sure to only use a single source for all your embassy crates!
|
||||
To do this, you should patch your dependencies to use git sources using `[patch.crates.io]`
|
||||
and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
|
||||
|
||||
Example:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[patch.crates-io]
|
||||
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
# embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
----
|
||||
|
||||
Note that the git revision should match any other embassy patches or git dependencies that you are using!
|
||||
|
@ -1,6 +1,17 @@
|
||||
= Starting a new Embassy project
|
||||
|
||||
Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. The easiest way to do this is to adapt an example for a similar chip to the one you’re targeting.
|
||||
Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project.
|
||||
|
||||
There are some tools for generating Embassy projects: (WIP)
|
||||
|
||||
==== CLI
|
||||
- link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF)
|
||||
|
||||
==== cargo-generate
|
||||
- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP)
|
||||
- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP)
|
||||
|
||||
But if you want to start from scratch:
|
||||
|
||||
As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes.
|
||||
|
||||
@ -166,13 +177,13 @@ should result in a blinking LED (if there’s one attached to the pin in `src/ma
|
||||
Erasing sectors ✔ [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s )
|
||||
Programming pages ✔ [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s ) Finished in 0.817s
|
||||
0.000000 TRACE BDCR configured: 00008200
|
||||
└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117
|
||||
└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117
|
||||
0.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) }
|
||||
└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130
|
||||
└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130
|
||||
0.000000 INFO Hello World!
|
||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14
|
||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14
|
||||
0.000091 INFO high
|
||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19
|
||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19
|
||||
0.300201 INFO low
|
||||
└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23
|
||||
----
|
||||
└─ 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
|
||||
|
||||
This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
|
||||
This is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
|
||||
|
||||
== Cargo.toml
|
||||
|
||||
This is your manifest file, where you can configure all of the embassy components to use the features you need.
|
||||
|
||||
TODO: someone should exhaustively describe every feature for every component!
|
||||
==== Features
|
||||
===== Time
|
||||
- tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes.
|
||||
- defmt-timestamp-uptime: defmt log entries will display the uptime in seconds.
|
||||
|
||||
...more to come
|
||||
|
||||
== memory.x
|
||||
|
||||
|
@ -49,16 +49,51 @@ pub struct BootLoaderConfig<ACTIVE, DFU, STATE> {
|
||||
pub state: STATE,
|
||||
}
|
||||
|
||||
impl<'a, FLASH: NorFlash>
|
||||
impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>
|
||||
BootLoaderConfig<
|
||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
||||
BlockingPartition<'a, NoopRawMutex, ACTIVE>,
|
||||
BlockingPartition<'a, NoopRawMutex, DFU>,
|
||||
BlockingPartition<'a, NoopRawMutex, STATE>,
|
||||
>
|
||||
{
|
||||
/// Create a bootloader config from the flash and address symbols defined in the linkerfile
|
||||
/// Constructs a `BootLoaderConfig` instance from flash memory and address symbols defined in the linker file.
|
||||
///
|
||||
/// This method initializes `BlockingPartition` instances for the active, DFU (Device Firmware Update),
|
||||
/// and state partitions, leveraging start and end addresses specified by the linker. These partitions
|
||||
/// are critical for managing firmware updates, application state, and boot operations within the bootloader.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `active_flash`: A reference to a mutex-protected `RefCell` for the active partition's flash interface.
|
||||
/// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.
|
||||
/// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.
|
||||
///
|
||||
/// # Safety
|
||||
/// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses
|
||||
/// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined
|
||||
/// in the memory.x file to prevent undefined behavior.
|
||||
///
|
||||
/// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory
|
||||
/// interfaces provided are compatible with these regions.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `BootLoaderConfig` instance with `BlockingPartition` instances for the active, DFU, and state partitions.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Assume `active_flash`, `dfu_flash`, and `state_flash` all share the same flash memory interface.
|
||||
/// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
||||
/// let flash = Mutex::new(RefCell::new(layout.bank1_region));
|
||||
///
|
||||
/// let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
|
||||
/// // `config` can now be used to create a `BootLoader` instance for managing boot operations.
|
||||
/// ```
|
||||
/// Working examples can be found in the bootloader examples folder.
|
||||
// #[cfg(target_os = "none")]
|
||||
pub fn from_linkerfile_blocking(flash: &'a Mutex<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" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
@ -73,21 +108,21 @@ impl<'a, FLASH: NorFlash>
|
||||
let end = &__bootloader_active_end as *const u32 as u32;
|
||||
trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(active_flash, start, end - start)
|
||||
};
|
||||
let dfu = unsafe {
|
||||
let start = &__bootloader_dfu_start as *const u32 as u32;
|
||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(dfu_flash, start, end - start)
|
||||
};
|
||||
let state = unsafe {
|
||||
let start = &__bootloader_state_start as *const u32 as u32;
|
||||
let end = &__bootloader_state_end as *const u32 as u32;
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(state_flash, start, end - start)
|
||||
};
|
||||
|
||||
Self { active, dfu, state }
|
||||
|
@ -16,11 +16,14 @@ pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "none")]
|
||||
impl<'a, FLASH: NorFlash>
|
||||
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>>
|
||||
impl<'a, DFU: NorFlash, STATE: NorFlash>
|
||||
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, DFU>, Partition<'a, NoopRawMutex, STATE>>
|
||||
{
|
||||
/// Create a firmware updater config from the flash and address symbols defined in the linkerfile
|
||||
pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex<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" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
@ -33,14 +36,14 @@ impl<'a, FLASH: NorFlash>
|
||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
Partition::new(flash, start, end - start)
|
||||
Partition::new(dfu_flash, start, end - start)
|
||||
};
|
||||
let state = unsafe {
|
||||
let start = &__bootloader_state_start as *const u32 as u32;
|
||||
let end = &__bootloader_state_end as *const u32 as u32;
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
Partition::new(flash, start, end - start)
|
||||
Partition::new(state_flash, start, end - start)
|
||||
};
|
||||
|
||||
Self { dfu, state }
|
||||
|
@ -16,12 +16,43 @@ pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "none")]
|
||||
impl<'a, FLASH: NorFlash>
|
||||
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>>
|
||||
impl<'a, DFU: NorFlash, STATE: NorFlash>
|
||||
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(
|
||||
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 {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
@ -35,14 +66,14 @@ impl<'a, FLASH: NorFlash>
|
||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(dfu_flash, start, end - start)
|
||||
};
|
||||
let state = unsafe {
|
||||
let start = &__bootloader_state_start as *const u32 as u32;
|
||||
let end = &__bootloader_state_end as *const u32 as u32;
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(state_flash, start, end - start)
|
||||
};
|
||||
|
||||
Self { dfu, state }
|
||||
|
@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
||||
/// Firmware updater flash configuration holding the two flashes used by the updater
|
||||
///
|
||||
/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use.
|
||||
/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
|
||||
/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile_blocking`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
|
||||
/// the provided flash according to symbols defined in the linkerfile.
|
||||
pub struct FirmwareUpdaterConfig<DFU, STATE> {
|
||||
/// The dfu flash partition
|
||||
|
@ -581,6 +581,15 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
|
||||
|
||||
#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))]
|
||||
const fn gcd(a: u64, b: u64) -> u64 {
|
||||
if b == 0 {
|
||||
a
|
||||
} else {
|
||||
gcd(b, a % b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
||||
fn task_list() {
|
||||
@ -588,7 +597,8 @@ impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
||||
}
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
fn time() -> u64 {
|
||||
Instant::now().as_micros()
|
||||
const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000);
|
||||
embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M)
|
||||
}
|
||||
#[cfg(not(feature = "integrated-timers"))]
|
||||
fn time() -> u64 {
|
||||
|
@ -13,7 +13,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO
|
||||
pub use pac::spim0::config::ORDER_A as BitOrder;
|
||||
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
||||
|
||||
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
@ -25,9 +25,9 @@ use crate::{interrupt, pac, Peripheral};
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// TX buffer was too long.
|
||||
/// Supplied TX buffer overflows EasyDMA transmit buffer
|
||||
TxBufferTooLong,
|
||||
/// RX buffer was too long.
|
||||
/// Supplied RX buffer overflows EasyDMA receive buffer
|
||||
RxBufferTooLong,
|
||||
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||
BufferNotInRAM,
|
||||
@ -220,11 +220,19 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
|
||||
// Set up the DMA write.
|
||||
let (ptr, tx_len) = slice_ptr_parts(tx);
|
||||
if tx_len > EASY_DMA_SIZE {
|
||||
return Err(Error::TxBufferTooLong);
|
||||
}
|
||||
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
|
||||
|
||||
// Set up the DMA read.
|
||||
let (ptr, rx_len) = slice_ptr_parts_mut(rx);
|
||||
if rx_len > EASY_DMA_SIZE {
|
||||
return Err(Error::RxBufferTooLong);
|
||||
}
|
||||
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
|
||||
|
||||
|
@ -11,7 +11,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
pub use pac::spis0::config::ORDER_A as BitOrder;
|
||||
|
||||
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{self, AnyPin, Pin as GpioPin};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
@ -227,11 +227,17 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
|
||||
// Set up the DMA write.
|
||||
let (ptr, len) = slice_ptr_parts(tx);
|
||||
if len > EASY_DMA_SIZE {
|
||||
return Err(Error::TxBufferTooLong);
|
||||
}
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
// Set up the DMA read.
|
||||
let (ptr, len) = slice_ptr_parts_mut(rx);
|
||||
if len > EASY_DMA_SIZE {
|
||||
return Err(Error::RxBufferTooLong);
|
||||
}
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
|
@ -68,7 +68,7 @@ rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
critical-section = "1.1"
|
||||
#stm32-metapac = { version = "15" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e702b4d564bc9e3c8a5c0141a11efdc5f7ee8f24" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e702b4d564bc9e3c8a5c0141a11efdc5f7ee8f24", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -8,6 +8,7 @@
|
||||
#[cfg_attr(adc_f3, path = "f3.rs")]
|
||||
#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")]
|
||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||
#[cfg_attr(adc_l0, path = "v1.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||
@ -36,15 +37,15 @@ pub struct Adc<'d, T: Instance> {
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
@ -59,14 +60,14 @@ pub(crate) mod sealed {
|
||||
|
||||
pub trait Instance: InterruptableInstance {
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
pub trait AdcPin<T: Instance> {
|
||||
#[cfg(any(adc_v1, adc_v2))]
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {}
|
||||
|
||||
fn channel(&self) -> u8;
|
||||
@ -78,10 +79,10 @@ pub(crate) mod sealed {
|
||||
}
|
||||
|
||||
/// ADC instance.
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||
/// 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 {}
|
||||
|
||||
/// ADC pin.
|
||||
@ -96,12 +97,12 @@ foreach_adc!(
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
return crate::pac::$common_inst
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
@ -125,7 +126,7 @@ macro_rules! impl_adc_pin {
|
||||
impl crate::adc::AdcPin<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) {
|
||||
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/// ADC resolution
|
||||
#[allow(missing_docs)]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Resolution {
|
||||
@ -25,7 +25,7 @@ pub enum Resolution {
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
{
|
||||
Self::TwelveBit
|
||||
}
|
||||
@ -46,7 +46,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,7 @@ impl Resolution {
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1))]
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ impl_sample_time!(
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_g0)]
|
||||
#[cfg(any(adc_l0, adc_g0))]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
|
@ -4,6 +4,8 @@ use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
#[cfg(adc_l0)]
|
||||
use stm32_metapac::adc::vals::Ckmode;
|
||||
|
||||
use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
@ -30,8 +32,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
pub struct Vbat;
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
impl AdcPin<ADC> for Vbat {}
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
impl super::sealed::AdcPin<ADC> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
@ -69,9 +76,18 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
// tstab = 14 * 1/fadc
|
||||
delay.delay_us(1);
|
||||
|
||||
// set default PCKL/2 on L0s because HSI is disabled in the default clock config
|
||||
#[cfg(adc_l0)]
|
||||
T::regs().cfgr2().modify(|reg| reg.set_ckmode(Ckmode::PCLK_DIV2));
|
||||
|
||||
// A.7.1 ADC calibration code example
|
||||
T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
|
||||
T::regs().cr().modify(|reg| reg.set_adcal(true));
|
||||
|
||||
#[cfg(adc_l0)]
|
||||
while !T::regs().isr().read().eocal() {}
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
while T::regs().cr().read().adcal() {}
|
||||
|
||||
// A.7.2 ADC enable sequence code example
|
||||
@ -97,6 +113,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
pub fn enable_vbat(&self, _delay: &mut impl DelayUs<u32>) -> Vbat {
|
||||
// SMP must be ≥ 56 ADC clock cycles when using HSI14.
|
||||
//
|
||||
@ -133,6 +150,12 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
|
||||
#[cfg(adc_l0)]
|
||||
pub fn set_ckmode(&mut self, ckmode: Ckmode) {
|
||||
// set ADC clock mode
|
||||
T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode));
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
let channel = pin.channel();
|
||||
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()));
|
||||
}
|
||||
|
||||
/// Write elements directly to the raw buffer.
|
||||
/// This can be used to fill the buffer before starting the DMA transfer.
|
||||
#[allow(dead_code)]
|
||||
pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||
self.ringbuf.write_immediate(buf)
|
||||
}
|
||||
|
||||
/// Write elements to the ring buffer
|
||||
/// Return a tuple of the length written and the length remaining in the buffer
|
||||
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||
|
@ -934,6 +934,13 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
||||
}
|
||||
|
||||
/// Write elements directly to the raw buffer.
|
||||
/// This can be used to fill the buffer before starting the DMA transfer.
|
||||
#[allow(dead_code)]
|
||||
pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||
self.ringbuf.write_immediate(buf)
|
||||
}
|
||||
|
||||
/// Write elements from the ring buffer
|
||||
/// Return a tuple of the length written and the length remaining in the buffer
|
||||
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||
|
@ -37,6 +37,7 @@ pub struct ReadableDmaRingBuffer<'a, W: Word> {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct OverrunError;
|
||||
|
||||
pub trait DmaCtrl {
|
||||
@ -263,6 +264,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
|
||||
self.cap() - dma.get_remaining_transfers()
|
||||
}
|
||||
|
||||
/// Write elements directly to the buffer. This must be done before the DMA is started
|
||||
/// or after the buffer has been cleared using `clear()`.
|
||||
pub fn write_immediate(&mut self, buffer: &[W]) -> Result<(usize, usize), OverrunError> {
|
||||
if self.end != 0 {
|
||||
return Err(OverrunError);
|
||||
}
|
||||
let written = self.copy_from(buffer, 0..self.cap());
|
||||
self.end = written % self.cap();
|
||||
Ok((written, self.cap() - written))
|
||||
}
|
||||
|
||||
/// Write an exact number of elements to the ringbuffer.
|
||||
pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> {
|
||||
let mut written_data = 0;
|
||||
|
@ -658,6 +658,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
#[cfg(stm32h5)]
|
||||
audioclk: None,
|
||||
per: None,
|
||||
i2s_ckin: None,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -477,6 +477,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
pll3_p: None,
|
||||
pll3_q: None,
|
||||
pll3_r: None,
|
||||
iclk: None,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ use crate::pac::timer::vals;
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
#[cfg(feature = "low-power")]
|
||||
use crate::rtc::Rtc;
|
||||
use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance};
|
||||
use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance};
|
||||
use crate::{interrupt, peripherals};
|
||||
|
||||
// NOTE regarding ALARM_COUNT:
|
||||
@ -234,8 +234,8 @@ impl RtcDriver {
|
||||
w.set_ccie(0, true);
|
||||
});
|
||||
|
||||
<T as BasicInstance>::Interrupt::unpend();
|
||||
unsafe { <T as BasicInstance>::Interrupt::enable() };
|
||||
<T as CoreInstance>::Interrupt::unpend();
|
||||
unsafe { <T as CoreInstance>::Interrupt::enable() };
|
||||
|
||||
r.cr1().modify(|w| w.set_cen(true));
|
||||
}
|
||||
@ -251,7 +251,7 @@ impl RtcDriver {
|
||||
// Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT.
|
||||
// Other approaches such as writing all zeros, or RMWing won't work, they can
|
||||
// miss interrupts.
|
||||
r.sr().write_value(regs::SrGp(!sr.0));
|
||||
r.sr().write_value(regs::SrGp16(!sr.0));
|
||||
|
||||
// Overflow
|
||||
if sr.uif() {
|
||||
|
@ -23,7 +23,7 @@ pub struct ComplementaryPwmPin<'d, T, C> {
|
||||
|
||||
macro_rules! complementary_channel_impl {
|
||||
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
||||
impl<'d, T: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> {
|
||||
impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> {
|
||||
#[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
|
||||
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
|
||||
into_ref!(pin);
|
||||
@ -84,14 +84,13 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
||||
|
||||
this.inner.enable_outputs();
|
||||
|
||||
this.inner
|
||||
.set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
|
||||
this.inner
|
||||
.set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
|
||||
this.inner
|
||||
.set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
|
||||
this.inner
|
||||
.set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
|
||||
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
|
||||
.iter()
|
||||
.for_each(|&channel| {
|
||||
this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
|
||||
this.inner.set_output_compare_preload(channel, true);
|
||||
});
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,37 @@
|
||||
//! Timers, PWM, quadrature decoder.
|
||||
//!
|
||||
|
||||
//! Timer inheritance
|
||||
//!
|
||||
|
||||
// sealed:
|
||||
//
|
||||
// Core -------------------------> 1CH -------------------------> 1CH_CMP
|
||||
// | | ^ |
|
||||
// +--> Basic_NoCr2 --> Basic +--> 2CH --> GP16 --> GP32 | +--> 2CH_CMP --> ADV
|
||||
// | | | ^ | | ^ ^
|
||||
// | | +------|--|--------------|-----------+ |
|
||||
// | +--------------------+ +--------------|-----------|---------+
|
||||
// | | | |
|
||||
// | +--------------------------------------|-----------+
|
||||
// +----------------------------------------------------+
|
||||
|
||||
//! ```text
|
||||
//! BasicInstance --> CaptureCompare16bitInstance --+--> ComplementaryCaptureCompare16bitInstance
|
||||
//! |
|
||||
//! +--> CaptureCompare32bitInstance
|
||||
//! ```
|
||||
//!
|
||||
//! Mapping:
|
||||
//!
|
||||
//! | trait | timer |
|
||||
//! | :----------------------------------------: | ------------------------------------------------------------------------------------------------- |
|
||||
//! | [BasicInstance] | Basic Timer |
|
||||
//! | [CaptureCompare16bitInstance] | 1-channel Timer, 2-channel Timer, General Purpose 16-bit Timer |
|
||||
//! | [CaptureCompare32bitInstance] | General Purpose 32-bit Timer |
|
||||
//! | [ComplementaryCaptureCompare16bitInstance] | 1-channel with one complentary Timer, 2-channel with one complentary Timer, Advance Control Timer |
|
||||
|
||||
#[cfg(not(stm32l0))]
|
||||
pub mod complementary_pwm;
|
||||
pub mod qei;
|
||||
pub mod simple_pwm;
|
||||
@ -19,32 +51,32 @@ pub mod low_level {
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
/// Basic 16-bit timer instance.
|
||||
pub trait Basic16bitInstance: RccPeripheral {
|
||||
/// Virtual Core 16-bit timer instance.
|
||||
pub trait CoreInstance: RccPeripheral {
|
||||
/// Interrupt for this timer.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
|
||||
/// Get access to the basic 16bit timer registers.
|
||||
/// Get access to the virutal core 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs() -> crate::pac::timer::TimBasic;
|
||||
fn regs_core() -> crate::pac::timer::TimCore;
|
||||
|
||||
/// Start the timer.
|
||||
fn start(&mut self) {
|
||||
Self::regs().cr1().modify(|r| r.set_cen(true));
|
||||
Self::regs_core().cr1().modify(|r| r.set_cen(true));
|
||||
}
|
||||
|
||||
/// Stop the timer.
|
||||
fn stop(&mut self) {
|
||||
Self::regs().cr1().modify(|r| r.set_cen(false));
|
||||
Self::regs_core().cr1().modify(|r| r.set_cen(false));
|
||||
}
|
||||
|
||||
/// Reset the counter value to 0
|
||||
fn reset(&mut self) {
|
||||
Self::regs().cnt().write(|r| r.set_cnt(0));
|
||||
Self::regs_core().cnt().write(|r| r.set_cnt(0));
|
||||
}
|
||||
|
||||
/// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
|
||||
@ -64,7 +96,7 @@ pub(crate) mod sealed {
|
||||
// the timer counts `0..=arr`, we want it to count `0..divide_by`
|
||||
let arr = unwrap!(u16::try_from(divide_by - 1));
|
||||
|
||||
let regs = Self::regs();
|
||||
let regs = Self::regs_core();
|
||||
regs.psc().write(|r| r.set_psc(psc));
|
||||
regs.arr().write(|r| r.set_arr(arr));
|
||||
|
||||
@ -77,7 +109,7 @@ pub(crate) mod sealed {
|
||||
///
|
||||
/// Returns whether the update interrupt flag was set.
|
||||
fn clear_update_interrupt(&mut self) -> bool {
|
||||
let regs = Self::regs();
|
||||
let regs = Self::regs_core();
|
||||
let sr = regs.sr().read();
|
||||
if sr.uif() {
|
||||
regs.sr().modify(|r| {
|
||||
@ -91,29 +123,19 @@ pub(crate) mod sealed {
|
||||
|
||||
/// Enable/disable the update interrupt.
|
||||
fn enable_update_interrupt(&mut self, enable: bool) {
|
||||
Self::regs().dier().modify(|r| r.set_uie(enable));
|
||||
}
|
||||
|
||||
/// Enable/disable the update dma.
|
||||
fn enable_update_dma(&mut self, enable: bool) {
|
||||
Self::regs().dier().modify(|r| r.set_ude(enable));
|
||||
}
|
||||
|
||||
/// Get the update dma enable/disable state.
|
||||
fn get_update_dma_state(&self) -> bool {
|
||||
Self::regs().dier().read().ude()
|
||||
Self::regs_core().dier().modify(|r| r.set_uie(enable));
|
||||
}
|
||||
|
||||
/// Enable/disable autoreload preload.
|
||||
fn set_autoreload_preload(&mut self, enable: bool) {
|
||||
Self::regs().cr1().modify(|r| r.set_arpe(enable));
|
||||
Self::regs_core().cr1().modify(|r| r.set_arpe(enable));
|
||||
}
|
||||
|
||||
/// Get the timer frequency.
|
||||
fn get_frequency(&self) -> Hertz {
|
||||
let timer_f = Self::frequency();
|
||||
|
||||
let regs = Self::regs();
|
||||
let regs = Self::regs_core();
|
||||
let arr = regs.arr().read().arr();
|
||||
let psc = regs.psc().read().psc();
|
||||
|
||||
@ -121,8 +143,72 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual Basic without CR2 16-bit timer instance.
|
||||
pub trait BasicNoCr2Instance: CoreInstance {
|
||||
/// Get access to the Baisc 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2;
|
||||
|
||||
/// Enable/disable the update dma.
|
||||
fn enable_update_dma(&mut self, enable: bool) {
|
||||
Self::regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable));
|
||||
}
|
||||
|
||||
/// Get the update dma enable/disable state.
|
||||
fn get_update_dma_state(&self) -> bool {
|
||||
Self::regs_basic_no_cr2().dier().read().ude()
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic 16-bit timer instance.
|
||||
pub trait BasicInstance: BasicNoCr2Instance {
|
||||
/// Get access to the Baisc 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_basic() -> crate::pac::timer::TimBasic;
|
||||
}
|
||||
|
||||
/// Gneral-purpose 1 channel 16-bit timer instance.
|
||||
pub trait GeneralPurpose1ChannelInstance: CoreInstance {
|
||||
/// Get access to the general purpose 1 channel 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_1ch() -> crate::pac::timer::Tim1ch;
|
||||
|
||||
/// Set clock divider.
|
||||
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
||||
Self::regs_1ch().cr1().modify(|r| r.set_ckd(ckd));
|
||||
}
|
||||
|
||||
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
|
||||
fn get_max_compare_value(&self) -> u16 {
|
||||
Self::regs_1ch().arr().read().arr()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gneral-purpose 1 channel 16-bit timer instance.
|
||||
pub trait GeneralPurpose2ChannelInstance: GeneralPurpose1ChannelInstance {
|
||||
/// Get access to the general purpose 2 channel 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_2ch() -> crate::pac::timer::Tim2ch;
|
||||
}
|
||||
|
||||
/// Gneral-purpose 16-bit timer instance.
|
||||
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
||||
pub trait GeneralPurpose16bitInstance: BasicInstance + GeneralPurpose2ChannelInstance {
|
||||
/// Get access to the general purpose 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
@ -135,7 +221,7 @@ pub(crate) mod sealed {
|
||||
fn set_counting_mode(&mut self, mode: CountingMode) {
|
||||
let (cms, dir) = mode.into();
|
||||
|
||||
let timer_enabled = Self::regs().cr1().read().cen();
|
||||
let timer_enabled = Self::regs_core().cr1().read().cen();
|
||||
// Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.
|
||||
// Changing direction is discouraged while the timer is running.
|
||||
assert!(!timer_enabled);
|
||||
@ -150,62 +236,8 @@ pub(crate) mod sealed {
|
||||
(cr1.cms(), cr1.dir()).into()
|
||||
}
|
||||
|
||||
/// Set clock divider.
|
||||
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
||||
Self::regs_gp16().cr1().modify(|r| r.set_ckd(ckd));
|
||||
}
|
||||
}
|
||||
|
||||
/// Gneral-purpose 32-bit timer instance.
|
||||
pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
|
||||
/// Get access to the general purpose 32bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_gp32() -> crate::pac::timer::TimGp32;
|
||||
|
||||
/// Set timer frequency.
|
||||
fn set_frequency(&mut self, frequency: Hertz) {
|
||||
let f = frequency.0;
|
||||
assert!(f > 0);
|
||||
let timer_f = Self::frequency().0;
|
||||
let pclk_ticks_per_timer_period = (timer_f / f) as u64;
|
||||
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
|
||||
let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into());
|
||||
|
||||
let regs = Self::regs_gp32();
|
||||
regs.psc().write(|r| r.set_psc(psc));
|
||||
regs.arr().write(|r| r.set_arr(arr));
|
||||
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
|
||||
regs.egr().write(|r| r.set_ug(true));
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
||||
}
|
||||
|
||||
/// Get timer frequency.
|
||||
fn get_frequency(&self) -> Hertz {
|
||||
let timer_f = Self::frequency();
|
||||
|
||||
let regs = Self::regs_gp32();
|
||||
let arr = regs.arr().read().arr();
|
||||
let psc = regs.psc().read().psc();
|
||||
|
||||
timer_f / arr / (psc + 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Advanced control timer instance.
|
||||
pub trait AdvancedControlInstance: GeneralPurpose16bitInstance {
|
||||
/// Get access to the advanced timer registers.
|
||||
fn regs_advanced() -> crate::pac::timer::TimAdv;
|
||||
}
|
||||
|
||||
/// Capture/Compare 16-bit timer instance.
|
||||
pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance {
|
||||
/// Set input capture filter.
|
||||
fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::Icf) {
|
||||
fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::FilterValue) {
|
||||
let raw_channel = channel.index();
|
||||
Self::regs_gp16()
|
||||
.ccmr_input(raw_channel / 2)
|
||||
@ -256,14 +288,11 @@ pub(crate) mod sealed {
|
||||
});
|
||||
}
|
||||
|
||||
/// Enable timer outputs.
|
||||
fn enable_outputs(&mut self);
|
||||
|
||||
/// Set output compare mode.
|
||||
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
|
||||
let r = Self::regs_gp16();
|
||||
let raw_channel: usize = channel.index();
|
||||
r.ccmr_output(raw_channel / 2)
|
||||
Self::regs_gp16()
|
||||
.ccmr_output(raw_channel / 2)
|
||||
.modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
|
||||
}
|
||||
|
||||
@ -294,11 +323,6 @@ pub(crate) mod sealed {
|
||||
Self::regs_gp16().ccr(channel.index()).read().ccr()
|
||||
}
|
||||
|
||||
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
|
||||
fn get_max_compare_value(&self) -> u16 {
|
||||
Self::regs_gp16().arr().read().arr()
|
||||
}
|
||||
|
||||
/// Get compare value for a channel.
|
||||
fn get_compare_value(&self, channel: Channel) -> u16 {
|
||||
Self::regs_gp16().ccr(channel.index()).read().ccr()
|
||||
@ -333,35 +357,46 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// Capture/Compare 16-bit timer instance with complementary pin support.
|
||||
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance {
|
||||
/// Set complementary output polarity.
|
||||
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||
Self::regs_advanced()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
|
||||
#[cfg(not(stm32l0))]
|
||||
/// Gneral-purpose 32-bit timer instance.
|
||||
pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
|
||||
/// Get access to the general purpose 32bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_gp32() -> crate::pac::timer::TimGp32;
|
||||
|
||||
/// Set timer frequency.
|
||||
fn set_frequency(&mut self, frequency: Hertz) {
|
||||
let f = frequency.0;
|
||||
assert!(f > 0);
|
||||
let timer_f = Self::frequency().0;
|
||||
let pclk_ticks_per_timer_period = (timer_f / f) as u64;
|
||||
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
|
||||
let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into());
|
||||
|
||||
let regs = Self::regs_gp32();
|
||||
regs.psc().write(|r| r.set_psc(psc));
|
||||
regs.arr().write(|r| r.set_arr(arr));
|
||||
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
|
||||
regs.egr().write(|r| r.set_ug(true));
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
||||
}
|
||||
|
||||
/// Set clock divider for the dead time.
|
||||
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
|
||||
Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
|
||||
/// Get timer frequency.
|
||||
fn get_frequency(&self) -> Hertz {
|
||||
let timer_f = Self::frequency();
|
||||
|
||||
let regs = Self::regs_gp32();
|
||||
let arr = regs.arr().read().arr();
|
||||
let psc = regs.psc().read().psc();
|
||||
|
||||
timer_f / arr / (psc + 1)
|
||||
}
|
||||
|
||||
/// Set dead time, as a fraction of the max duty value.
|
||||
fn set_dead_time_value(&mut self, value: u8) {
|
||||
Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
|
||||
}
|
||||
|
||||
/// Enable/disable a complementary channel.
|
||||
fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
|
||||
Self::regs_advanced()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccne(channel.index(), enable));
|
||||
}
|
||||
}
|
||||
|
||||
/// Capture/Compare 32-bit timer instance.
|
||||
pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance + CaptureCompare16bitInstance {
|
||||
/// Set comapre value for a channel.
|
||||
fn set_compare_value(&mut self, channel: Channel, value: u32) {
|
||||
Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value));
|
||||
@ -382,6 +417,70 @@ pub(crate) mod sealed {
|
||||
Self::regs_gp32().ccr(channel.index()).read().ccr()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(stm32l0))]
|
||||
/// Gneral-purpose 1 channel with one complementary 16-bit timer instance.
|
||||
pub trait GeneralPurpose1ChannelComplementaryInstance: BasicNoCr2Instance + GeneralPurpose1ChannelInstance {
|
||||
/// Get access to the general purpose 1 channel with one complementary 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp;
|
||||
|
||||
/// Set clock divider for the dead time.
|
||||
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
|
||||
Self::regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));
|
||||
}
|
||||
|
||||
/// Set dead time, as a fraction of the max duty value.
|
||||
fn set_dead_time_value(&mut self, value: u8) {
|
||||
Self::regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
|
||||
}
|
||||
|
||||
/// Enable timer outputs.
|
||||
fn enable_outputs(&mut self) {
|
||||
Self::regs_1ch_cmp().bdtr().modify(|w| w.set_moe(true));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(stm32l0))]
|
||||
/// Gneral-purpose 2 channel with one complementary 16-bit timer instance.
|
||||
pub trait GeneralPurpose2ChannelComplementaryInstance:
|
||||
BasicInstance + GeneralPurpose2ChannelInstance + GeneralPurpose1ChannelComplementaryInstance
|
||||
{
|
||||
/// Get access to the general purpose 2 channel with one complementary 16bit timer registers.
|
||||
///
|
||||
/// Note: This works even if the timer is more capable, because registers
|
||||
/// for the less capable timers are a subset. This allows writing a driver
|
||||
/// for a given set of capabilities, and having it transparently work with
|
||||
/// more capable timers.
|
||||
fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp;
|
||||
}
|
||||
|
||||
#[cfg(not(stm32l0))]
|
||||
/// Advanced control timer instance.
|
||||
pub trait AdvancedControlInstance:
|
||||
GeneralPurpose2ChannelComplementaryInstance + GeneralPurpose16bitInstance
|
||||
{
|
||||
/// Get access to the advanced timer registers.
|
||||
fn regs_advanced() -> crate::pac::timer::TimAdv;
|
||||
|
||||
/// Set complementary output polarity.
|
||||
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||
Self::regs_advanced()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
|
||||
}
|
||||
|
||||
/// Enable/disable a complementary channel.
|
||||
fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
|
||||
Self::regs_advanced()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccne(channel.index(), enable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Timer channel.
|
||||
@ -572,61 +671,92 @@ impl From<OutputPolarity> for bool {
|
||||
}
|
||||
|
||||
/// Basic 16-bit timer instance.
|
||||
pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {}
|
||||
pub trait BasicInstance: sealed::BasicInstance + sealed::BasicNoCr2Instance + sealed::CoreInstance + 'static {}
|
||||
|
||||
/// Gneral-purpose 16-bit timer instance.
|
||||
pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + Basic16bitInstance + 'static {}
|
||||
|
||||
/// Gneral-purpose 32-bit timer instance.
|
||||
pub trait GeneralPurpose32bitInstance:
|
||||
sealed::GeneralPurpose32bitInstance + GeneralPurpose16bitInstance + 'static
|
||||
{
|
||||
}
|
||||
|
||||
/// Advanced control timer instance.
|
||||
pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + GeneralPurpose16bitInstance + 'static {}
|
||||
|
||||
/// Capture/Compare 16-bit timer instance.
|
||||
// It's just a General-purpose 16-bit timer instance.
|
||||
/// Capture Compare timer instance.
|
||||
pub trait CaptureCompare16bitInstance:
|
||||
sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static
|
||||
BasicInstance
|
||||
+ sealed::GeneralPurpose2ChannelInstance
|
||||
+ sealed::GeneralPurpose1ChannelInstance
|
||||
+ sealed::GeneralPurpose16bitInstance
|
||||
+ 'static
|
||||
{
|
||||
}
|
||||
|
||||
/// Capture/Compare 16-bit timer instance with complementary pin support.
|
||||
pub trait ComplementaryCaptureCompare16bitInstance:
|
||||
sealed::ComplementaryCaptureCompare16bitInstance + CaptureCompare16bitInstance + AdvancedControlInstance + 'static
|
||||
{
|
||||
}
|
||||
|
||||
/// Capture/Compare 32-bit timer instance.
|
||||
#[cfg(not(stm32l0))]
|
||||
// It's just a General-purpose 32-bit timer instance.
|
||||
/// Capture Compare 32-bit timer instance.
|
||||
pub trait CaptureCompare32bitInstance:
|
||||
sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static
|
||||
CaptureCompare16bitInstance + sealed::GeneralPurpose32bitInstance + 'static
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(not(stm32l0))]
|
||||
// It's just a Advanced Control timer instance.
|
||||
/// Complementary Capture Compare 32-bit timer instance.
|
||||
pub trait ComplementaryCaptureCompare16bitInstance:
|
||||
CaptureCompare16bitInstance
|
||||
+ sealed::GeneralPurpose1ChannelComplementaryInstance
|
||||
+ sealed::GeneralPurpose2ChannelComplementaryInstance
|
||||
+ sealed::AdvancedControlInstance
|
||||
+ 'static
|
||||
{
|
||||
}
|
||||
|
||||
pin_trait!(Channel1Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel2Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel3Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel4Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance);
|
||||
pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInputPin, CaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(stm32l0))] {
|
||||
pin_trait!(Channel1ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
|
||||
pin_trait!(Channel2ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
|
||||
pin_trait!(Channel3ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
|
||||
pin_trait!(Channel4ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
|
||||
|
||||
pin_trait!(BreakInputPin, ComplementaryCaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInput2Pin, ComplementaryCaptureCompare16bitInstance);
|
||||
|
||||
pin_trait!(BreakInputComparator1Pin, ComplementaryCaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInputComparator2Pin, ComplementaryCaptureCompare16bitInstance);
|
||||
|
||||
pin_trait!(BreakInput2Comparator1Pin, ComplementaryCaptureCompare16bitInstance);
|
||||
pin_trait!(BreakInput2Comparator2Pin, ComplementaryCaptureCompare16bitInstance);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_basic_16bit_timer {
|
||||
macro_rules! impl_core_timer {
|
||||
($inst:ident, $irq:ident) => {
|
||||
impl sealed::Basic16bitInstance for crate::peripherals::$inst {
|
||||
impl sealed::CoreInstance for crate::peripherals::$inst {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
|
||||
fn regs() -> crate::pac::timer::TimBasic {
|
||||
fn regs_core() -> crate::pac::timer::TimCore {
|
||||
unsafe { crate::pac::timer::TimCore::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_basic_no_cr2_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::BasicNoCr2Instance for crate::peripherals::$inst {
|
||||
fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2 {
|
||||
unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_basic_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::BasicInstance for crate::peripherals::$inst {
|
||||
fn regs_basic() -> crate::pac::timer::TimBasic {
|
||||
unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
@ -634,7 +764,40 @@ macro_rules! impl_basic_16bit_timer {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_32bit_timer {
|
||||
macro_rules! impl_1ch_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::GeneralPurpose1ChannelInstance for crate::peripherals::$inst {
|
||||
fn regs_1ch() -> crate::pac::timer::Tim1ch {
|
||||
unsafe { crate::pac::timer::Tim1ch::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_2ch_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::GeneralPurpose2ChannelInstance for crate::peripherals::$inst {
|
||||
fn regs_2ch() -> crate::pac::timer::Tim2ch {
|
||||
unsafe { crate::pac::timer::Tim2ch::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_gp16_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
|
||||
fn regs_gp16() -> crate::pac::timer::TimGp16 {
|
||||
unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_gp32_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst {
|
||||
fn regs_gp32() -> crate::pac::timer::TimGp32 {
|
||||
@ -645,83 +808,144 @@ macro_rules! impl_32bit_timer {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_compare_capable_16bit {
|
||||
macro_rules! impl_1ch_cmp_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst {
|
||||
fn enable_outputs(&mut self) {}
|
||||
impl sealed::GeneralPurpose1ChannelComplementaryInstance for crate::peripherals::$inst {
|
||||
fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp {
|
||||
unsafe { crate::pac::timer::Tim1chCmp::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_2ch_cmp_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::GeneralPurpose2ChannelComplementaryInstance for crate::peripherals::$inst {
|
||||
fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp {
|
||||
unsafe { crate::pac::timer::Tim2chCmp::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_adv_timer {
|
||||
($inst:ident) => {
|
||||
impl sealed::AdvancedControlInstance for crate::peripherals::$inst {
|
||||
fn regs_advanced() -> crate::pac::timer::TimAdv {
|
||||
unsafe { crate::pac::timer::TimAdv::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
foreach_interrupt! {
|
||||
($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
|
||||
impl_basic_16bit_timer!($inst, $irq);
|
||||
impl Basic16bitInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
|
||||
impl_basic_16bit_timer!($inst, $irq);
|
||||
impl_compare_capable_16bit!($inst);
|
||||
impl Basic16bitInstance for crate::peripherals::$inst {}
|
||||
impl GeneralPurpose16bitInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
|
||||
impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
|
||||
fn regs_gp16() -> crate::pac::timer::TimGp16 {
|
||||
crate::pac::$inst
|
||||
}
|
||||
}
|
||||
($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
|
||||
($inst:ident, timer, TIM_1CH, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
|
||||
|
||||
($inst:ident, timer, TIM_2CH, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
|
||||
($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
|
||||
($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
|
||||
impl_basic_16bit_timer!($inst, $irq);
|
||||
impl_32bit_timer!($inst);
|
||||
impl_compare_capable_16bit!($inst);
|
||||
impl Basic16bitInstance for crate::peripherals::$inst {}
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl_gp32_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare32bitInstance for crate::peripherals::$inst {}
|
||||
impl GeneralPurpose16bitInstance for crate::peripherals::$inst {}
|
||||
impl GeneralPurpose32bitInstance for crate::peripherals::$inst {}
|
||||
impl sealed::CaptureCompare32bitInstance for crate::peripherals::$inst {}
|
||||
|
||||
impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
|
||||
fn regs_gp16() -> crate::pac::timer::TimGp16 {
|
||||
unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
|
||||
impl_basic_16bit_timer!($inst, $irq);
|
||||
|
||||
impl Basic16bitInstance for crate::peripherals::$inst {}
|
||||
impl GeneralPurpose16bitInstance for crate::peripherals::$inst {}
|
||||
($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl_1ch_cmp_timer!($inst);
|
||||
impl_2ch_cmp_timer!($inst);
|
||||
impl_adv_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
impl AdvancedControlInstance for crate::peripherals::$inst {}
|
||||
impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst {
|
||||
fn enable_outputs(&mut self) {
|
||||
use crate::timer::sealed::AdvancedControlInstance;
|
||||
let r = Self::regs_advanced();
|
||||
r.bdtr().modify(|w| w.set_moe(true));
|
||||
}
|
||||
}
|
||||
impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
|
||||
fn regs_gp16() -> crate::pac::timer::TimGp16 {
|
||||
unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
impl sealed::AdvancedControlInstance for crate::peripherals::$inst {
|
||||
fn regs_advanced() -> crate::pac::timer::TimAdv {
|
||||
crate::pac::$inst
|
||||
}
|
||||
}
|
||||
|
||||
($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl_1ch_cmp_timer!($inst);
|
||||
impl_2ch_cmp_timer!($inst);
|
||||
impl_adv_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
|
||||
|
||||
($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
|
||||
impl_core_timer!($inst, $irq);
|
||||
impl_basic_no_cr2_timer!($inst);
|
||||
impl_basic_timer!($inst);
|
||||
impl_1ch_timer!($inst);
|
||||
impl_2ch_timer!($inst);
|
||||
impl_gp16_timer!($inst);
|
||||
impl_1ch_cmp_timer!($inst);
|
||||
impl_2ch_cmp_timer!($inst);
|
||||
impl_adv_timer!($inst);
|
||||
impl BasicInstance for crate::peripherals::$inst {}
|
||||
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
|
||||
};
|
||||
}
|
||||
|
||||
// Update Event trigger DMA for every timer
|
||||
dma_trait!(UpDma, Basic16bitInstance);
|
||||
dma_trait!(UpDma, BasicInstance);
|
||||
|
||||
dma_trait!(Ch1Dma, CaptureCompare16bitInstance);
|
||||
dma_trait!(Ch2Dma, CaptureCompare16bitInstance);
|
||||
|
@ -84,13 +84,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
||||
this.set_frequency(freq);
|
||||
this.inner.start();
|
||||
|
||||
this.inner.enable_outputs();
|
||||
|
||||
[Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
|
||||
.iter()
|
||||
.for_each(|&channel| {
|
||||
this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);
|
||||
this.inner.set_output_compare_preload(channel, true)
|
||||
|
||||
this.inner.set_output_compare_preload(channel, true);
|
||||
});
|
||||
|
||||
this
|
||||
@ -202,7 +201,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
||||
&mut dma,
|
||||
req,
|
||||
duty,
|
||||
T::regs_gp16().ccr(channel.index()).as_ptr() as *mut _,
|
||||
T::regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
|
||||
dma_transfer_option,
|
||||
)
|
||||
.await
|
||||
|
@ -50,7 +50,7 @@ async fn main(_spawner: Spawner) {
|
||||
let nvmc = Nvmc::new(p.NVMC);
|
||||
let nvmc = Mutex::new(BlockingAsync::new(nvmc));
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc);
|
||||
let mut magic = [0; 4];
|
||||
let mut updater = FirmwareUpdater::new(config, &mut magic);
|
||||
loop {
|
||||
|
@ -36,7 +36,7 @@ async fn main(_s: Spawner) {
|
||||
let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);
|
||||
let flash = Mutex::new(RefCell::new(flash));
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
|
||||
let mut aligned = AlignedBuffer([0; 1]);
|
||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
|
||||
|
||||
|
@ -3,8 +3,8 @@ MEMORY
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||
DFU : ORIGIN = 0x08018000, LENGTH = 66K
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||
button.wait_for_falling_edge().await;
|
||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);
|
||||
let writer = updater.prepare_update().unwrap();
|
||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);
|
||||
let writer = updater.prepare_update().unwrap();
|
||||
|
@ -3,8 +3,8 @@ MEMORY
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||
DFU : ORIGIN = 0x08018000, LENGTH = 66K
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||
button.wait_for_falling_edge().await;
|
||||
|
@ -3,8 +3,8 @@ MEMORY
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 46K
|
||||
DFU : ORIGIN = 0x08013800, LENGTH = 54K
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||
button.wait_for_falling_edge().await;
|
||||
|
@ -3,8 +3,8 @@ MEMORY
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||
DFU : ORIGIN = 0x08018000, LENGTH = 68K
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||
button.wait_for_falling_edge().await;
|
||||
|
@ -1,29 +1,9 @@
|
||||
# Examples using bootloader
|
||||
|
||||
Example for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a'
|
||||
which allows you to press a button to start the DFU process, and 'b' which is the updated
|
||||
application.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* `cargo-binutils`
|
||||
* `cargo-flash`
|
||||
* `embassy-boot-stm32`
|
||||
Example for STM32WB demonstrating the USB DFU application.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
# Flash bootloader
|
||||
cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32wl55jc-cm4 --chip STM32WLE5JCIx
|
||||
# Build 'b'
|
||||
cargo build --release --bin b
|
||||
# Generate binary for 'b'
|
||||
cargo objcopy --release --bin b -- -O binary b.bin
|
||||
```
|
||||
|
||||
# Flash `a` (which includes b.bin)
|
||||
|
||||
```
|
||||
cargo flash --release --bin a --chip STM32WLE5JCIx
|
||||
cargo flash --release --chip STM32WB55RGVx
|
||||
```
|
||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
||||
let flash = Flash::new_blocking(p.FLASH);
|
||||
let flash = Mutex::new(RefCell::new(flash));
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0);
|
||||
firmware_state.mark_booted().expect("Failed to mark booted");
|
||||
|
@ -3,8 +3,8 @@ MEMORY
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
||||
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
||||
FLASH : ORIGIN = 0x08008000, LENGTH = 64K
|
||||
DFU : ORIGIN = 0x08018000, LENGTH = 68K
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
||||
let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
|
||||
led.set_high();
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash);
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let mut updater = FirmwareUpdater::new(config, &mut magic.0);
|
||||
button.wait_for_falling_edge().await;
|
||||
|
@ -31,7 +31,7 @@ fn main() -> ! {
|
||||
let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);
|
||||
let flash = Mutex::new(RefCell::new(flash));
|
||||
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
|
||||
let active_offset = config.active.offset();
|
||||
let bl: BootLoader = BootLoader::prepare(config);
|
||||
|
||||
|
@ -27,7 +27,7 @@ fn main() -> ! {
|
||||
let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8));
|
||||
let flash = Mutex::new(RefCell::new(flash));
|
||||
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
|
||||
let active_offset = config.active.offset();
|
||||
let bl: BootLoader = BootLoader::prepare(config);
|
||||
|
||||
|
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 flash = Mutex::new(RefCell::new(layout.bank1_region));
|
||||
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
|
||||
let active_offset = config.active.offset();
|
||||
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
||||
|
||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
|
||||
embassy-stm32 = { path = "../../../../embassy-stm32", features = ["stm32wb55rg"] }
|
||||
embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
|
||||
embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
embassy-sync = { version = "0.5.0", path = "../../../../embassy-sync" }
|
||||
|
@ -7,5 +7,5 @@ The bootloader uses `embassy-boot` to interact with the flash.
|
||||
Flash the bootloader
|
||||
|
||||
```
|
||||
cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx
|
||||
cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx
|
||||
```
|
||||
|
@ -35,7 +35,7 @@ fn main() -> ! {
|
||||
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
||||
let flash = Mutex::new(RefCell::new(layout.bank1_region));
|
||||
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
|
||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
|
||||
let active_offset = config.active.offset();
|
||||
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
||||
if bl.state == State::DfuDetach {
|
||||
@ -45,7 +45,7 @@ fn main() -> ! {
|
||||
config.product = Some("USB-DFU Bootloader example");
|
||||
config.serial_number = Some("1235678");
|
||||
|
||||
let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||
let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
|
||||
let mut buffer = AlignedBuffer([0; WRITE_SIZE]);
|
||||
let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);
|
||||
|
||||
|
@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms;
|
||||
use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
|
||||
use embassy_stm32::rcc::low_level::RccPeripheral;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||
use embassy_stm32::timer::low_level::BasicInstance;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -75,9 +75,9 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||
dac.enable();
|
||||
|
||||
TIM6::enable_and_reset();
|
||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM6::regs().cr1().modify(|w| {
|
||||
TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM6::regs_basic().cr1().modify(|w| {
|
||||
w.set_opm(false);
|
||||
w.set_cen(true);
|
||||
});
|
||||
@ -112,9 +112,9 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||
}
|
||||
|
||||
TIM7::enable_and_reset();
|
||||
TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM7::regs().cr1().modify(|w| {
|
||||
TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM7::regs_basic().cr1().modify(|w| {
|
||||
w.set_opm(false);
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
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::rcc::low_level::RccPeripheral;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
|
||||
use embassy_stm32::timer::low_level::BasicInstance;
|
||||
use micromath::F32Ext;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -46,9 +46,9 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
|
||||
dac.enable();
|
||||
|
||||
TIM6::enable_and_reset();
|
||||
TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM6::regs().cr1().modify(|w| {
|
||||
TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM6::regs_basic().cr1().modify(|w| {
|
||||
w.set_opm(false);
|
||||
w.set_cen(true);
|
||||
});
|
||||
@ -83,9 +83,9 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
|
||||
}
|
||||
|
||||
TIM7::enable_and_reset();
|
||||
TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM7::regs().cr1().modify(|w| {
|
||||
TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
|
||||
TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
|
||||
TIM7::regs_basic().cr1().modify(|w| {
|
||||
w.set_opm(false);
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
@ -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