Compare commits
44 Commits
ci-cache-t
...
james/fix-
Author | SHA1 | Date | |
---|---|---|---|
2b497c1e57 | |||
88e77c733c | |||
2a542bc143 | |||
c0cfd68c0c | |||
80c9d04bbd | |||
9959c8c3e3 | |||
b857334f92 | |||
a2d4bab2f8 | |||
a5379e708c | |||
2a7a44477e | |||
f6bc96dfbd | |||
ccf602b333 | |||
3568e4a5ff | |||
858987263b | |||
b966f55883 | |||
ea1e1973eb | |||
560e728132 | |||
c17fee27bb | |||
a8d0da91dc | |||
e5e85ba02b | |||
77e372e842 | |||
a165d73eed | |||
df0f41c41c | |||
98481c20fe | |||
5ec2fbe3a2 | |||
33e8943e5b | |||
9f9f6e75bb | |||
cbc8ccc51e | |||
485765320a | |||
27d054aa68 | |||
a34abd849f | |||
138ed87b95 | |||
d81395fab3 | |||
ef692c5141 | |||
9cc5d8ac89 | |||
c1438fe87b | |||
e27e00f628 | |||
b60b3f4eb8 | |||
702d2a1a19 | |||
c2942f2727 | |||
6bf70e14fb | |||
2afec225e3 | |||
976a7ae22a | |||
d596a1091d |
2
ci.sh
2
ci.sh
@ -173,10 +173,12 @@ cargo batch \
|
|||||||
--- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \
|
--- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \
|
||||||
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \
|
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \
|
||||||
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
|
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
|
||||||
|
--- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wb-dfu \
|
||||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||||
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
|
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||||
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||||
|
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabihf \
|
||||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||||
|
@ -23,7 +23,7 @@ cortex-m = "0.7.6"
|
|||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
||||||
|
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.3" }
|
||||||
num_enum = { version = "0.5.7", default-features = false }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
|
@ -9,7 +9,7 @@ use {defmt_rtt as _, panic_probe as _};
|
|||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
let mut led = Output::new(p.PB14, Level::High, Speed::VeryHigh);
|
let mut led = Output::new(p.PB14, Level::High, Speed::VeryHigh);
|
||||||
let button = Input::new(p.PC13, Pull::Up);
|
let mut button = Input::new(p.PC13, Pull::Up);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_low() {
|
if button.is_low() {
|
||||||
|
@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
|
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
|
||||||
|
|
||||||
use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||||
|
|
||||||
/// Errors returned by bootloader
|
/// Errors returned by bootloader
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
@ -371,6 +371,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
|||||||
|
|
||||||
if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
|
if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
|
||||||
Ok(State::Swap)
|
Ok(State::Swap)
|
||||||
|
} else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) {
|
||||||
|
Ok(State::DfuDetach)
|
||||||
} else {
|
} else {
|
||||||
Ok(State::Boot)
|
Ok(State::Boot)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|||||||
use embedded_storage_async::nor_flash::NorFlash;
|
use embedded_storage_async::nor_flash::NorFlash;
|
||||||
|
|
||||||
use super::FirmwareUpdaterConfig;
|
use super::FirmwareUpdaterConfig;
|
||||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||||
|
|
||||||
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
@ -161,6 +161,12 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
|||||||
self.state.mark_updated().await
|
self.state.mark_updated().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark to trigger USB DFU on next boot.
|
||||||
|
pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.state.verify_booted().await?;
|
||||||
|
self.state.mark_dfu().await
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.state.mark_booted().await
|
self.state.mark_booted().await
|
||||||
@ -207,6 +213,16 @@ pub struct FirmwareState<'d, STATE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||||
|
/// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||||
|
/// and written to.
|
||||||
|
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
||||||
|
Self::new(config.state, aligned)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -247,6 +263,11 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
|||||||
self.set_magic(SWAP_MAGIC).await
|
self.set_magic(SWAP_MAGIC).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark to trigger USB DFU on next boot.
|
||||||
|
pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.set_magic(DFU_DETACH_MAGIC).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.set_magic(BOOT_MAGIC).await
|
self.set_magic(BOOT_MAGIC).await
|
||||||
|
@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
|||||||
use embedded_storage::nor_flash::NorFlash;
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
use super::FirmwareUpdaterConfig;
|
use super::FirmwareUpdaterConfig;
|
||||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||||
|
|
||||||
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
@ -168,6 +168,12 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
|||||||
self.state.mark_updated()
|
self.state.mark_updated()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark to trigger USB DFU device on next boot.
|
||||||
|
pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.state.verify_booted()?;
|
||||||
|
self.state.mark_dfu()
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.state.mark_booted()
|
self.state.mark_booted()
|
||||||
@ -213,6 +219,16 @@ pub struct BlockingFirmwareState<'d, STATE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||||
|
/// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||||
|
/// and written to.
|
||||||
|
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
||||||
|
Self::new(config.state, aligned)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -226,7 +242,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
|||||||
|
|
||||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||||
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
if self.get_state()? == State::Boot {
|
if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(FirmwareUpdaterError::BadState)
|
Err(FirmwareUpdaterError::BadState)
|
||||||
@ -243,6 +259,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
|||||||
|
|
||||||
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||||
Ok(State::Swap)
|
Ok(State::Swap)
|
||||||
|
} else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) {
|
||||||
|
Ok(State::DfuDetach)
|
||||||
} else {
|
} else {
|
||||||
Ok(State::Boot)
|
Ok(State::Boot)
|
||||||
}
|
}
|
||||||
@ -253,6 +271,11 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
|||||||
self.set_magic(SWAP_MAGIC)
|
self.set_magic(SWAP_MAGIC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark to trigger USB DFU on next boot.
|
||||||
|
pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.set_magic(DFU_DETACH_MAGIC)
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
self.set_magic(BOOT_MAGIC)
|
self.set_magic(BOOT_MAGIC)
|
||||||
|
@ -23,6 +23,7 @@ pub use firmware_updater::{
|
|||||||
|
|
||||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
||||||
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
||||||
|
pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;
|
||||||
|
|
||||||
/// The state of the bootloader after running prepare.
|
/// The state of the bootloader after running prepare.
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
@ -32,6 +33,8 @@ pub enum State {
|
|||||||
Boot,
|
Boot,
|
||||||
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
|
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
|
||||||
Swap,
|
Swap,
|
||||||
|
/// Application has received a request to reboot into DFU mode to apply an update.
|
||||||
|
DfuDetach,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
|
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
|
||||||
|
@ -10,7 +10,10 @@ pub use embassy_boot::{
|
|||||||
use embedded_storage::nor_flash::NorFlash;
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
/// A bootloader for STM32 devices.
|
/// A bootloader for STM32 devices.
|
||||||
pub struct BootLoader;
|
pub struct BootLoader {
|
||||||
|
/// The reported state of the bootloader after preparing for boot
|
||||||
|
pub state: State,
|
||||||
|
}
|
||||||
|
|
||||||
impl BootLoader {
|
impl BootLoader {
|
||||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
||||||
@ -19,8 +22,8 @@ impl BootLoader {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||||
let mut boot = embassy_boot::BootLoader::new(config);
|
let mut boot = embassy_boot::BootLoader::new(config);
|
||||||
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
let state = boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
||||||
Self
|
Self { state }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boots the application.
|
/// Boots the application.
|
||||||
|
@ -23,8 +23,8 @@ embassy-time = { version = "0.2", path = "../embassy-time", optional = true }
|
|||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.1"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.1" }
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
@ -13,16 +13,16 @@ edition = "2021"
|
|||||||
heapless = "0.8"
|
heapless = "0.8"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4", default-features = false, optional = true }
|
log = { version = "0.4", default-features = false, optional = true }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] }
|
embedded-hal-bus = { version = "=0.1.0-rc.3", features = ["async"] }
|
||||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.2", path = "../embassy-time" }
|
embassy-time = { version = "0.2", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
bitfield = "0.14.0"
|
bitfield = "0.14.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
embedded-hal-mock = { git = "https://github.com/Dirbaio/embedded-hal-mock", rev = "c5c4dca18e043e6386aee02173f61a65fea3981e", features = ["embedded-hal-async", "eh1"] }
|
embedded-hal-mock = { version = "0.10.0-rc.4", features = ["embedded-hal-async", "eh1"] }
|
||||||
crc = "3.0.1"
|
crc = "3.0.1"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
critical-section = { version = "1.1.2", features = ["std"] }
|
critical-section = { version = "1.1.2", features = ["std"] }
|
||||||
|
@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-rc.2" }
|
embedded-hal = { version = "1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
embassy-time = { version = "0.2", path = "../embassy-time" }
|
embassy-time = { version = "0.2", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
@ -12,8 +12,8 @@ embassy-sync = { version = "0.5.0", path = "../embassy-sync"}
|
|||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
||||||
|
|
||||||
embedded-hal = { version = "1.0.0-rc.2" }
|
embedded-hal = { version = "1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
|
|
||||||
noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", default-features = false, features = ["derive"] }
|
noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", default-features = false, features = ["derive"] }
|
||||||
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||||
|
@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-rc.2" }
|
embedded-hal = { version = "1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.2", path = "../embassy-time" }
|
embassy-time = { version = "0.2", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
@ -64,6 +64,11 @@ nfc-pins-as-gpio = []
|
|||||||
# nrf52820, nrf52833, nrf52840: P0_18
|
# nrf52820, nrf52833, nrf52840: P0_18
|
||||||
reset-pin-as-gpio = []
|
reset-pin-as-gpio = []
|
||||||
|
|
||||||
|
# Implements the MultiwriteNorFlash trait for QSPI. Should only be enabled if your external
|
||||||
|
# flash supports the semantics described in
|
||||||
|
# https://docs.rs/embedded-storage/0.3.1/embedded_storage/nor_flash/trait.MultiwriteNorFlash.html
|
||||||
|
qspi-multiwrite-flash = []
|
||||||
|
|
||||||
# Features starting with `_` are for internal use only. They're not intended
|
# Features starting with `_` are for internal use only. They're not intended
|
||||||
# to be enabled by other crates, and are not covered by semver guarantees.
|
# to be enabled by other crates, and are not covered by semver guarantees.
|
||||||
|
|
||||||
@ -94,8 +99,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
|||||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
|
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-io = { version = "0.6.0" }
|
embedded-io = { version = "0.6.0" }
|
||||||
embedded-io-async = { version = "0.6.1" }
|
embedded-io-async = { version = "0.6.1" }
|
||||||
|
|
||||||
@ -120,4 +125,3 @@ nrf52840-pac = { version = "0.12.0", optional = true }
|
|||||||
nrf5340-app-pac = { version = "0.12.0", optional = true }
|
nrf5340-app-pac = { version = "0.12.0", optional = true }
|
||||||
nrf5340-net-pac = { version = "0.12.0", optional = true }
|
nrf5340-net-pac = { version = "0.12.0", optional = true }
|
||||||
nrf9160-pac = { version = "0.12.0", optional = true }
|
nrf9160-pac = { version = "0.12.0", optional = true }
|
||||||
|
|
||||||
|
@ -50,21 +50,21 @@ impl<'d, T: Pin> Input<'d, T> {
|
|||||||
Self { pin }
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if current pin level is high.
|
/// Get whether the pin input level is high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
self.pin.is_high()
|
self.pin.is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if current pin level is low.
|
/// Get whether the pin input level is low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
self.pin.is_low()
|
self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns current pin level
|
/// Get the pin input level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.pin.get_level()
|
self.pin.get_level()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,21 +158,21 @@ impl<'d, T: Pin> Output<'d, T> {
|
|||||||
self.pin.set_level(level)
|
self.pin.set_level(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as high?
|
/// Get whether the output level is set to high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
self.pin.is_set_high()
|
self.pin.is_set_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Get whether the output level is set to low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
self.pin.is_set_low()
|
self.pin.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// Get the current output level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.pin.get_output_level()
|
self.pin.get_output_level()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,21 +275,26 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
self.pin.conf().reset();
|
self.pin.conf().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if current pin level is high.
|
/// Get whether the pin input level is high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
!self.is_low()
|
!self.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if current pin level is low.
|
/// Get whether the pin input level is low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
|
self.ref_is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn ref_is_low(&self) -> bool {
|
||||||
self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0
|
self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns current pin level
|
/// Get the pin input level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.is_high().into()
|
self.is_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,21 +319,26 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as high?
|
/// Get whether the output level is set to high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
!self.is_set_low()
|
!self.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Get whether the output level is set to low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
|
self.ref_is_set_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn ref_is_set_low(&self) -> bool {
|
||||||
self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0
|
self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// Get the current output level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.is_set_high().into()
|
self.is_set_high().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,11 +508,11 @@ mod eh02 {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,11 +530,11 @@ mod eh02 {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,11 +545,11 @@ mod eh02 {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,11 +567,11 @@ mod eh02 {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,11 +581,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -595,11 +605,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,11 +622,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> {
|
|||||||
///
|
///
|
||||||
/// If the pin is not in input mode the result is unspecified.
|
/// If the pin is not in input mode the result is unspecified.
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -632,11 +642,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
|
|||||||
|
|
||||||
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
||||||
/// Create a new GPIOTE output channel driver.
|
/// Create a new GPIOTE output channel driver.
|
||||||
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
|
pub fn new(ch: impl Peripheral<P = C> + 'd, mut pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
|
||||||
into_ref!(ch);
|
into_ref!(ch);
|
||||||
let g = regs();
|
let g = regs();
|
||||||
let num = ch.number();
|
let num = ch.number();
|
||||||
@ -481,11 +481,11 @@ mod eh02 {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.pin.is_high())
|
Ok(!self.pin.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.pin.is_low())
|
Ok(self.pin.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,11 +495,11 @@ impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::ErrorType for InputCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
|
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.pin.is_high())
|
Ok(self.pin.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.pin.is_low())
|
Ok(self.pin.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -605,6 +605,9 @@ impl<'d, T: Instance> NorFlash for Qspi<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "qspi-multiwrite-flash")]
|
||||||
|
impl<'d, T: Instance> embedded_storage::nor_flash::MultiwriteNorFlash for Qspi<'d, T> {}
|
||||||
|
|
||||||
mod _eh1 {
|
mod _eh1 {
|
||||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||||
|
|
||||||
|
@ -78,9 +78,9 @@ fixed = "1.23.1"
|
|||||||
rp-pac = { version = "6" }
|
rp-pac = { version = "6" }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-nb = { version = "=1.0.0-rc.2" }
|
embedded-hal-nb = { version = "=1.0.0-rc.3" }
|
||||||
|
|
||||||
pio-proc = {version= "0.2" }
|
pio-proc = {version= "0.2" }
|
||||||
pio = {version= "0.2.1" }
|
pio = {version= "0.2.1" }
|
||||||
|
@ -105,18 +105,18 @@ impl<'d, T: Pin> Input<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
self.pin.is_high()
|
self.pin.is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
self.pin.is_low()
|
self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns current pin level
|
/// Returns current pin level
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.pin.get_level()
|
self.pin.get_level()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,19 +357,19 @@ impl<'d, T: Pin> Output<'d, T> {
|
|||||||
|
|
||||||
/// Is the output pin set as high?
|
/// Is the output pin set as high?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
self.pin.is_set_high()
|
self.pin.is_set_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
self.pin.is_set_low()
|
self.pin.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// What level output is set to
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.pin.get_output_level()
|
self.pin.get_output_level()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,19 +434,19 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
|||||||
|
|
||||||
/// Is the output level high?
|
/// Is the output level high?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
!self.is_set_low()
|
!self.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output level low?
|
/// Is the output level low?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
self.pin.is_set_as_output()
|
self.pin.is_set_as_output()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// What level output is set to
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.is_set_high().into()
|
self.is_set_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,18 +457,18 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
self.pin.is_high()
|
self.pin.is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
self.pin.is_low()
|
self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns current pin level
|
/// Returns current pin level
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.is_high().into()
|
self.is_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +590,12 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_as_output(&self) -> bool {
|
pub fn is_set_as_output(&mut self) -> bool {
|
||||||
|
self.ref_is_set_as_output()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn ref_is_set_as_output(&self) -> bool {
|
||||||
(self.pin.sio_oe().value().read() & self.bit()) != 0
|
(self.pin.sio_oe().value().read() & self.bit()) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,18 +605,23 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
!self.is_low()
|
!self.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
|
self.ref_is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn ref_is_low(&self) -> bool {
|
||||||
self.pin.sio_in().read() & self.bit() == 0
|
self.pin.sio_in().read() & self.bit() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns current pin level
|
/// Returns current pin level
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.is_high().into()
|
self.is_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,19 +648,24 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
|
|
||||||
/// Is the output level high?
|
/// Is the output level high?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
!self.is_set_low()
|
!self.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output level low?
|
/// Is the output level low?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
|
self.ref_is_set_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn ref_is_set_low(&self) -> bool {
|
||||||
(self.pin.sio_out().value().read() & self.bit()) == 0
|
(self.pin.sio_out().value().read() & self.bit()) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// What level output is set to
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.is_set_high().into()
|
self.is_set_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,11 +927,11 @@ mod eh02 {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -934,11 +949,11 @@ mod eh02 {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,11 +969,11 @@ mod eh02 {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,11 +993,11 @@ mod eh02 {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.pin.ref_is_set_as_output())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.pin.ref_is_set_as_output())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -998,11 +1013,11 @@ mod eh02 {
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,11 +1035,11 @@ mod eh02 {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1042,11 +1057,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1066,11 +1081,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1096,11 +1111,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1112,11 +1127,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrai
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1126,11 +1141,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1146,11 +1161,11 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -820,6 +820,10 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T
|
|||||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> {
|
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> {
|
||||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
if r.uartfr().read().rxfe() {
|
||||||
|
return Err(nb::Error::WouldBlock);
|
||||||
|
}
|
||||||
|
|
||||||
let dr = r.uartdr().read();
|
let dr = r.uartdr().read();
|
||||||
|
|
||||||
if dr.oe() {
|
if dr.oe() {
|
||||||
@ -830,10 +834,8 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M
|
|||||||
Err(nb::Error::Other(Error::Parity))
|
Err(nb::Error::Other(Error::Parity))
|
||||||
} else if dr.fe() {
|
} else if dr.fe() {
|
||||||
Err(nb::Error::Other(Error::Framing))
|
Err(nb::Error::Other(Error::Framing))
|
||||||
} else if dr.fe() {
|
|
||||||
Ok(dr.data())
|
|
||||||
} else {
|
} else {
|
||||||
Err(nb::Error::WouldBlock)
|
Ok(dr.data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +363,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
let siestatus = regs.sie_status().read();
|
let siestatus = regs.sie_status().read();
|
||||||
let intrstatus = regs.intr().read();
|
let intrstatus = regs.intr().read();
|
||||||
|
|
||||||
if siestatus.resume() {
|
if siestatus.resume() || intrstatus.dev_resume_from_host() {
|
||||||
regs.sie_status().write(|w| w.set_resume(true));
|
regs.sie_status().write(|w| w.set_resume(true));
|
||||||
return Poll::Ready(Event::Resume);
|
return Poll::Ready(Event::Resume);
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
|
|||||||
embassy-executor = { version = "0.4.0", path = "../embassy-executor", optional = true }
|
embassy-executor = { version = "0.4.0", path = "../embassy-executor", optional = true }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-nb = { version = "=1.0.0-rc.2" }
|
embedded-hal-nb = { version = "=1.0.0-rc.3" }
|
||||||
|
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.1"
|
||||||
embedded-storage-async = { version = "0.4.1" }
|
embedded-storage-async = { version = "0.4.1" }
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
//! Analog to Digital (ADC) converter driver.
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
#[cfg(not(adc_f3_v2))]
|
#[cfg(not(adc_f3_v2))]
|
||||||
@ -24,6 +25,7 @@ pub use sample_time::SampleTime;
|
|||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
|
|
||||||
|
/// Analog to Digital driver.
|
||||||
pub struct Adc<'d, T: Instance> {
|
pub struct Adc<'d, T: Instance> {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
adc: crate::PeripheralRef<'d, T>,
|
adc: crate::PeripheralRef<'d, T>,
|
||||||
@ -75,12 +77,16 @@ 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_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))]
|
||||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||||
|
/// ADC instance.
|
||||||
#[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_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))]
|
||||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||||
|
|
||||||
|
/// ADC pin.
|
||||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||||
|
/// ADC internal channel.
|
||||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||||
|
|
||||||
foreach_adc!(
|
foreach_adc!(
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
/// 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_g0, adc_f3, adc_f3_v1_1))]
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -8,6 +10,8 @@ pub enum Resolution {
|
|||||||
SixBit,
|
SixBit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ADC resolution
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[cfg(adc_v4)]
|
#[cfg(adc_v4)]
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -49,6 +53,9 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Resolution {
|
impl Resolution {
|
||||||
|
/// Get the maximum reading value for this resolution.
|
||||||
|
///
|
||||||
|
/// This is `2**n - 1`.
|
||||||
pub fn to_max_count(&self) -> u32 {
|
pub fn to_max_count(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(adc_v4)]
|
#[cfg(adc_v4)]
|
||||||
|
@ -32,6 +32,7 @@ const TEMP_CHANNEL: u8 = 18;
|
|||||||
const VBAT_CHANNEL: u8 = 17;
|
const VBAT_CHANNEL: u8 = 17;
|
||||||
|
|
||||||
// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
||||||
|
/// Internal voltage reference channel.
|
||||||
pub struct VrefInt;
|
pub struct VrefInt;
|
||||||
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
||||||
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
||||||
@ -40,6 +41,7 @@ impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal temperature channel.
|
||||||
pub struct Temperature;
|
pub struct Temperature;
|
||||||
impl<T: Instance> InternalChannel<T> for Temperature {}
|
impl<T: Instance> InternalChannel<T> for Temperature {}
|
||||||
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
||||||
@ -48,6 +50,7 @@ impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal battery voltage channel.
|
||||||
pub struct Vbat;
|
pub struct Vbat;
|
||||||
impl<T: Instance> InternalChannel<T> for Vbat {}
|
impl<T: Instance> InternalChannel<T> for Vbat {}
|
||||||
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
|
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
|
||||||
@ -125,6 +128,7 @@ impl Prescaler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Adc<'d, T> {
|
impl<'d, T: Instance> Adc<'d, T> {
|
||||||
|
/// Create a new ADC driver.
|
||||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
|
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u16>) -> Self {
|
||||||
embassy_hal_internal::into_ref!(adc);
|
embassy_hal_internal::into_ref!(adc);
|
||||||
T::enable_and_reset();
|
T::enable_and_reset();
|
||||||
@ -212,6 +216,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable reading the voltage reference internal channel.
|
||||||
pub fn enable_vrefint(&self) -> VrefInt {
|
pub fn enable_vrefint(&self) -> VrefInt {
|
||||||
T::common_regs().ccr().modify(|reg| {
|
T::common_regs().ccr().modify(|reg| {
|
||||||
reg.set_vrefen(true);
|
reg.set_vrefen(true);
|
||||||
@ -220,6 +225,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
VrefInt {}
|
VrefInt {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable reading the temperature internal channel.
|
||||||
pub fn enable_temperature(&self) -> Temperature {
|
pub fn enable_temperature(&self) -> Temperature {
|
||||||
T::common_regs().ccr().modify(|reg| {
|
T::common_regs().ccr().modify(|reg| {
|
||||||
reg.set_vsenseen(true);
|
reg.set_vsenseen(true);
|
||||||
@ -228,6 +234,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
Temperature {}
|
Temperature {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable reading the vbat internal channel.
|
||||||
pub fn enable_vbat(&self) -> Vbat {
|
pub fn enable_vbat(&self) -> Vbat {
|
||||||
T::common_regs().ccr().modify(|reg| {
|
T::common_regs().ccr().modify(|reg| {
|
||||||
reg.set_vbaten(true);
|
reg.set_vbaten(true);
|
||||||
@ -236,10 +243,12 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
Vbat {}
|
Vbat {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the ADC sample time.
|
||||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||||
self.sample_time = sample_time;
|
self.sample_time = sample_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the ADC resolution.
|
||||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||||
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
||||||
}
|
}
|
||||||
@ -263,6 +272,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
T::regs().dr().read().0 as u16
|
T::regs().dr().read().0 as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read an ADC pin.
|
||||||
pub fn read<P>(&mut self, pin: &mut P) -> u16
|
pub fn read<P>(&mut self, pin: &mut P) -> u16
|
||||||
where
|
where
|
||||||
P: AdcPin<T>,
|
P: AdcPin<T>,
|
||||||
@ -273,6 +283,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
|||||||
self.read_channel(pin.channel())
|
self.read_channel(pin.channel())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read an ADC internal channel.
|
||||||
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
||||||
self.read_channel(channel.channel())
|
self.read_channel(channel.channel())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
pub use bxcan;
|
|
||||||
use embassy_hal_internal::PeripheralRef;
|
|
||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
@ -25,27 +22,19 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance {
|
pub trait Instance {
|
||||||
const REGISTERS: *mut bxcan::RegisterBlock;
|
|
||||||
|
|
||||||
fn regs() -> &'static crate::pac::can::Fdcan;
|
fn regs() -> &'static crate::pac::can::Fdcan;
|
||||||
fn state() -> &'static State;
|
fn state() -> &'static State;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interruptable FDCAN instance.
|
||||||
pub trait InterruptableInstance {}
|
pub trait InterruptableInstance {}
|
||||||
|
/// FDCAN instance.
|
||||||
pub trait Instance: sealed::Instance + InterruptableInstance + 'static {}
|
pub trait Instance: sealed::Instance + InterruptableInstance + 'static {}
|
||||||
|
|
||||||
pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>);
|
|
||||||
|
|
||||||
unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> {
|
|
||||||
const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach_peripheral!(
|
foreach_peripheral!(
|
||||||
(can, $inst:ident) => {
|
(can, $inst:ident) => {
|
||||||
impl sealed::Instance for peripherals::$inst {
|
impl sealed::Instance for peripherals::$inst {
|
||||||
const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _;
|
|
||||||
|
|
||||||
fn regs() -> &'static crate::pac::can::Fdcan {
|
fn regs() -> &'static crate::pac::can::Fdcan {
|
||||||
&crate::pac::$inst
|
&crate::pac::$inst
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,19 @@ use crate::peripherals::CRC;
|
|||||||
use crate::rcc::sealed::RccPeripheral;
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
/// CRC driver.
|
||||||
pub struct Crc<'d> {
|
pub struct Crc<'d> {
|
||||||
_peripheral: PeripheralRef<'d, CRC>,
|
_peripheral: PeripheralRef<'d, CRC>,
|
||||||
_config: Config,
|
_config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CRC configuration errlr
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
|
/// The selected polynomial is invalid.
|
||||||
InvalidPolynomial,
|
InvalidPolynomial,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CRC configuration
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
reverse_in: InputReverseConfig,
|
reverse_in: InputReverseConfig,
|
||||||
reverse_out: bool,
|
reverse_out: bool,
|
||||||
@ -25,14 +29,20 @@ pub struct Config {
|
|||||||
crc_poly: u32,
|
crc_poly: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Input reverse configuration.
|
||||||
pub enum InputReverseConfig {
|
pub enum InputReverseConfig {
|
||||||
|
/// Don't reverse anything
|
||||||
None,
|
None,
|
||||||
|
/// Reverse bytes
|
||||||
Byte,
|
Byte,
|
||||||
|
/// Reverse 16-bit halfwords.
|
||||||
Halfword,
|
Halfword,
|
||||||
|
/// Reverse 32-bit words.
|
||||||
Word,
|
Word,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
/// Create a new CRC config.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
reverse_in: InputReverseConfig,
|
reverse_in: InputReverseConfig,
|
||||||
reverse_out: bool,
|
reverse_out: bool,
|
||||||
@ -57,7 +67,9 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Polynomial size
|
||||||
#[cfg(crc_v3)]
|
#[cfg(crc_v3)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub enum PolySize {
|
pub enum PolySize {
|
||||||
Width7,
|
Width7,
|
||||||
Width8,
|
Width8,
|
||||||
@ -81,6 +93,7 @@ impl<'d> Crc<'d> {
|
|||||||
instance
|
instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset the CRC engine.
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
PAC_CRC.cr().modify(|w| w.set_reset(true));
|
PAC_CRC.cr().modify(|w| w.set_reset(true));
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,11 @@ impl Mode {
|
|||||||
///
|
///
|
||||||
/// 12-bit values outside the permitted range are silently truncated.
|
/// 12-bit values outside the permitted range are silently truncated.
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
// 8 bit value
|
/// 8 bit value
|
||||||
Bit8(u8),
|
Bit8(u8),
|
||||||
// 12 bit value stored in a u16, left-aligned
|
/// 12 bit value stored in a u16, left-aligned
|
||||||
Bit12Left(u16),
|
Bit12Left(u16),
|
||||||
// 12 bit value stored in a u16, right-aligned
|
/// 12 bit value stored in a u16, right-aligned
|
||||||
Bit12Right(u16),
|
Bit12Right(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,11 +76,11 @@ pub enum Value {
|
|||||||
///
|
///
|
||||||
/// 12-bit values outside the permitted range are silently truncated.
|
/// 12-bit values outside the permitted range are silently truncated.
|
||||||
pub enum DualValue {
|
pub enum DualValue {
|
||||||
// 8 bit value
|
/// 8 bit value
|
||||||
Bit8(u8, u8),
|
Bit8(u8, u8),
|
||||||
// 12 bit value stored in a u16, left-aligned
|
/// 12 bit value stored in a u16, left-aligned
|
||||||
Bit12Left(u16, u16),
|
Bit12Left(u16, u16),
|
||||||
// 12 bit value stored in a u16, right-aligned
|
/// 12 bit value stored in a u16, right-aligned
|
||||||
Bit12Right(u16, u16),
|
Bit12Right(u16, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,11 +88,11 @@ pub enum DualValue {
|
|||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// Array variant of [`Value`].
|
/// Array variant of [`Value`].
|
||||||
pub enum ValueArray<'a> {
|
pub enum ValueArray<'a> {
|
||||||
// 8 bit values
|
/// 8 bit values
|
||||||
Bit8(&'a [u8]),
|
Bit8(&'a [u8]),
|
||||||
// 12 bit value stored in a u16, left-aligned
|
/// 12 bit value stored in a u16, left-aligned
|
||||||
Bit12Left(&'a [u16]),
|
Bit12Left(&'a [u16]),
|
||||||
// 12 bit values stored in a u16, right-aligned
|
/// 12 bit values stored in a u16, right-aligned
|
||||||
Bit12Right(&'a [u16]),
|
Bit12Right(&'a [u16]),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +106,9 @@ pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> {
|
|||||||
dma: PeripheralRef<'d, DMA>,
|
dma: PeripheralRef<'d, DMA>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DAC channel 1 type alias.
|
||||||
pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
|
pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
|
||||||
|
/// DAC channel 2 type alias.
|
||||||
pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
|
pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
|
||||||
|
|
||||||
impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
|
impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
|
||||||
@ -492,6 +494,7 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DAC instance.
|
||||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||||
dma_trait!(DacDma1, Instance);
|
dma_trait!(DacDma1, Instance);
|
||||||
dma_trait!(DacDma2, Instance);
|
dma_trait!(DacDma2, Instance);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
/// Trigger selection for STM32F0.
|
/// Trigger selection for STM32F0.
|
||||||
#[cfg(stm32f0)]
|
#[cfg(stm32f0)]
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
@ -36,6 +36,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The level on the VSync pin when the data is not valid on the parallel interface.
|
/// The level on the VSync pin when the data is not valid on the parallel interface.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum VSyncDataInvalidLevel {
|
pub enum VSyncDataInvalidLevel {
|
||||||
Low,
|
Low,
|
||||||
@ -43,6 +44,7 @@ pub enum VSyncDataInvalidLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The level on the VSync pin when the data is not valid on the parallel interface.
|
/// The level on the VSync pin when the data is not valid on the parallel interface.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum HSyncDataInvalidLevel {
|
pub enum HSyncDataInvalidLevel {
|
||||||
Low,
|
Low,
|
||||||
@ -50,14 +52,16 @@ pub enum HSyncDataInvalidLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub enum PixelClockPolarity {
|
pub enum PixelClockPolarity {
|
||||||
RisingEdge,
|
RisingEdge,
|
||||||
FallingEdge,
|
FallingEdge,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
struct State {
|
||||||
waker: AtomicWaker,
|
waker: AtomicWaker,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
const fn new() -> State {
|
const fn new() -> State {
|
||||||
State {
|
State {
|
||||||
@ -68,18 +72,25 @@ impl State {
|
|||||||
|
|
||||||
static STATE: State = State::new();
|
static STATE: State = State::new();
|
||||||
|
|
||||||
|
/// DCMI error.
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Overrun error: the hardware generated data faster than we could read it.
|
||||||
Overrun,
|
Overrun,
|
||||||
|
/// Internal peripheral error.
|
||||||
PeripheralError,
|
PeripheralError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DCMI configuration.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// VSYNC level.
|
||||||
pub vsync_level: VSyncDataInvalidLevel,
|
pub vsync_level: VSyncDataInvalidLevel,
|
||||||
|
/// HSYNC level.
|
||||||
pub hsync_level: HSyncDataInvalidLevel,
|
pub hsync_level: HSyncDataInvalidLevel,
|
||||||
|
/// PIXCLK polarity.
|
||||||
pub pixclk_polarity: PixelClockPolarity,
|
pub pixclk_polarity: PixelClockPolarity,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +116,7 @@ macro_rules! config_pins {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DCMI driver.
|
||||||
pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> {
|
pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> {
|
||||||
inner: PeripheralRef<'d, T>,
|
inner: PeripheralRef<'d, T>,
|
||||||
dma: PeripheralRef<'d, Dma>,
|
dma: PeripheralRef<'d, Dma>,
|
||||||
@ -115,6 +127,7 @@ where
|
|||||||
T: Instance,
|
T: Instance,
|
||||||
Dma: FrameDma<T>,
|
Dma: FrameDma<T>,
|
||||||
{
|
{
|
||||||
|
/// Create a new DCMI driver with 8 data bits.
|
||||||
pub fn new_8bit(
|
pub fn new_8bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -139,6 +152,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, false, 0b00)
|
Self::new_inner(peri, dma, config, false, 0b00)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 10 data bits.
|
||||||
pub fn new_10bit(
|
pub fn new_10bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -165,6 +179,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, false, 0b01)
|
Self::new_inner(peri, dma, config, false, 0b01)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 12 data bits.
|
||||||
pub fn new_12bit(
|
pub fn new_12bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -193,6 +208,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, false, 0b10)
|
Self::new_inner(peri, dma, config, false, 0b10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 14 data bits.
|
||||||
pub fn new_14bit(
|
pub fn new_14bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -223,6 +239,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, false, 0b11)
|
Self::new_inner(peri, dma, config, false, 0b11)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 8 data bits, with embedded synchronization.
|
||||||
pub fn new_es_8bit(
|
pub fn new_es_8bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -245,6 +262,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, true, 0b00)
|
Self::new_inner(peri, dma, config, true, 0b00)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 10 data bits, with embedded synchronization.
|
||||||
pub fn new_es_10bit(
|
pub fn new_es_10bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -269,6 +287,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, true, 0b01)
|
Self::new_inner(peri, dma, config, true, 0b01)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 12 data bits, with embedded synchronization.
|
||||||
pub fn new_es_12bit(
|
pub fn new_es_12bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -295,6 +314,7 @@ where
|
|||||||
Self::new_inner(peri, dma, config, true, 0b10)
|
Self::new_inner(peri, dma, config, true, 0b10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new DCMI driver with 14 data bits, with embedded synchronization.
|
||||||
pub fn new_es_14bit(
|
pub fn new_es_14bit(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
@ -538,7 +558,9 @@ mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DCMI instance.
|
||||||
pub trait Instance: sealed::Instance + 'static {
|
pub trait Instance: sealed::Instance + 'static {
|
||||||
|
/// Interrupt for this instance.
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type Interrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#![macro_use]
|
//! Basic Direct Memory Acccess (BDMA)
|
||||||
|
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
@ -17,6 +17,7 @@ use crate::interrupt::Priority;
|
|||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::pac::bdma::{regs, vals};
|
use crate::pac::bdma::{regs, vals};
|
||||||
|
|
||||||
|
/// BDMA transfer options.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -140,13 +141,17 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index
|
|||||||
STATE.ch_wakers[index].wake();
|
STATE.ch_wakers[index].wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA request type alias.
|
||||||
#[cfg(any(bdma_v2, dmamux))]
|
#[cfg(any(bdma_v2, dmamux))]
|
||||||
pub type Request = u8;
|
pub type Request = u8;
|
||||||
|
/// DMA request type alias.
|
||||||
#[cfg(not(any(bdma_v2, dmamux)))]
|
#[cfg(not(any(bdma_v2, dmamux)))]
|
||||||
pub type Request = ();
|
pub type Request = ();
|
||||||
|
|
||||||
|
/// DMA channel.
|
||||||
#[cfg(dmamux)]
|
#[cfg(dmamux)]
|
||||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
||||||
|
/// DMA channel.
|
||||||
#[cfg(not(dmamux))]
|
#[cfg(not(dmamux))]
|
||||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||||
|
|
||||||
@ -161,12 +166,14 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA transfer.
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct Transfer<'a, C: Channel> {
|
pub struct Transfer<'a, C: Channel> {
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel> Transfer<'a, C> {
|
impl<'a, C: Channel> Transfer<'a, C> {
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory).
|
||||||
pub unsafe fn new_read<W: Word>(
|
pub unsafe fn new_read<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -177,6 +184,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory), using raw pointers.
|
||||||
pub unsafe fn new_read_raw<W: Word>(
|
pub unsafe fn new_read_raw<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -202,6 +210,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral).
|
||||||
pub unsafe fn new_write<W: Word>(
|
pub unsafe fn new_write<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -212,6 +221,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral), using raw pointers.
|
||||||
pub unsafe fn new_write_raw<W: Word>(
|
pub unsafe fn new_write_raw<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -237,6 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
|
||||||
pub unsafe fn new_write_repeated<W: Word>(
|
pub unsafe fn new_write_repeated<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -321,6 +332,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request the transfer to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
|
|
||||||
@ -331,6 +345,10 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether this transfer is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
let en = ch.cr().read().en();
|
let en = ch.cr().read().en();
|
||||||
@ -339,13 +357,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
en && (circular || !tcif)
|
en && (circular || !tcif)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the total remaining transfers for the channel
|
/// Get the total remaining transfers for the channel.
|
||||||
/// Note: this will be zero for transfers that completed without cancellation.
|
///
|
||||||
|
/// This will be zero for transfers that completed instead of being canceled with [`request_stop`](Self::request_stop).
|
||||||
pub fn get_remaining_transfers(&self) -> u16 {
|
pub fn get_remaining_transfers(&self) -> u16 {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
ch.ndtr().read().ndt()
|
ch.ndtr().read().ndt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking wait until the transfer finishes.
|
||||||
pub fn blocking_wait(mut self) {
|
pub fn blocking_wait(mut self) {
|
||||||
while self.is_running() {}
|
while self.is_running() {}
|
||||||
self.request_stop();
|
self.request_stop();
|
||||||
@ -411,6 +431,7 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ringbuffer for reading data using DMA circular mode.
|
||||||
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
|
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
|
||||||
cr: regs::Cr,
|
cr: regs::Cr,
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
@ -418,7 +439,8 @@ pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
||||||
pub unsafe fn new_read(
|
/// Create a new ring buffer.
|
||||||
|
pub unsafe fn new(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
_request: Request,
|
_request: Request,
|
||||||
peri_addr: *mut W,
|
peri_addr: *mut W,
|
||||||
@ -473,11 +495,15 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the ring buffer operation.
|
||||||
|
///
|
||||||
|
/// You must call this after creating it for it to work.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
ch.cr().write_value(self.cr)
|
ch.cr().write_value(self.cr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all data in the ring buffer.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
||||||
}
|
}
|
||||||
@ -509,10 +535,11 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The capacity of the ringbuffer.
|
/// The capacity of the ringbuffer.
|
||||||
pub const fn cap(&self) -> usize {
|
pub const fn capacity(&self) -> usize {
|
||||||
self.ringbuf.cap()
|
self.ringbuf.cap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a waker to be woken when at least one byte is received.
|
||||||
pub fn set_waker(&mut self, waker: &Waker) {
|
pub fn set_waker(&mut self, waker: &Waker) {
|
||||||
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
||||||
}
|
}
|
||||||
@ -526,6 +553,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request DMA to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
|
|
||||||
@ -539,6 +569,10 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether DMA is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
ch.cr().read().en()
|
ch.cr().read().en()
|
||||||
@ -555,6 +589,7 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ringbuffer for writing data using DMA circular mode.
|
||||||
pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
|
pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
|
||||||
cr: regs::Cr,
|
cr: regs::Cr,
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
@ -562,7 +597,8 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
||||||
pub unsafe fn new_write(
|
/// Create a new ring buffer.
|
||||||
|
pub unsafe fn new(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
_request: Request,
|
_request: Request,
|
||||||
peri_addr: *mut W,
|
peri_addr: *mut W,
|
||||||
@ -617,11 +653,15 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the ring buffer operation.
|
||||||
|
///
|
||||||
|
/// You must call this after creating it for it to work.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
ch.cr().write_value(self.cr)
|
ch.cr().write_value(self.cr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all data in the ring buffer.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
||||||
}
|
}
|
||||||
@ -640,10 +680,11 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The capacity of the ringbuffer.
|
/// The capacity of the ringbuffer.
|
||||||
pub const fn cap(&self) -> usize {
|
pub const fn capacity(&self) -> usize {
|
||||||
self.ringbuf.cap()
|
self.ringbuf.cap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a waker to be woken when at least one byte is sent.
|
||||||
pub fn set_waker(&mut self, waker: &Waker) {
|
pub fn set_waker(&mut self, waker: &Waker) {
|
||||||
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
||||||
}
|
}
|
||||||
@ -657,6 +698,9 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request DMA to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
|
|
||||||
@ -670,6 +714,10 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether DMA is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().ch(self.channel.num());
|
let ch = self.channel.regs().ch(self.channel.num());
|
||||||
ch.cr().read().en()
|
ch.cr().read().en()
|
||||||
|
@ -16,6 +16,7 @@ use crate::interrupt::Priority;
|
|||||||
use crate::pac::dma::{regs, vals};
|
use crate::pac::dma::{regs, vals};
|
||||||
use crate::{interrupt, pac};
|
use crate::{interrupt, pac};
|
||||||
|
|
||||||
|
/// DMA transfer options.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -69,6 +70,7 @@ impl From<Dir> for vals::Dir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA transfer burst setting.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Burst {
|
pub enum Burst {
|
||||||
@ -93,6 +95,7 @@ impl From<Burst> for vals::Burst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA flow control setting.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum FlowControl {
|
pub enum FlowControl {
|
||||||
@ -111,6 +114,7 @@ impl From<FlowControl> for vals::Pfctrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA FIFO threshold.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum FifoThreshold {
|
pub enum FifoThreshold {
|
||||||
@ -208,13 +212,17 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index:
|
|||||||
STATE.ch_wakers[index].wake();
|
STATE.ch_wakers[index].wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA request type alias. (also known as DMA channel number in some chips)
|
||||||
#[cfg(any(dma_v2, dmamux))]
|
#[cfg(any(dma_v2, dmamux))]
|
||||||
pub type Request = u8;
|
pub type Request = u8;
|
||||||
|
/// DMA request type alias. (also known as DMA channel number in some chips)
|
||||||
#[cfg(not(any(dma_v2, dmamux)))]
|
#[cfg(not(any(dma_v2, dmamux)))]
|
||||||
pub type Request = ();
|
pub type Request = ();
|
||||||
|
|
||||||
|
/// DMA channel.
|
||||||
#[cfg(dmamux)]
|
#[cfg(dmamux)]
|
||||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static + super::dmamux::MuxChannel {}
|
||||||
|
/// DMA channel.
|
||||||
#[cfg(not(dmamux))]
|
#[cfg(not(dmamux))]
|
||||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + 'static {}
|
||||||
|
|
||||||
@ -229,12 +237,14 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA transfer.
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct Transfer<'a, C: Channel> {
|
pub struct Transfer<'a, C: Channel> {
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel> Transfer<'a, C> {
|
impl<'a, C: Channel> Transfer<'a, C> {
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory).
|
||||||
pub unsafe fn new_read<W: Word>(
|
pub unsafe fn new_read<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -245,6 +255,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
Self::new_read_raw(channel, request, peri_addr, buf, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory), using raw pointers.
|
||||||
pub unsafe fn new_read_raw<W: Word>(
|
pub unsafe fn new_read_raw<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -270,6 +281,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral).
|
||||||
pub unsafe fn new_write<W: Word>(
|
pub unsafe fn new_write<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -280,6 +292,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
Self::new_write_raw(channel, request, buf, peri_addr, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral), using raw pointers.
|
||||||
pub unsafe fn new_write_raw<W: Word>(
|
pub unsafe fn new_write_raw<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -305,6 +318,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly.
|
||||||
pub unsafe fn new_write_repeated<W: Word>(
|
pub unsafe fn new_write_repeated<W: Word>(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -407,6 +421,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request the transfer to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
|
|
||||||
@ -417,6 +434,10 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether this transfer is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().read().en()
|
ch.cr().read().en()
|
||||||
@ -429,6 +450,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||||||
ch.ndtr().read().ndt()
|
ch.ndtr().read().ndt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking wait until the transfer finishes.
|
||||||
pub fn blocking_wait(mut self) {
|
pub fn blocking_wait(mut self) {
|
||||||
while self.is_running() {}
|
while self.is_running() {}
|
||||||
|
|
||||||
@ -465,12 +487,14 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
|
|||||||
|
|
||||||
// ==================================
|
// ==================================
|
||||||
|
|
||||||
|
/// Double-buffered DMA transfer.
|
||||||
pub struct DoubleBuffered<'a, C: Channel, W: Word> {
|
pub struct DoubleBuffered<'a, C: Channel, W: Word> {
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
_phantom: PhantomData<W>,
|
_phantom: PhantomData<W>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
||||||
|
/// Create a new read DMA transfer (peripheral to memory).
|
||||||
pub unsafe fn new_read(
|
pub unsafe fn new_read(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
_request: Request,
|
_request: Request,
|
||||||
@ -554,25 +578,36 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the first buffer address.
|
||||||
|
///
|
||||||
|
/// You may call this while DMA is transferring the other buffer.
|
||||||
pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
|
pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.m0ar().write_value(buffer as _);
|
ch.m0ar().write_value(buffer as _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the second buffer address.
|
||||||
|
///
|
||||||
|
/// You may call this while DMA is transferring the other buffer.
|
||||||
pub unsafe fn set_buffer1(&mut self, buffer: *mut W) {
|
pub unsafe fn set_buffer1(&mut self, buffer: *mut W) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.m1ar().write_value(buffer as _);
|
ch.m1ar().write_value(buffer as _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returh whether buffer0 is accessible (i.e. whether DMA is transferring buffer1 now)
|
||||||
pub fn is_buffer0_accessible(&mut self) -> bool {
|
pub fn is_buffer0_accessible(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().read().ct() == vals::Ct::MEMORY1
|
ch.cr().read().ct() == vals::Ct::MEMORY1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a waker to be woken when one of the buffers is being transferred.
|
||||||
pub fn set_waker(&mut self, waker: &Waker) {
|
pub fn set_waker(&mut self, waker: &Waker) {
|
||||||
STATE.ch_wakers[self.channel.index()].register(waker);
|
STATE.ch_wakers[self.channel.index()].register(waker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request the transfer to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
|
|
||||||
@ -583,6 +618,10 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether this transfer is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().read().en()
|
ch.cr().read().en()
|
||||||
@ -629,6 +668,7 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ringbuffer for receiving data using DMA circular mode.
|
||||||
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
|
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
|
||||||
cr: regs::Cr,
|
cr: regs::Cr,
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
@ -636,7 +676,8 @@ pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
||||||
pub unsafe fn new_read(
|
/// Create a new ring buffer.
|
||||||
|
pub unsafe fn new(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
_request: Request,
|
_request: Request,
|
||||||
peri_addr: *mut W,
|
peri_addr: *mut W,
|
||||||
@ -706,11 +747,15 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the ring buffer operation.
|
||||||
|
///
|
||||||
|
/// You must call this after creating it for it to work.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().write_value(self.cr);
|
ch.cr().write_value(self.cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all data in the ring buffer.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
||||||
}
|
}
|
||||||
@ -741,11 +786,12 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
// The capacity of the ringbuffer
|
/// The capacity of the ringbuffer
|
||||||
pub const fn cap(&self) -> usize {
|
pub const fn capacity(&self) -> usize {
|
||||||
self.ringbuf.cap()
|
self.ringbuf.cap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a waker to be woken when at least one byte is received.
|
||||||
pub fn set_waker(&mut self, waker: &Waker) {
|
pub fn set_waker(&mut self, waker: &Waker) {
|
||||||
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
||||||
}
|
}
|
||||||
@ -763,6 +809,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request DMA to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
|
|
||||||
@ -774,6 +823,10 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether DMA is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().read().en()
|
ch.cr().read().en()
|
||||||
@ -790,6 +843,7 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ringbuffer for writing data using DMA circular mode.
|
||||||
pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
|
pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
|
||||||
cr: regs::Cr,
|
cr: regs::Cr,
|
||||||
channel: PeripheralRef<'a, C>,
|
channel: PeripheralRef<'a, C>,
|
||||||
@ -797,7 +851,8 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
||||||
pub unsafe fn new_write(
|
/// Create a new ring buffer.
|
||||||
|
pub unsafe fn new(
|
||||||
channel: impl Peripheral<P = C> + 'a,
|
channel: impl Peripheral<P = C> + 'a,
|
||||||
_request: Request,
|
_request: Request,
|
||||||
peri_addr: *mut W,
|
peri_addr: *mut W,
|
||||||
@ -867,11 +922,15 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the ring buffer operation.
|
||||||
|
///
|
||||||
|
/// You must call this after creating it for it to work.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().write_value(self.cr);
|
ch.cr().write_value(self.cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all data in the ring buffer.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
|
||||||
}
|
}
|
||||||
@ -889,11 +948,12 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
// The capacity of the ringbuffer
|
/// The capacity of the ringbuffer
|
||||||
pub const fn cap(&self) -> usize {
|
pub const fn capacity(&self) -> usize {
|
||||||
self.ringbuf.cap()
|
self.ringbuf.cap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a waker to be woken when at least one byte is received.
|
||||||
pub fn set_waker(&mut self, waker: &Waker) {
|
pub fn set_waker(&mut self, waker: &Waker) {
|
||||||
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
|
||||||
}
|
}
|
||||||
@ -911,6 +971,9 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request DMA to stop.
|
||||||
|
///
|
||||||
|
/// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.
|
||||||
pub fn request_stop(&mut self) {
|
pub fn request_stop(&mut self) {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
|
|
||||||
@ -922,6 +985,10 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether DMA is still running.
|
||||||
|
///
|
||||||
|
/// If this returns `false`, it can be because either the transfer finished, or
|
||||||
|
/// it was requested to stop early with [`request_stop`](Self::request_stop).
|
||||||
pub fn is_running(&mut self) -> bool {
|
pub fn is_running(&mut self) -> bool {
|
||||||
let ch = self.channel.regs().st(self.channel.num());
|
let ch = self.channel.regs().st(self.channel.num());
|
||||||
ch.cr().read().en()
|
ch.cr().read().en()
|
||||||
|
@ -22,11 +22,15 @@ pub(crate) mod dmamux_sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMAMUX1 instance.
|
||||||
pub struct DMAMUX1;
|
pub struct DMAMUX1;
|
||||||
|
/// DMAMUX2 instance.
|
||||||
#[cfg(stm32h7)]
|
#[cfg(stm32h7)]
|
||||||
pub struct DMAMUX2;
|
pub struct DMAMUX2;
|
||||||
|
|
||||||
|
/// DMAMUX channel trait.
|
||||||
pub trait MuxChannel: dmamux_sealed::MuxChannel {
|
pub trait MuxChannel: dmamux_sealed::MuxChannel {
|
||||||
|
/// DMAMUX instance this channel is on.
|
||||||
type Mux;
|
type Mux;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,13 @@ enum Dir {
|
|||||||
PeripheralToMemory,
|
PeripheralToMemory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// "No DMA" placeholder.
|
||||||
|
///
|
||||||
|
/// You may pass this in place of a real DMA channel when creating a driver
|
||||||
|
/// to indicate it should not use DMA.
|
||||||
|
///
|
||||||
|
/// This often causes async functionality to not be available on the instance,
|
||||||
|
/// leaving only blocking functionality.
|
||||||
pub struct NoDma;
|
pub struct NoDma;
|
||||||
|
|
||||||
impl_peripheral!(NoDma);
|
impl_peripheral!(NoDma);
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! DMA word sizes.
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum WordSize {
|
pub enum WordSize {
|
||||||
@ -7,6 +10,7 @@ pub enum WordSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WordSize {
|
impl WordSize {
|
||||||
|
/// Amount of bytes of this word size.
|
||||||
pub fn bytes(&self) -> usize {
|
pub fn bytes(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::OneByte => 1,
|
Self::OneByte => 1,
|
||||||
@ -20,8 +24,13 @@ mod sealed {
|
|||||||
pub trait Word {}
|
pub trait Word {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DMA word trait.
|
||||||
|
///
|
||||||
|
/// This is implemented for u8, u16, u32, etc.
|
||||||
pub trait Word: sealed::Word + Default + Copy + 'static {
|
pub trait Word: sealed::Word + Default + Copy + 'static {
|
||||||
|
/// Word size
|
||||||
fn size() -> WordSize;
|
fn size() -> WordSize;
|
||||||
|
/// Amount of bits of this word size.
|
||||||
fn bits() -> usize;
|
fn bits() -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +49,7 @@ macro_rules! impl_word {
|
|||||||
($T:ident, $uX:ident, $bits:literal, $size:ident) => {
|
($T:ident, $uX:ident, $bits:literal, $size:ident) => {
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
|
#[doc = concat!(stringify!($T), " word size")]
|
||||||
pub struct $T(pub $uX);
|
pub struct $T(pub $uX);
|
||||||
impl_word!(_, $T, $bits, $size);
|
impl_word!(_, $T, $bits, $size);
|
||||||
};
|
};
|
||||||
|
@ -102,6 +102,7 @@ unsafe impl PHY for GenericSMI {
|
|||||||
|
|
||||||
/// Public functions for the PHY
|
/// Public functions for the PHY
|
||||||
impl GenericSMI {
|
impl GenericSMI {
|
||||||
|
/// Set the SMI polling interval.
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
|
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
|
||||||
self.poll_interval = poll_interval
|
self.poll_interval = poll_interval
|
||||||
|
@ -22,6 +22,14 @@ const RX_BUFFER_SIZE: usize = 1536;
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub(crate) struct Packet<const N: usize>([u8; N]);
|
pub(crate) struct Packet<const N: usize>([u8; N]);
|
||||||
|
|
||||||
|
/// Ethernet packet queue.
|
||||||
|
///
|
||||||
|
/// This struct owns the memory used for reading and writing packets.
|
||||||
|
///
|
||||||
|
/// `TX` is the number of packets in the transmit queue, `RX` in the receive
|
||||||
|
/// queue. A bigger queue allows the hardware to receive more packets while the
|
||||||
|
/// CPU is busy doing other things, which may increase performance (especially for RX)
|
||||||
|
/// at the cost of more RAM usage.
|
||||||
pub struct PacketQueue<const TX: usize, const RX: usize> {
|
pub struct PacketQueue<const TX: usize, const RX: usize> {
|
||||||
tx_desc: [TDes; TX],
|
tx_desc: [TDes; TX],
|
||||||
rx_desc: [RDes; RX],
|
rx_desc: [RDes; RX],
|
||||||
@ -30,6 +38,7 @@ pub struct PacketQueue<const TX: usize, const RX: usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
|
impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
|
||||||
|
/// Create a new packet queue.
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
const NEW_TDES: TDes = TDes::new();
|
const NEW_TDES: TDes = TDes::new();
|
||||||
const NEW_RDES: RDes = RDes::new();
|
const NEW_RDES: RDes = RDes::new();
|
||||||
@ -41,7 +50,18 @@ impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow to initialize a Self without requiring it to go on the stack
|
/// Initialize a packet queue in-place.
|
||||||
|
///
|
||||||
|
/// This can be helpful to avoid accidentally stack-allocating the packet queue in the stack. The
|
||||||
|
/// Rust compiler can sometimes be a bit dumb when working with large owned values: if you call `new()`
|
||||||
|
/// and then store the returned PacketQueue in its final place (like a `static`), the compiler might
|
||||||
|
/// place it temporarily on the stack then move it. Since this struct is quite big, it may result
|
||||||
|
/// in a stack overflow.
|
||||||
|
///
|
||||||
|
/// With this function, you can create an uninitialized `static` with type `MaybeUninit<PacketQueue<...>>`
|
||||||
|
/// and initialize it in-place, guaranteeing no stack usage.
|
||||||
|
///
|
||||||
|
/// After calling this function, calling `assume_init` on the MaybeUninit is guaranteed safe.
|
||||||
pub fn init(this: &mut MaybeUninit<Self>) {
|
pub fn init(this: &mut MaybeUninit<Self>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
this.as_mut_ptr().write_bytes(0u8, 1);
|
this.as_mut_ptr().write_bytes(0u8, 1);
|
||||||
@ -93,6 +113,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `embassy-net` RX token.
|
||||||
pub struct RxToken<'a, 'd> {
|
pub struct RxToken<'a, 'd> {
|
||||||
rx: &'a mut RDesRing<'d>,
|
rx: &'a mut RDesRing<'d>,
|
||||||
}
|
}
|
||||||
@ -110,6 +131,7 @@ impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `embassy-net` TX token.
|
||||||
pub struct TxToken<'a, 'd> {
|
pub struct TxToken<'a, 'd> {
|
||||||
tx: &'a mut TDesRing<'d>,
|
tx: &'a mut TDesRing<'d>,
|
||||||
}
|
}
|
||||||
@ -159,6 +181,7 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ethernet instance.
|
||||||
pub trait Instance: sealed::Instance + Send + 'static {}
|
pub trait Instance: sealed::Instance + Send + 'static {}
|
||||||
|
|
||||||
impl sealed::Instance for crate::peripherals::ETH {
|
impl sealed::Instance for crate::peripherals::ETH {
|
||||||
|
@ -34,6 +34,7 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ethernet driver.
|
||||||
pub struct Ethernet<'d, T: Instance, P: PHY> {
|
pub struct Ethernet<'d, T: Instance, P: PHY> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
pub(crate) tx: TDesRing<'d>,
|
pub(crate) tx: TDesRing<'d>,
|
||||||
@ -56,6 +57,7 @@ macro_rules! config_pins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||||
|
/// Create a new Ethernet driver.
|
||||||
pub fn new<const TX: usize, const RX: usize>(
|
pub fn new<const TX: usize, const RX: usize>(
|
||||||
queue: &'d mut PacketQueue<TX, RX>,
|
queue: &'d mut PacketQueue<TX, RX>,
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
@ -237,6 +239,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ethernet SMI driver.
|
||||||
pub struct EthernetStationManagement<T: Instance> {
|
pub struct EthernetStationManagement<T: Instance> {
|
||||||
peri: PhantomData<T>,
|
peri: PhantomData<T>,
|
||||||
clock_range: u8,
|
clock_range: u8,
|
||||||
|
@ -39,7 +39,7 @@ fn exticr_regs() -> pac::afio::Afio {
|
|||||||
pac::AFIO
|
pac::AFIO
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn on_irq() {
|
unsafe fn on_irq() {
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
crate::low_power::on_wakeup_irq();
|
crate::low_power::on_wakeup_irq();
|
||||||
|
|
||||||
@ -85,7 +85,13 @@ impl Iterator for BitIter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// EXTI input driver
|
/// EXTI input driver.
|
||||||
|
///
|
||||||
|
/// This driver augments a GPIO `Input` with EXTI functionality. EXTI is not
|
||||||
|
/// built into `Input` itself because it needs to take ownership of the corresponding
|
||||||
|
/// EXTI channel, which is a limited resource.
|
||||||
|
///
|
||||||
|
/// Pins PA5, PB5, PC5... all use EXTI channel 5, so you can't use EXTI on, say, PA5 and PC5 at the same time.
|
||||||
pub struct ExtiInput<'d, T: GpioPin> {
|
pub struct ExtiInput<'d, T: GpioPin> {
|
||||||
pin: Input<'d, T>,
|
pin: Input<'d, T>,
|
||||||
}
|
}
|
||||||
@ -93,23 +99,30 @@ pub struct ExtiInput<'d, T: GpioPin> {
|
|||||||
impl<'d, T: GpioPin> Unpin for ExtiInput<'d, T> {}
|
impl<'d, T: GpioPin> Unpin for ExtiInput<'d, T> {}
|
||||||
|
|
||||||
impl<'d, T: GpioPin> ExtiInput<'d, T> {
|
impl<'d, T: GpioPin> ExtiInput<'d, T> {
|
||||||
|
/// Create an EXTI input.
|
||||||
pub fn new(pin: Input<'d, T>, _ch: impl Peripheral<P = T::ExtiChannel> + 'd) -> Self {
|
pub fn new(pin: Input<'d, T>, _ch: impl Peripheral<P = T::ExtiChannel> + 'd) -> Self {
|
||||||
Self { pin }
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_high(&self) -> bool {
|
/// Get whether the pin is high.
|
||||||
|
pub fn is_high(&mut self) -> bool {
|
||||||
self.pin.is_high()
|
self.pin.is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_low(&self) -> bool {
|
/// Get whether the pin is low.
|
||||||
|
pub fn is_low(&mut self) -> bool {
|
||||||
self.pin.is_low()
|
self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_level(&self) -> Level {
|
/// Get the pin level.
|
||||||
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.pin.get_level()
|
self.pin.get_level()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_high<'a>(&'a mut self) {
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false);
|
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false);
|
||||||
if self.is_high() {
|
if self.is_high() {
|
||||||
return;
|
return;
|
||||||
@ -117,7 +130,10 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> {
|
|||||||
fut.await
|
fut.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_low<'a>(&'a mut self) {
|
/// Asynchronously wait until the pin is low.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already low.
|
||||||
|
pub async fn wait_for_low(&mut self) {
|
||||||
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true);
|
let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true);
|
||||||
if self.is_low() {
|
if self.is_low() {
|
||||||
return;
|
return;
|
||||||
@ -125,15 +141,22 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> {
|
|||||||
fut.await
|
fut.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_rising_edge<'a>(&'a mut self) {
|
/// Asynchronously wait until the pin sees a rising edge.
|
||||||
|
///
|
||||||
|
/// If the pin is already high, it will wait for it to go low then back high.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await
|
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_falling_edge<'a>(&'a mut self) {
|
/// Asynchronously wait until the pin sees a falling edge.
|
||||||
|
///
|
||||||
|
/// If the pin is already low, it will wait for it to go high then back low.
|
||||||
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await
|
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn wait_for_any_edge<'a>(&'a mut self) {
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await
|
ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,11 +165,11 @@ impl<'d, T: GpioPin> embedded_hal_02::digital::v2::InputPin for ExtiInput<'d, T>
|
|||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.pin.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.pin.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,11 +178,11 @@ impl<'d, T: GpioPin> embedded_hal_1::digital::ErrorType for ExtiInput<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: GpioPin> embedded_hal_1::digital::InputPin for ExtiInput<'d, T> {
|
impl<'d, T: GpioPin> embedded_hal_1::digital::InputPin for ExtiInput<'d, T> {
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,6 +307,7 @@ macro_rules! foreach_exti_irq {
|
|||||||
|
|
||||||
macro_rules! impl_irq {
|
macro_rules! impl_irq {
|
||||||
($e:ident) => {
|
($e:ident) => {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[cfg(feature = "rt")]
|
#[cfg(feature = "rt")]
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
unsafe fn $e() {
|
unsafe fn $e() {
|
||||||
@ -298,8 +322,16 @@ pub(crate) mod sealed {
|
|||||||
pub trait Channel {}
|
pub trait Channel {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// EXTI channel trait.
|
||||||
pub trait Channel: sealed::Channel + Sized {
|
pub trait Channel: sealed::Channel + Sized {
|
||||||
|
/// Get the EXTI channel number.
|
||||||
fn number(&self) -> usize;
|
fn number(&self) -> usize;
|
||||||
|
|
||||||
|
/// Type-erase (degrade) this channel into an `AnyChannel`.
|
||||||
|
///
|
||||||
|
/// This converts EXTI channel singletons (`EXTI0`, `EXTI1`, ...), which
|
||||||
|
/// are all different types, into the same type. It is useful for
|
||||||
|
/// creating arrays of channels, or avoiding generics.
|
||||||
fn degrade(self) -> AnyChannel {
|
fn degrade(self) -> AnyChannel {
|
||||||
AnyChannel {
|
AnyChannel {
|
||||||
number: self.number() as u8,
|
number: self.number() as u8,
|
||||||
@ -307,9 +339,13 @@ pub trait Channel: sealed::Channel + Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type-erased (degraded) EXTI channel.
|
||||||
|
///
|
||||||
|
/// This represents ownership over any EXTI channel, known at runtime.
|
||||||
pub struct AnyChannel {
|
pub struct AnyChannel {
|
||||||
number: u8,
|
number: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_peripheral!(AnyChannel);
|
impl_peripheral!(AnyChannel);
|
||||||
impl sealed::Channel for AnyChannel {}
|
impl sealed::Channel for AnyChannel {}
|
||||||
impl Channel for AnyChannel {
|
impl Channel for AnyChannel {
|
||||||
|
@ -59,7 +59,7 @@ impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> {
|
|||||||
const READ_SIZE: usize = super::READ_SIZE;
|
const READ_SIZE: usize = super::READ_SIZE;
|
||||||
|
|
||||||
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.read(offset, bytes)
|
self.blocking_read(offset, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capacity(&self) -> usize {
|
fn capacity(&self) -> usize {
|
||||||
|
@ -12,12 +12,14 @@ use super::{
|
|||||||
use crate::peripherals::FLASH;
|
use crate::peripherals::FLASH;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
/// Internal flash memory driver.
|
||||||
pub struct Flash<'d, MODE = Async> {
|
pub struct Flash<'d, MODE = Async> {
|
||||||
pub(crate) inner: PeripheralRef<'d, FLASH>,
|
pub(crate) inner: PeripheralRef<'d, FLASH>,
|
||||||
pub(crate) _mode: PhantomData<MODE>,
|
pub(crate) _mode: PhantomData<MODE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> Flash<'d, Blocking> {
|
impl<'d> Flash<'d, Blocking> {
|
||||||
|
/// Create a new flash driver, usable in blocking mode.
|
||||||
pub fn new_blocking(p: impl Peripheral<P = FLASH> + 'd) -> Self {
|
pub fn new_blocking(p: impl Peripheral<P = FLASH> + 'd) -> Self {
|
||||||
into_ref!(p);
|
into_ref!(p);
|
||||||
|
|
||||||
@ -29,15 +31,26 @@ impl<'d> Flash<'d, Blocking> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, MODE> Flash<'d, MODE> {
|
impl<'d, MODE> Flash<'d, MODE> {
|
||||||
|
/// Split this flash driver into one instance per flash memory region.
|
||||||
|
///
|
||||||
|
/// See module-level documentation for details on how memory regions work.
|
||||||
pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> {
|
pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> {
|
||||||
assert!(family::is_default_layout());
|
assert!(family::is_default_layout());
|
||||||
FlashLayout::new(self.inner)
|
FlashLayout::new(self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
/// Blocking read.
|
||||||
|
///
|
||||||
|
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
|
||||||
|
/// For example, to read address `0x0800_1234` you have to use offset `0x1234`.
|
||||||
|
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
||||||
blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)
|
blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking write.
|
||||||
|
///
|
||||||
|
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
|
||||||
|
/// For example, to write address `0x0800_1234` you have to use offset `0x1234`.
|
||||||
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
|
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
blocking_write(
|
blocking_write(
|
||||||
@ -50,6 +63,10 @@ impl<'d, MODE> Flash<'d, MODE> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking erase.
|
||||||
|
///
|
||||||
|
/// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.
|
||||||
|
/// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.
|
||||||
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
||||||
unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) }
|
unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) }
|
||||||
}
|
}
|
||||||
@ -206,7 +223,7 @@ impl<MODE> embedded_storage::nor_flash::ReadNorFlash for Flash<'_, MODE> {
|
|||||||
const READ_SIZE: usize = READ_SIZE;
|
const READ_SIZE: usize = READ_SIZE;
|
||||||
|
|
||||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.read(offset, bytes)
|
self.blocking_read(offset, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capacity(&self) -> usize {
|
fn capacity(&self) -> usize {
|
||||||
@ -230,16 +247,28 @@ impl<MODE> embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> {
|
|||||||
foreach_flash_region! {
|
foreach_flash_region! {
|
||||||
($type_name:ident, $write_size:literal, $erase_size:literal) => {
|
($type_name:ident, $write_size:literal, $erase_size:literal) => {
|
||||||
impl<MODE> crate::_generated::flash_regions::$type_name<'_, MODE> {
|
impl<MODE> crate::_generated::flash_regions::$type_name<'_, MODE> {
|
||||||
|
/// Blocking read.
|
||||||
|
///
|
||||||
|
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
|
||||||
|
/// For example, to read address `0x0800_1234` you have to use offset `0x1234`.
|
||||||
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
||||||
blocking_read(self.0.base, self.0.size, offset, bytes)
|
blocking_read(self.0.base, self.0.size, offset, bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::_generated::flash_regions::$type_name<'_, Blocking> {
|
impl crate::_generated::flash_regions::$type_name<'_, Blocking> {
|
||||||
|
/// Blocking write.
|
||||||
|
///
|
||||||
|
/// NOTE: `offset` is an offset from the flash start, NOT an absolute address.
|
||||||
|
/// For example, to write address `0x0800_1234` you have to use offset `0x1234`.
|
||||||
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
|
pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
|
||||||
unsafe { blocking_write(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) }
|
unsafe { blocking_write(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocking erase.
|
||||||
|
///
|
||||||
|
/// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.
|
||||||
|
/// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.
|
||||||
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
||||||
unsafe { blocking_erase(self.0.base, from, to, erase_sector_with_critical_section) }
|
unsafe { blocking_erase(self.0.base, from, to, erase_sector_with_critical_section) }
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
|||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
|
|||||||
|
|
||||||
pub(crate) unsafe fn clear_all_err() {
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
pac::FLASH.sr().modify(|_| {});
|
pac::FLASH.sr().modify(|_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
|||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
|
|||||||
|
|
||||||
pub(crate) unsafe fn clear_all_err() {
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
pac::FLASH.sr().modify(|_| {});
|
pac::FLASH.sr().modify(|_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
|
|||||||
|
|
||||||
pub(crate) fn clear_all_err() {
|
pub(crate) fn clear_all_err() {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
pac::FLASH.sr().modify(|_| {});
|
pac::FLASH.sr().modify(|_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
|||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
|
|||||||
|
|
||||||
pub(crate) unsafe fn clear_all_err() {
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
pac::FLASH.sr().modify(|_| {});
|
pac::FLASH.sr().modify(|_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
|||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +92,6 @@ pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {
|
|||||||
|
|
||||||
pub(crate) unsafe fn clear_all_err() {
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
pac::FLASH.sr().modify(|_| {});
|
pac::FLASH.sr().modify(|_| {});
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
|
|||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ const fn is_dual_bank() -> bool {
|
|||||||
FLASH_REGIONS.len() >= 2
|
FLASH_REGIONS.len() >= 2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ pub(crate) unsafe fn clear_all_err() {
|
|||||||
|
|
||||||
unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
|
unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
bank.sr().modify(|_| {});
|
bank.sr().modify(|_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
|||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
|
|||||||
|
|
||||||
pub(crate) unsafe fn clear_all_err() {
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
// read and write back the same value.
|
// read and write back the same value.
|
||||||
// This clears all "write 0 to clear" bits.
|
// This clears all "write 1 to clear" bits.
|
||||||
pac::FLASH.sr().modify(|_| {});
|
pac::FLASH.sr().modify(|_| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,62 +14,96 @@ pub use crate::_generated::flash_regions::*;
|
|||||||
pub use crate::_generated::MAX_ERASE_SIZE;
|
pub use crate::_generated::MAX_ERASE_SIZE;
|
||||||
pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
|
pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE};
|
||||||
|
|
||||||
|
/// Get whether the default flash layout is being used.
|
||||||
|
///
|
||||||
|
/// In some chips, dual-bank is not default. This will then return `false`
|
||||||
|
/// when dual-bank is enabled.
|
||||||
|
pub fn is_default_layout() -> bool {
|
||||||
|
family::is_default_layout()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all flash regions.
|
||||||
|
pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
|
family::get_flash_regions()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read size (always 1)
|
||||||
pub const READ_SIZE: usize = 1;
|
pub const READ_SIZE: usize = 1;
|
||||||
|
|
||||||
pub struct Blocking;
|
/// Blocking flash mode typestate.
|
||||||
pub struct Async;
|
pub enum Blocking {}
|
||||||
|
/// Async flash mode typestate.
|
||||||
|
pub enum Async {}
|
||||||
|
|
||||||
|
/// Flash memory region
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct FlashRegion {
|
pub struct FlashRegion {
|
||||||
|
/// Bank number.
|
||||||
pub bank: FlashBank,
|
pub bank: FlashBank,
|
||||||
|
/// Absolute base address.
|
||||||
pub base: u32,
|
pub base: u32,
|
||||||
|
/// Size in bytes.
|
||||||
pub size: u32,
|
pub size: u32,
|
||||||
|
/// Erase size (sector size).
|
||||||
pub erase_size: u32,
|
pub erase_size: u32,
|
||||||
|
/// Minimum write size.
|
||||||
pub write_size: u32,
|
pub write_size: u32,
|
||||||
|
/// Erase value (usually `0xFF`, but is `0x00` in some chips)
|
||||||
pub erase_value: u8,
|
pub erase_value: u8,
|
||||||
pub(crate) _ensure_internal: (),
|
pub(crate) _ensure_internal: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct FlashSector {
|
|
||||||
pub bank: FlashBank,
|
|
||||||
pub index_in_bank: u8,
|
|
||||||
pub start: u32,
|
|
||||||
pub size: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum FlashBank {
|
|
||||||
Bank1 = 0,
|
|
||||||
Bank2 = 1,
|
|
||||||
Otp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlashRegion {
|
impl FlashRegion {
|
||||||
|
/// Absolute end address.
|
||||||
pub const fn end(&self) -> u32 {
|
pub const fn end(&self) -> u32 {
|
||||||
self.base + self.size
|
self.base + self.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Number of sectors in the region.
|
||||||
pub const fn sectors(&self) -> u8 {
|
pub const fn sectors(&self) -> u8 {
|
||||||
(self.size / self.erase_size) as u8
|
(self.size / self.erase_size) as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flash sector.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct FlashSector {
|
||||||
|
/// Bank number.
|
||||||
|
pub bank: FlashBank,
|
||||||
|
/// Sector number within the bank.
|
||||||
|
pub index_in_bank: u8,
|
||||||
|
/// Absolute start address.
|
||||||
|
pub start: u32,
|
||||||
|
/// Size in bytes.
|
||||||
|
pub size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flash bank.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum FlashBank {
|
||||||
|
/// Bank 1
|
||||||
|
Bank1 = 0,
|
||||||
|
/// Bank 2
|
||||||
|
Bank2 = 1,
|
||||||
|
/// OTP region
|
||||||
|
Otp,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")]
|
#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")]
|
||||||
#[cfg_attr(flash_f0, path = "f0.rs")]
|
#[cfg_attr(flash_f0, path = "f0.rs")]
|
||||||
#[cfg_attr(flash_f3, path = "f3.rs")]
|
#[cfg_attr(flash_f3, path = "f3.rs")]
|
||||||
#[cfg_attr(flash_f4, path = "f4.rs")]
|
#[cfg_attr(flash_f4, path = "f4.rs")]
|
||||||
#[cfg_attr(flash_f7, path = "f7.rs")]
|
#[cfg_attr(flash_f7, path = "f7.rs")]
|
||||||
#[cfg_attr(flash_g0, path = "g0.rs")]
|
#[cfg_attr(any(flash_g0, flash_g4), path = "g.rs")]
|
||||||
#[cfg_attr(flash_h7, path = "h7.rs")]
|
#[cfg_attr(flash_h7, path = "h7.rs")]
|
||||||
#[cfg_attr(flash_h7ab, path = "h7.rs")]
|
#[cfg_attr(flash_h7ab, path = "h7.rs")]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
not(any(
|
not(any(
|
||||||
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7,
|
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_g4,
|
||||||
flash_h7ab
|
flash_h7, flash_h7ab
|
||||||
)),
|
)),
|
||||||
path = "other.rs"
|
path = "other.rs"
|
||||||
)]
|
)]
|
||||||
@ -78,6 +112,10 @@ mod family;
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use family::*;
|
pub use family::*;
|
||||||
|
|
||||||
|
/// Flash error
|
||||||
|
///
|
||||||
|
/// See STM32 Reference Manual for your chip for details.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||||
|
|
||||||
pub const fn is_default_layout() -> bool {
|
pub(crate) const fn is_default_layout() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||||
&FLASH_REGIONS
|
&FLASH_REGIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,11 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
Self { pin }
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type-erase (degrade) this pin into an `AnyPin`.
|
||||||
|
///
|
||||||
|
/// This converts pin singletons (`PA5`, `PB6`, ...), which
|
||||||
|
/// are all different types, into the same type. It is useful for
|
||||||
|
/// creating arrays of pins, or avoiding generics.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn degrade(self) -> Flex<'d, AnyPin> {
|
pub fn degrade(self) -> Flex<'d, AnyPin> {
|
||||||
// Safety: We are about to drop the other copy of this pin, so
|
// Safety: We are about to drop the other copy of this pin, so
|
||||||
@ -141,40 +146,55 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the pin input level is high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
!self.is_low()
|
!self.ref_is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get whether the pin input level is low.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low(&mut self) -> bool {
|
||||||
|
self.ref_is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub(crate) fn ref_is_low(&self) -> bool {
|
||||||
let state = self.pin.block().idr().read().idr(self.pin.pin() as _);
|
let state = self.pin.block().idr().read().idr(self.pin.pin() as _);
|
||||||
state == vals::Idr::LOW
|
state == vals::Idr::LOW
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current pin input level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.is_high().into()
|
self.is_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the output level is set to high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
!self.is_set_low()
|
!self.ref_is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Get whether the output level is set to low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
|
self.ref_is_set_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn ref_is_set_low(&self) -> bool {
|
||||||
let state = self.pin.block().odr().read().odr(self.pin.pin() as _);
|
let state = self.pin.block().odr().read().odr(self.pin.pin() as _);
|
||||||
state == vals::Odr::LOW
|
state == vals::Odr::LOW
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// Get the current output level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.is_set_high().into()
|
self.is_set_high().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the output as high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_high(&mut self) {
|
pub fn set_high(&mut self) {
|
||||||
self.pin.set_high();
|
self.pin.set_high();
|
||||||
@ -186,6 +206,7 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
self.pin.set_low();
|
self.pin.set_low();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the output level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_level(&mut self, level: Level) {
|
pub fn set_level(&mut self, level: Level) {
|
||||||
match level {
|
match level {
|
||||||
@ -194,7 +215,7 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle pin output
|
/// Toggle the output level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn toggle(&mut self) {
|
pub fn toggle(&mut self) {
|
||||||
if self.is_set_low() {
|
if self.is_set_low() {
|
||||||
@ -232,8 +253,11 @@ impl<'d, T: Pin> Drop for Flex<'d, T> {
|
|||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Pull {
|
pub enum Pull {
|
||||||
|
/// No pull
|
||||||
None,
|
None,
|
||||||
|
/// Pull up
|
||||||
Up,
|
Up,
|
||||||
|
/// Pull down
|
||||||
Down,
|
Down,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +275,9 @@ impl From<Pull> for vals::Pupdr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Speed settings
|
/// Speed settings
|
||||||
|
///
|
||||||
|
/// These vary dpeending on the chip, ceck the reference manual or datasheet for details.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Speed {
|
pub enum Speed {
|
||||||
@ -295,6 +322,7 @@ pub struct Input<'d, T: Pin> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> Input<'d, T> {
|
impl<'d, T: Pin> Input<'d, T> {
|
||||||
|
/// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(pin: impl Peripheral<P = T> + 'd, pull: Pull) -> Self {
|
pub fn new(pin: impl Peripheral<P = T> + 'd, pull: Pull) -> Self {
|
||||||
let mut pin = Flex::new(pin);
|
let mut pin = Flex::new(pin);
|
||||||
@ -302,6 +330,11 @@ impl<'d, T: Pin> Input<'d, T> {
|
|||||||
Self { pin }
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type-erase (degrade) this pin into an `AnyPin`.
|
||||||
|
///
|
||||||
|
/// This converts pin singletons (`PA5`, `PB6`, ...), which
|
||||||
|
/// are all different types, into the same type. It is useful for
|
||||||
|
/// creating arrays of pins, or avoiding generics.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn degrade(self) -> Input<'d, AnyPin> {
|
pub fn degrade(self) -> Input<'d, AnyPin> {
|
||||||
Input {
|
Input {
|
||||||
@ -309,18 +342,21 @@ impl<'d, T: Pin> Input<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the pin input level is high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
self.pin.is_high()
|
self.pin.is_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the pin input level is low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
self.pin.is_low()
|
self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current pin input level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.pin.get_level()
|
self.pin.get_level()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,7 +365,9 @@ impl<'d, T: Pin> Input<'d, T> {
|
|||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Level {
|
pub enum Level {
|
||||||
|
/// Low
|
||||||
Low,
|
Low,
|
||||||
|
/// High
|
||||||
High,
|
High,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,6 +399,7 @@ pub struct Output<'d, T: Pin> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> Output<'d, T> {
|
impl<'d, T: Pin> Output<'d, T> {
|
||||||
|
/// Create GPIO output driver for a [Pin] with the provided [Level] and [Speed] configuration.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, speed: Speed) -> Self {
|
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, speed: Speed) -> Self {
|
||||||
let mut pin = Flex::new(pin);
|
let mut pin = Flex::new(pin);
|
||||||
@ -372,6 +411,11 @@ impl<'d, T: Pin> Output<'d, T> {
|
|||||||
Self { pin }
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type-erase (degrade) this pin into an `AnyPin`.
|
||||||
|
///
|
||||||
|
/// This converts pin singletons (`PA5`, `PB6`, ...), which
|
||||||
|
/// are all different types, into the same type. It is useful for
|
||||||
|
/// creating arrays of pins, or avoiding generics.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn degrade(self) -> Output<'d, AnyPin> {
|
pub fn degrade(self) -> Output<'d, AnyPin> {
|
||||||
Output {
|
Output {
|
||||||
@ -399,19 +443,19 @@ impl<'d, T: Pin> Output<'d, T> {
|
|||||||
|
|
||||||
/// Is the output pin set as high?
|
/// Is the output pin set as high?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
self.pin.is_set_high()
|
self.pin.is_set_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
self.pin.is_set_low()
|
self.pin.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// What level output is set to
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.pin.get_output_level()
|
self.pin.get_output_level()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,6 +476,7 @@ pub struct OutputOpenDrain<'d, T: Pin> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
||||||
|
/// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed], [Pull] configuration.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self {
|
pub fn new(pin: impl Peripheral<P = T> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self {
|
||||||
let mut pin = Flex::new(pin);
|
let mut pin = Flex::new(pin);
|
||||||
@ -445,6 +490,11 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
|||||||
Self { pin }
|
Self { pin }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type-erase (degrade) this pin into an `AnyPin`.
|
||||||
|
///
|
||||||
|
/// This converts pin singletons (`PA5`, `PB6`, ...), which
|
||||||
|
/// are all different types, into the same type. It is useful for
|
||||||
|
/// creating arrays of pins, or avoiding generics.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn degrade(self) -> Output<'d, AnyPin> {
|
pub fn degrade(self) -> Output<'d, AnyPin> {
|
||||||
Output {
|
Output {
|
||||||
@ -452,19 +502,21 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the pin input level is high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_high(&self) -> bool {
|
pub fn is_high(&mut self) -> bool {
|
||||||
!self.pin.is_low()
|
!self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get whether the pin input level is low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_low(&self) -> bool {
|
pub fn is_low(&mut self) -> bool {
|
||||||
self.pin.is_low()
|
self.pin.is_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns current pin level
|
/// Get the current pin input level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_level(&self) -> Level {
|
pub fn get_level(&mut self) -> Level {
|
||||||
self.pin.get_level()
|
self.pin.get_level()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,21 +538,21 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
|||||||
self.pin.set_level(level);
|
self.pin.set_level(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as high?
|
/// Get whether the output level is set to high.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
pub fn is_set_high(&mut self) -> bool {
|
||||||
self.pin.is_set_high()
|
self.pin.is_set_high()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Get whether the output level is set to low.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
pub fn is_set_low(&mut self) -> bool {
|
||||||
self.pin.is_set_low()
|
self.pin.is_set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What level output is set to
|
/// Get the current output level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_output_level(&self) -> Level {
|
pub fn get_output_level(&mut self) -> Level {
|
||||||
self.pin.get_output_level()
|
self.pin.get_output_level()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,8 +563,11 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// GPIO output type
|
||||||
pub enum OutputType {
|
pub enum OutputType {
|
||||||
|
/// Drive the pin both high or low.
|
||||||
PushPull,
|
PushPull,
|
||||||
|
/// Drive the pin low, or don't drive it at all if the output level is high.
|
||||||
OpenDrain,
|
OpenDrain,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,6 +580,7 @@ impl From<OutputType> for sealed::AFType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -532,8 +588,11 @@ pub(crate) mod sealed {
|
|||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum AFType {
|
pub enum AFType {
|
||||||
|
/// Input
|
||||||
Input,
|
Input,
|
||||||
|
/// Output, drive the pin both high or low.
|
||||||
OutputPushPull,
|
OutputPushPull,
|
||||||
|
/// Output, drive the pin low, or don't drive it at all if the output level is high.
|
||||||
OutputOpenDrain,
|
OutputOpenDrain,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,7 +735,11 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// GPIO pin trait.
|
||||||
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'static {
|
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'static {
|
||||||
|
/// EXTI channel assigned to this pin.
|
||||||
|
///
|
||||||
|
/// For example, PC4 uses EXTI4.
|
||||||
#[cfg(feature = "exti")]
|
#[cfg(feature = "exti")]
|
||||||
type ExtiChannel: crate::exti::Channel;
|
type ExtiChannel: crate::exti::Channel;
|
||||||
|
|
||||||
@ -692,7 +755,11 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
|
|||||||
self._port()
|
self._port()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert from concrete pin type PX_XX to type erased `AnyPin`.
|
/// Type-erase (degrade) this pin into an `AnyPin`.
|
||||||
|
///
|
||||||
|
/// This converts pin singletons (`PA5`, `PB6`, ...), which
|
||||||
|
/// are all different types, into the same type. It is useful for
|
||||||
|
/// creating arrays of pins, or avoiding generics.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn degrade(self) -> AnyPin {
|
fn degrade(self) -> AnyPin {
|
||||||
AnyPin {
|
AnyPin {
|
||||||
@ -701,12 +768,15 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type-erased GPIO pin
|
/// Type-erased GPIO pin
|
||||||
pub struct AnyPin {
|
pub struct AnyPin {
|
||||||
pin_port: u8,
|
pin_port: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnyPin {
|
impl AnyPin {
|
||||||
|
/// Unsafely create an `AnyPin` from a pin+port number.
|
||||||
|
///
|
||||||
|
/// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc...
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn steal(pin_port: u8) -> Self {
|
pub unsafe fn steal(pin_port: u8) -> Self {
|
||||||
Self { pin_port }
|
Self { pin_port }
|
||||||
@ -717,6 +787,8 @@ impl AnyPin {
|
|||||||
self.pin_port / 16
|
self.pin_port / 16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the GPIO register block for this pin.
|
||||||
|
#[cfg(feature = "unstable-pac")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn block(&self) -> gpio::Gpio {
|
pub fn block(&self) -> gpio::Gpio {
|
||||||
pac::GPIO(self._port() as _)
|
pac::GPIO(self._port() as _)
|
||||||
@ -777,12 +849,12 @@ impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Input<'d, T> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.pin.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,13 +877,13 @@ impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Output<'d, T> {
|
|||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,13 +915,13 @@ impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d,
|
|||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.pin.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,12 +939,12 @@ impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Flex<'d, T> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(!self.ref_is_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.ref_is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -895,13 +967,13 @@ impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Flex<'d, T> {
|
|||||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(!self.ref_is_set_low())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.ref_is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,12 +992,12 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -948,13 +1020,13 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -972,12 +1044,12 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d, T> {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -996,13 +1068,13 @@ impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1016,12 +1088,12 @@ impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrai
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_high())
|
Ok(self.is_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_low())
|
Ok(self.is_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1051,17 +1123,18 @@ impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> {
|
|||||||
|
|
||||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_high())
|
Ok(self.is_set_high())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the output pin set as low?
|
/// Is the output pin set as low?
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.is_set_low())
|
Ok(self.is_set_low())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Low-level GPIO manipulation.
|
||||||
#[cfg(feature = "unstable-pac")]
|
#[cfg(feature = "unstable-pac")]
|
||||||
pub mod low_level {
|
pub mod low_level {
|
||||||
pub use super::sealed::*;
|
pub use super::sealed::*;
|
||||||
|
@ -13,15 +13,23 @@ use embassy_sync::waitqueue::AtomicWaker;
|
|||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
|
|
||||||
|
/// I2C error.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// Bus error
|
||||||
Bus,
|
Bus,
|
||||||
|
/// Arbitration lost
|
||||||
Arbitration,
|
Arbitration,
|
||||||
|
/// ACK not received (either to the address or to a data byte)
|
||||||
Nack,
|
Nack,
|
||||||
|
/// Timeout
|
||||||
Timeout,
|
Timeout,
|
||||||
|
/// CRC error
|
||||||
Crc,
|
Crc,
|
||||||
|
/// Overrun error
|
||||||
Overrun,
|
Overrun,
|
||||||
|
/// Zero-length transfers are not allowed.
|
||||||
ZeroLengthTransfer,
|
ZeroLengthTransfer,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +55,11 @@ pub(crate) mod sealed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// I2C peripheral instance
|
||||||
pub trait Instance: sealed::Instance + 'static {
|
pub trait Instance: sealed::Instance + 'static {
|
||||||
|
/// Event interrupt for this instance
|
||||||
type EventInterrupt: interrupt::typelevel::Interrupt;
|
type EventInterrupt: interrupt::typelevel::Interrupt;
|
||||||
|
/// Error interrupt for this instance
|
||||||
type ErrorInterrupt: interrupt::typelevel::Interrupt;
|
type ErrorInterrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +68,7 @@ pin_trait!(SdaPin, Instance);
|
|||||||
dma_trait!(RxDma, Instance);
|
dma_trait!(RxDma, Instance);
|
||||||
dma_trait!(TxDma, Instance);
|
dma_trait!(TxDma, Instance);
|
||||||
|
|
||||||
/// Interrupt handler.
|
/// Event interrupt handler.
|
||||||
pub struct EventInterruptHandler<T: Instance> {
|
pub struct EventInterruptHandler<T: Instance> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
@ -68,6 +79,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error interrupt handler.
|
||||||
pub struct ErrorInterruptHandler<T: Instance> {
|
pub struct ErrorInterruptHandler<T: Instance> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,17 @@ impl Into<u8> for QspiMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QSPI lane width
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum QspiWidth {
|
pub enum QspiWidth {
|
||||||
|
/// None
|
||||||
NONE,
|
NONE,
|
||||||
|
/// Single lane
|
||||||
SING,
|
SING,
|
||||||
|
/// Dual lanes
|
||||||
DUAL,
|
DUAL,
|
||||||
|
/// Quad lanes
|
||||||
QUAD,
|
QUAD,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,10 +43,13 @@ impl Into<u8> for QspiWidth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flash bank selection
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum FlashSelection {
|
pub enum FlashSelection {
|
||||||
|
/// Bank 1
|
||||||
Flash1,
|
Flash1,
|
||||||
|
/// Bank 2
|
||||||
Flash2,
|
Flash2,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +62,8 @@ impl Into<bool> for FlashSelection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QSPI memory size.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum MemorySize {
|
pub enum MemorySize {
|
||||||
_1KiB,
|
_1KiB,
|
||||||
@ -113,11 +123,16 @@ impl Into<u8> for MemorySize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// QSPI Address size
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum AddressSize {
|
pub enum AddressSize {
|
||||||
|
/// 8-bit address
|
||||||
_8Bit,
|
_8Bit,
|
||||||
|
/// 16-bit address
|
||||||
_16Bit,
|
_16Bit,
|
||||||
|
/// 24-bit address
|
||||||
_24bit,
|
_24bit,
|
||||||
|
/// 32-bit address
|
||||||
_32bit,
|
_32bit,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,8 +147,10 @@ impl Into<u8> for AddressSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Time the Chip Select line stays high.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ChipSelectHightTime {
|
pub enum ChipSelectHighTime {
|
||||||
_1Cycle,
|
_1Cycle,
|
||||||
_2Cycle,
|
_2Cycle,
|
||||||
_3Cycle,
|
_3Cycle,
|
||||||
@ -144,21 +161,23 @@ pub enum ChipSelectHightTime {
|
|||||||
_8Cycle,
|
_8Cycle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<u8> for ChipSelectHightTime {
|
impl Into<u8> for ChipSelectHighTime {
|
||||||
fn into(self) -> u8 {
|
fn into(self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
ChipSelectHightTime::_1Cycle => 0,
|
ChipSelectHighTime::_1Cycle => 0,
|
||||||
ChipSelectHightTime::_2Cycle => 1,
|
ChipSelectHighTime::_2Cycle => 1,
|
||||||
ChipSelectHightTime::_3Cycle => 2,
|
ChipSelectHighTime::_3Cycle => 2,
|
||||||
ChipSelectHightTime::_4Cycle => 3,
|
ChipSelectHighTime::_4Cycle => 3,
|
||||||
ChipSelectHightTime::_5Cycle => 4,
|
ChipSelectHighTime::_5Cycle => 4,
|
||||||
ChipSelectHightTime::_6Cycle => 5,
|
ChipSelectHighTime::_6Cycle => 5,
|
||||||
ChipSelectHightTime::_7Cycle => 6,
|
ChipSelectHighTime::_7Cycle => 6,
|
||||||
ChipSelectHightTime::_8Cycle => 7,
|
ChipSelectHighTime::_8Cycle => 7,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// FIFO threshold.
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum FIFOThresholdLevel {
|
pub enum FIFOThresholdLevel {
|
||||||
_1Bytes,
|
_1Bytes,
|
||||||
@ -234,6 +253,8 @@ impl Into<u8> for FIFOThresholdLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dummy cycle count
|
||||||
|
#[allow(missing_docs)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum DummyCycles {
|
pub enum DummyCycles {
|
||||||
_0,
|
_0,
|
||||||
|
@ -54,7 +54,7 @@ pub struct Config {
|
|||||||
/// Number of bytes to trigger FIFO threshold flag.
|
/// Number of bytes to trigger FIFO threshold flag.
|
||||||
pub fifo_threshold: FIFOThresholdLevel,
|
pub fifo_threshold: FIFOThresholdLevel,
|
||||||
/// Minimum number of cycles that chip select must be high between issued commands
|
/// Minimum number of cycles that chip select must be high between issued commands
|
||||||
pub cs_high_time: ChipSelectHightTime,
|
pub cs_high_time: ChipSelectHighTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -64,7 +64,7 @@ impl Default for Config {
|
|||||||
address_size: AddressSize::_24bit,
|
address_size: AddressSize::_24bit,
|
||||||
prescaler: 128,
|
prescaler: 128,
|
||||||
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
||||||
cs_high_time: ChipSelectHightTime::_5Cycle,
|
cs_high_time: ChipSelectHighTime::_5Cycle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
|||||||
Some(nss.map_into()),
|
Some(nss.map_into()),
|
||||||
dma,
|
dma,
|
||||||
config,
|
config,
|
||||||
FlashSelection::Flash2,
|
FlashSelection::Flash1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,9 @@ pub struct Pll {
|
|||||||
pub mul: PllMul,
|
pub mul: PllMul,
|
||||||
|
|
||||||
/// PLL P division factor. If None, PLL P output is disabled.
|
/// PLL P division factor. If None, PLL P output is disabled.
|
||||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
/// On PLL1, it must be even for most series (in particular,
|
||||||
|
/// it cannot be 1 in series other than STM32H723/733,
|
||||||
|
/// STM32H725/735 and STM32H730.)
|
||||||
pub divp: Option<PllDiv>,
|
pub divp: Option<PllDiv>,
|
||||||
/// PLL Q division factor. If None, PLL Q output is disabled.
|
/// PLL Q division factor. If None, PLL Q output is disabled.
|
||||||
pub divq: Option<PllDiv>,
|
pub divq: Option<PllDiv>,
|
||||||
@ -476,7 +478,14 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
VoltageScale::Scale2 => (Hertz(160_000_000), Hertz(160_000_000), Hertz(80_000_000)),
|
VoltageScale::Scale2 => (Hertz(160_000_000), Hertz(160_000_000), Hertz(80_000_000)),
|
||||||
VoltageScale::Scale3 => (Hertz(88_000_000), Hertz(88_000_000), Hertz(44_000_000)),
|
VoltageScale::Scale3 => (Hertz(88_000_000), Hertz(88_000_000), Hertz(44_000_000)),
|
||||||
};
|
};
|
||||||
#[cfg(all(stm32h7, not(pwr_h7rm0455)))]
|
#[cfg(pwr_h7rm0468)]
|
||||||
|
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
||||||
|
VoltageScale::Scale0 => (Hertz(520_000_000), Hertz(275_000_000), Hertz(137_500_000)),
|
||||||
|
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
||||||
|
VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),
|
||||||
|
VoltageScale::Scale3 => (Hertz(170_000_000), Hertz(85_000_000), Hertz(42_500_000)),
|
||||||
|
};
|
||||||
|
#[cfg(all(stm32h7, not(any(pwr_h7rm0455, pwr_h7rm0468))))]
|
||||||
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
||||||
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
|
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
|
||||||
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
||||||
@ -729,9 +738,12 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
|
|
||||||
let p = config.divp.map(|div| {
|
let p = config.divp.map(|div| {
|
||||||
if num == 0 {
|
if num == 0 {
|
||||||
// on PLL1, DIVP must be even.
|
// on PLL1, DIVP must be even for most series.
|
||||||
// The enum value is 1 less than the divider, so check it's odd.
|
// The enum value is 1 less than the divider, so check it's odd.
|
||||||
|
#[cfg(not(pwr_h7rm0468))]
|
||||||
assert!(div.to_bits() % 2 == 1);
|
assert!(div.to_bits() % 2 == 1);
|
||||||
|
#[cfg(pwr_h7rm0468)]
|
||||||
|
assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vco_clk / div
|
vco_clk / div
|
||||||
@ -820,7 +832,7 @@ fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(flash_h7)]
|
#[cfg(all(flash_h7, not(pwr_h7rm0468)))]
|
||||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||||
// VOS 0 range VCORE 1.26V - 1.40V
|
// VOS 0 range VCORE 1.26V - 1.40V
|
||||||
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
|
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
|
||||||
@ -849,6 +861,30 @@ fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// See RM0468 Rev 3 Table 16. FLASH recommended number of wait
|
||||||
|
// states and programming delay
|
||||||
|
#[cfg(all(flash_h7, pwr_h7rm0468))]
|
||||||
|
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||||
|
// VOS 0 range VCORE 1.26V - 1.40V
|
||||||
|
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
|
||||||
|
(VoltageScale::Scale0, ..=140_000_000) => (1, 1),
|
||||||
|
(VoltageScale::Scale0, ..=210_000_000) => (2, 2),
|
||||||
|
(VoltageScale::Scale0, ..=275_000_000) => (3, 3),
|
||||||
|
// VOS 1 range VCORE 1.15V - 1.26V
|
||||||
|
(VoltageScale::Scale1, ..=67_000_000) => (0, 0),
|
||||||
|
(VoltageScale::Scale1, ..=133_000_000) => (1, 1),
|
||||||
|
(VoltageScale::Scale1, ..=200_000_000) => (2, 2),
|
||||||
|
// VOS 2 range VCORE 1.05V - 1.15V
|
||||||
|
(VoltageScale::Scale2, ..=50_000_000) => (0, 0),
|
||||||
|
(VoltageScale::Scale2, ..=100_000_000) => (1, 1),
|
||||||
|
(VoltageScale::Scale2, ..=150_000_000) => (2, 2),
|
||||||
|
// VOS 3 range VCORE 0.95V - 1.05V
|
||||||
|
(VoltageScale::Scale3, ..=35_000_000) => (0, 0),
|
||||||
|
(VoltageScale::Scale3, ..=70_000_000) => (1, 1),
|
||||||
|
(VoltageScale::Scale3, ..=85_000_000) => (2, 2),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
|
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
|
||||||
// states and programming delay
|
// states and programming delay
|
||||||
#[cfg(flash_h7ab)]
|
#[cfg(flash_h7ab)]
|
||||||
|
@ -583,10 +583,10 @@ fn get_ring_buffer<'d, T: Instance, C: Channel, W: word::Word>(
|
|||||||
};
|
};
|
||||||
match tx_rx {
|
match tx_rx {
|
||||||
TxRx::Transmitter => RingBuffer::Writable(unsafe {
|
TxRx::Transmitter => RingBuffer::Writable(unsafe {
|
||||||
WritableRingBuffer::new_write(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
|
WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
|
||||||
}),
|
}),
|
||||||
TxRx::Receiver => RingBuffer::Readable(unsafe {
|
TxRx::Receiver => RingBuffer::Readable(unsafe {
|
||||||
ReadableRingBuffer::new_read(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
|
ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,16 +474,29 @@ impl Driver for RtcDriver {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let safe_timestamp = timestamp.max(t + 3);
|
|
||||||
|
|
||||||
// Write the CCR value regardless of whether we're going to enable it now or not.
|
// Write the CCR value regardless of whether we're going to enable it now or not.
|
||||||
// This way, when we enable it later, the right value is already set.
|
// This way, when we enable it later, the right value is already set.
|
||||||
r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16));
|
r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
|
||||||
|
|
||||||
// Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
|
// Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
|
||||||
let diff = timestamp - t;
|
let diff = timestamp - t;
|
||||||
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
|
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
|
||||||
|
|
||||||
|
// Reevaluate if the alarm timestamp is still in the future
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
// If alarm timestamp has passed since we set it, we have a race condition and
|
||||||
|
// the alarm may or may not have fired.
|
||||||
|
// Disarm the alarm and return `false` to indicate that.
|
||||||
|
// It is the caller's responsibility to handle this ambiguity.
|
||||||
|
r.dier().modify(|w| w.set_ccie(n + 1, false));
|
||||||
|
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're confident the alarm will ring in the future.
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
macro_rules! pin_trait {
|
macro_rules! pin_trait {
|
||||||
($signal:ident, $instance:path) => {
|
($signal:ident, $instance:path) => {
|
||||||
|
#[doc = concat!(stringify!($signal), " pin trait")]
|
||||||
pub trait $signal<T: $instance>: crate::gpio::Pin {
|
pub trait $signal<T: $instance>: crate::gpio::Pin {
|
||||||
|
#[doc = concat!("Get the AF number needed to use this pin as", stringify!($signal))]
|
||||||
fn af_num(&self) -> u8;
|
fn af_num(&self) -> u8;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -22,7 +24,11 @@ macro_rules! pin_trait_impl {
|
|||||||
|
|
||||||
macro_rules! dma_trait {
|
macro_rules! dma_trait {
|
||||||
($signal:ident, $instance:path) => {
|
($signal:ident, $instance:path) => {
|
||||||
|
#[doc = concat!(stringify!($signal), " DMA request trait")]
|
||||||
pub trait $signal<T: $instance>: crate::dma::Channel {
|
pub trait $signal<T: $instance>: crate::dma::Channel {
|
||||||
|
#[doc = concat!("Get the DMA request number needed to use this channel as", stringify!($signal))]
|
||||||
|
/// Note: in some chips, ST calls this the "channel", and calls channels "streams".
|
||||||
|
/// `embassy-stm32` always uses the "channel" and "request number" names.
|
||||||
fn request(&self) -> crate::dma::Request;
|
fn request(&self) -> crate::dma::Request;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -39,7 +39,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
|
|||||||
let rx_dma = unsafe { self.rx_dma.clone_unchecked() };
|
let rx_dma = unsafe { self.rx_dma.clone_unchecked() };
|
||||||
let _peri = unsafe { self._peri.clone_unchecked() };
|
let _peri = unsafe { self._peri.clone_unchecked() };
|
||||||
|
|
||||||
let ring_buf = unsafe { ReadableRingBuffer::new_read(rx_dma, request, rdr(T::regs()), dma_buf, opts) };
|
let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(T::regs()), dma_buf, opts) };
|
||||||
|
|
||||||
// Don't disable the clock
|
// Don't disable the clock
|
||||||
mem::forget(self);
|
mem::forget(self);
|
||||||
|
@ -24,8 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## 0.1.3 - 2023-08-28
|
## 0.1.3 - 2023-08-28
|
||||||
|
|
||||||
- Update `embedded-hal-async` to `1.0.0-rc.2`
|
- Update `embedded-hal-async` to `1.0.0-rc.3`
|
||||||
- Update `embedded-hal v1` to `1.0.0-rc.2`
|
- Update `embedded-hal v1` to `1.0.0-rc.3`
|
||||||
|
|
||||||
## 0.1.2 - 2023-07-05
|
## 0.1.2 - 2023-07-05
|
||||||
|
|
||||||
|
@ -235,8 +235,8 @@ defmt = { version = "0.3", optional = true }
|
|||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
|
@ -108,6 +108,10 @@ pub trait Driver: Send + Sync + 'static {
|
|||||||
/// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
|
/// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
|
||||||
/// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set,
|
/// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set,
|
||||||
/// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
|
/// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
|
||||||
|
/// There is a rare third possibility that the alarm was barely in the future, and by the time it was enabled, it had slipped into the
|
||||||
|
/// past. This is can be detected by double-checking that the alarm is still in the future after enabling it; if it isn't, `false`
|
||||||
|
/// should also be returned to indicate that the callback may have been called already by the alarm, but it is not guaranteed, so the
|
||||||
|
/// caller should also call the callback, just like in the more common `false` case. (Note: This requires idempotency of the callback.)
|
||||||
///
|
///
|
||||||
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
|
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
|
||||||
///
|
///
|
||||||
|
32
embassy-usb-dfu/Cargo.toml
Normal file
32
embassy-usb-dfu/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "embassy-usb-dfu"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "An implementation of the USB DFU 1.1 protocol, using embassy-boot"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous"
|
||||||
|
]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "2.4.1"
|
||||||
|
cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true }
|
||||||
|
defmt = { version = "0.3.5", optional = true }
|
||||||
|
embassy-boot = { version = "0.1.1", path = "../embassy-boot/boot" }
|
||||||
|
# embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
|
embassy-futures = { version = "0.1.1", path = "../embassy-futures" }
|
||||||
|
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||||
|
embassy-time = { version = "0.2.0", path = "../embassy-time" }
|
||||||
|
embassy-usb = { version = "0.1.0", path = "../embassy-usb", default-features = false }
|
||||||
|
embedded-storage = { version = "0.3.1" }
|
||||||
|
esp32c3-hal = { version = "0.13.0", optional = true, default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dfu = []
|
||||||
|
application = []
|
||||||
|
defmt = ["dep:defmt"]
|
135
embassy-usb-dfu/src/application.rs
Normal file
135
embassy-usb-dfu/src/application.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use embassy_boot::BlockingFirmwareState;
|
||||||
|
use embassy_time::{Duration, Instant};
|
||||||
|
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||||
|
use embassy_usb::driver::Driver;
|
||||||
|
use embassy_usb::{Builder, Handler};
|
||||||
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
|
use crate::consts::{
|
||||||
|
DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT,
|
||||||
|
USB_CLASS_APPN_SPEC,
|
||||||
|
};
|
||||||
|
use crate::Reset;
|
||||||
|
|
||||||
|
/// Internal state for the DFU class
|
||||||
|
pub struct Control<'d, STATE: NorFlash, RST: Reset> {
|
||||||
|
firmware_state: BlockingFirmwareState<'d, STATE>,
|
||||||
|
attrs: DfuAttributes,
|
||||||
|
state: State,
|
||||||
|
timeout: Option<Duration>,
|
||||||
|
detach_start: Option<Instant>,
|
||||||
|
_rst: PhantomData<RST>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, STATE: NorFlash, RST: Reset> Control<'d, STATE, RST> {
|
||||||
|
pub fn new(firmware_state: BlockingFirmwareState<'d, STATE>, attrs: DfuAttributes) -> Self {
|
||||||
|
Control {
|
||||||
|
firmware_state,
|
||||||
|
attrs,
|
||||||
|
state: State::AppIdle,
|
||||||
|
detach_start: None,
|
||||||
|
timeout: None,
|
||||||
|
_rst: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, STATE: NorFlash, RST: Reset> Handler for Control<'d, STATE, RST> {
|
||||||
|
fn reset(&mut self) {
|
||||||
|
if let Some(start) = self.detach_start {
|
||||||
|
let delta = Instant::now() - start;
|
||||||
|
let timeout = self.timeout.unwrap();
|
||||||
|
trace!(
|
||||||
|
"Received RESET with delta = {}, timeout = {}",
|
||||||
|
delta.as_millis(),
|
||||||
|
timeout.as_millis()
|
||||||
|
);
|
||||||
|
if delta < timeout {
|
||||||
|
self.firmware_state
|
||||||
|
.mark_dfu()
|
||||||
|
.expect("Failed to mark DFU mode in bootloader");
|
||||||
|
RST::sys_reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_out(
|
||||||
|
&mut self,
|
||||||
|
req: embassy_usb::control::Request,
|
||||||
|
_: &[u8],
|
||||||
|
) -> Option<embassy_usb::control::OutResponse> {
|
||||||
|
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("Received request {}", req);
|
||||||
|
|
||||||
|
match Request::try_from(req.request) {
|
||||||
|
Ok(Request::Detach) => {
|
||||||
|
trace!("Received DETACH, awaiting USB reset");
|
||||||
|
self.detach_start = Some(Instant::now());
|
||||||
|
self.timeout = Some(Duration::from_millis(req.value as u64));
|
||||||
|
self.state = State::AppDetach;
|
||||||
|
Some(OutResponse::Accepted)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_in<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
req: embassy_usb::control::Request,
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
) -> Option<embassy_usb::control::InResponse<'a>> {
|
||||||
|
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("Received request {}", req);
|
||||||
|
|
||||||
|
match Request::try_from(req.request) {
|
||||||
|
Ok(Request::GetStatus) => {
|
||||||
|
buf[0..6].copy_from_slice(&[Status::Ok as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]);
|
||||||
|
Some(InResponse::Accepted(buf))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation of the USB DFU 1.1 runtime protocol
|
||||||
|
///
|
||||||
|
/// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device. The USB builder can be used as normal once this is complete.
|
||||||
|
/// The handler is responsive to DFU GetStatus and Detach commands.
|
||||||
|
///
|
||||||
|
/// Once a detach command, followed by a USB reset is received by the host, a magic number will be written into the bootloader state partition to indicate that
|
||||||
|
/// it should expose a DFU device, and a software reset will be issued.
|
||||||
|
///
|
||||||
|
/// To apply USB DFU updates, the bootloader must be capable of recognizing the DFU magic and exposing a device to handle the full DFU transaction with the host.
|
||||||
|
pub fn usb_dfu<'d, D: Driver<'d>, STATE: NorFlash, RST: Reset>(
|
||||||
|
builder: &mut Builder<'d, D>,
|
||||||
|
handler: &'d mut Control<'d, STATE, RST>,
|
||||||
|
timeout: Duration,
|
||||||
|
) {
|
||||||
|
let mut func = builder.function(0x00, 0x00, 0x00);
|
||||||
|
let mut iface = func.interface();
|
||||||
|
let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_RT, None);
|
||||||
|
let timeout = timeout.as_millis() as u16;
|
||||||
|
alt.descriptor(
|
||||||
|
DESC_DFU_FUNCTIONAL,
|
||||||
|
&[
|
||||||
|
handler.attrs.bits(),
|
||||||
|
(timeout & 0xff) as u8,
|
||||||
|
((timeout >> 8) & 0xff) as u8,
|
||||||
|
0x40,
|
||||||
|
0x00, // 64B control buffer size for application side
|
||||||
|
0x10,
|
||||||
|
0x01, // DFU 1.1
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
drop(func);
|
||||||
|
builder.handler(handler);
|
||||||
|
}
|
189
embassy-usb-dfu/src/bootloader.rs
Normal file
189
embassy-usb-dfu/src/bootloader.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater};
|
||||||
|
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||||
|
use embassy_usb::driver::Driver;
|
||||||
|
use embassy_usb::{Builder, Handler};
|
||||||
|
use embedded_storage::nor_flash::{NorFlash, NorFlashErrorKind};
|
||||||
|
|
||||||
|
use crate::consts::{
|
||||||
|
DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU,
|
||||||
|
USB_CLASS_APPN_SPEC,
|
||||||
|
};
|
||||||
|
use crate::Reset;
|
||||||
|
|
||||||
|
/// Internal state for USB DFU
|
||||||
|
pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> {
|
||||||
|
updater: BlockingFirmwareUpdater<'d, DFU, STATE>,
|
||||||
|
attrs: DfuAttributes,
|
||||||
|
state: State,
|
||||||
|
status: Status,
|
||||||
|
offset: usize,
|
||||||
|
_rst: PhantomData<RST>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Control<'d, DFU, STATE, RST, BLOCK_SIZE> {
|
||||||
|
pub fn new(updater: BlockingFirmwareUpdater<'d, DFU, STATE>, attrs: DfuAttributes) -> Self {
|
||||||
|
Self {
|
||||||
|
updater,
|
||||||
|
attrs,
|
||||||
|
state: State::DfuIdle,
|
||||||
|
status: Status::Ok,
|
||||||
|
offset: 0,
|
||||||
|
_rst: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_state(&mut self) {
|
||||||
|
self.offset = 0;
|
||||||
|
self.state = State::DfuIdle;
|
||||||
|
self.status = Status::Ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler
|
||||||
|
for Control<'d, DFU, STATE, RST, BLOCK_SIZE>
|
||||||
|
{
|
||||||
|
fn control_out(
|
||||||
|
&mut self,
|
||||||
|
req: embassy_usb::control::Request,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<embassy_usb::control::OutResponse> {
|
||||||
|
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match Request::try_from(req.request) {
|
||||||
|
Ok(Request::Abort) => {
|
||||||
|
self.reset_state();
|
||||||
|
Some(OutResponse::Accepted)
|
||||||
|
}
|
||||||
|
Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => {
|
||||||
|
if req.value == 0 {
|
||||||
|
self.state = State::Download;
|
||||||
|
self.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = AlignedBuffer([0; BLOCK_SIZE]);
|
||||||
|
buf.as_mut()[..data.len()].copy_from_slice(data);
|
||||||
|
|
||||||
|
if req.length == 0 {
|
||||||
|
match self.updater.mark_updated() {
|
||||||
|
Ok(_) => {
|
||||||
|
self.status = Status::Ok;
|
||||||
|
self.state = State::ManifestSync;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.state = State::Error;
|
||||||
|
match e {
|
||||||
|
embassy_boot::FirmwareUpdaterError::Flash(e) => match e {
|
||||||
|
NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite,
|
||||||
|
NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress,
|
||||||
|
_ => self.status = Status::ErrUnknown,
|
||||||
|
},
|
||||||
|
embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify,
|
||||||
|
embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if self.state != State::Download {
|
||||||
|
// Unexpected DNLOAD while chip is waiting for a GETSTATUS
|
||||||
|
self.status = Status::ErrUnknown;
|
||||||
|
self.state = State::Error;
|
||||||
|
return Some(OutResponse::Rejected);
|
||||||
|
}
|
||||||
|
match self.updater.write_firmware(self.offset, buf.as_ref()) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.status = Status::Ok;
|
||||||
|
self.state = State::DlSync;
|
||||||
|
self.offset += data.len();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.state = State::Error;
|
||||||
|
match e {
|
||||||
|
embassy_boot::FirmwareUpdaterError::Flash(e) => match e {
|
||||||
|
NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite,
|
||||||
|
NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress,
|
||||||
|
_ => self.status = Status::ErrUnknown,
|
||||||
|
},
|
||||||
|
embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify,
|
||||||
|
embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(OutResponse::Accepted)
|
||||||
|
}
|
||||||
|
Ok(Request::Detach) => Some(OutResponse::Accepted), // Device is already in DFU mode
|
||||||
|
Ok(Request::ClrStatus) => {
|
||||||
|
self.reset_state();
|
||||||
|
Some(OutResponse::Accepted)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_in<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
req: embassy_usb::control::Request,
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
) -> Option<embassy_usb::control::InResponse<'a>> {
|
||||||
|
if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match Request::try_from(req.request) {
|
||||||
|
Ok(Request::GetStatus) => {
|
||||||
|
//TODO: Configurable poll timeout, ability to add string for Vendor error
|
||||||
|
buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]);
|
||||||
|
match self.state {
|
||||||
|
State::DlSync => self.state = State::Download,
|
||||||
|
State::ManifestSync => RST::sys_reset(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(InResponse::Accepted(&buf[0..6]))
|
||||||
|
}
|
||||||
|
Ok(Request::GetState) => {
|
||||||
|
buf[0] = self.state as u8;
|
||||||
|
Some(InResponse::Accepted(&buf[0..1]))
|
||||||
|
}
|
||||||
|
Ok(Request::Upload) if self.attrs.contains(DfuAttributes::CAN_UPLOAD) => {
|
||||||
|
//TODO: FirmwareUpdater does not provide a way of reading the active partition, can't upload.
|
||||||
|
Some(InResponse::Rejected)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation of the USB DFU 1.1 protocol
|
||||||
|
///
|
||||||
|
/// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device
|
||||||
|
/// The handler is responsive to DFU GetState, GetStatus, Abort, and ClrStatus commands, as well as Download if configured by the user.
|
||||||
|
///
|
||||||
|
/// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition.
|
||||||
|
/// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware.
|
||||||
|
pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize>(
|
||||||
|
builder: &mut Builder<'d, D>,
|
||||||
|
handler: &'d mut Control<'d, DFU, STATE, RST, BLOCK_SIZE>,
|
||||||
|
) {
|
||||||
|
let mut func = builder.function(0x00, 0x00, 0x00);
|
||||||
|
let mut iface = func.interface();
|
||||||
|
let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, None);
|
||||||
|
alt.descriptor(
|
||||||
|
DESC_DFU_FUNCTIONAL,
|
||||||
|
&[
|
||||||
|
handler.attrs.bits(),
|
||||||
|
0xc4,
|
||||||
|
0x09, // 2500ms timeout, doesn't affect operation as DETACH not necessary in bootloader code
|
||||||
|
(BLOCK_SIZE & 0xff) as u8,
|
||||||
|
((BLOCK_SIZE & 0xff00) >> 8) as u8,
|
||||||
|
0x10,
|
||||||
|
0x01, // DFU 1.1
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
drop(func);
|
||||||
|
builder.handler(handler);
|
||||||
|
}
|
95
embassy-usb-dfu/src/consts.rs
Normal file
95
embassy-usb-dfu/src/consts.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
pub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE;
|
||||||
|
pub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01;
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) const DFU_PROTOCOL_DFU: u8 = 0x02;
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) const DFU_PROTOCOL_RT: u8 = 0x01;
|
||||||
|
pub(crate) const DESC_DFU_FUNCTIONAL: u8 = 0x21;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
defmt::bitflags! {
|
||||||
|
pub struct DfuAttributes: u8 {
|
||||||
|
const WILL_DETACH = 0b0000_1000;
|
||||||
|
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
||||||
|
const CAN_UPLOAD = 0b0000_0010;
|
||||||
|
const CAN_DOWNLOAD = 0b0000_0001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
bitflags::bitflags! {
|
||||||
|
pub struct DfuAttributes: u8 {
|
||||||
|
const WILL_DETACH = 0b0000_1000;
|
||||||
|
const MANIFESTATION_TOLERANT = 0b0000_0100;
|
||||||
|
const CAN_UPLOAD = 0b0000_0010;
|
||||||
|
const CAN_DOWNLOAD = 0b0000_0001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum State {
|
||||||
|
AppIdle = 0,
|
||||||
|
AppDetach = 1,
|
||||||
|
DfuIdle = 2,
|
||||||
|
DlSync = 3,
|
||||||
|
DlBusy = 4,
|
||||||
|
Download = 5,
|
||||||
|
ManifestSync = 6,
|
||||||
|
Manifest = 7,
|
||||||
|
ManifestWaitReset = 8,
|
||||||
|
UploadIdle = 9,
|
||||||
|
Error = 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum Status {
|
||||||
|
Ok = 0x00,
|
||||||
|
ErrTarget = 0x01,
|
||||||
|
ErrFile = 0x02,
|
||||||
|
ErrWrite = 0x03,
|
||||||
|
ErrErase = 0x04,
|
||||||
|
ErrCheckErased = 0x05,
|
||||||
|
ErrProg = 0x06,
|
||||||
|
ErrVerify = 0x07,
|
||||||
|
ErrAddress = 0x08,
|
||||||
|
ErrNotDone = 0x09,
|
||||||
|
ErrFirmware = 0x0A,
|
||||||
|
ErrVendor = 0x0B,
|
||||||
|
ErrUsbr = 0x0C,
|
||||||
|
ErrPor = 0x0D,
|
||||||
|
ErrUnknown = 0x0E,
|
||||||
|
ErrStalledPkt = 0x0F,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum Request {
|
||||||
|
Detach = 0,
|
||||||
|
Dnload = 1,
|
||||||
|
Upload = 2,
|
||||||
|
GetStatus = 3,
|
||||||
|
ClrStatus = 4,
|
||||||
|
GetState = 5,
|
||||||
|
Abort = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for Request {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Request::Detach),
|
||||||
|
1 => Ok(Request::Dnload),
|
||||||
|
2 => Ok(Request::Upload),
|
||||||
|
3 => Ok(Request::GetStatus),
|
||||||
|
4 => Ok(Request::ClrStatus),
|
||||||
|
5 => Ok(Request::GetState),
|
||||||
|
6 => Ok(Request::Abort),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
258
embassy-usb-dfu/src/fmt.rs
Normal file
258
embassy-usb-dfu/src/fmt.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
#![macro_use]
|
||||||
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
|
macro_rules! assert {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::assert!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::assert!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_eq {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::assert_eq!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::assert_eq!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_ne {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::assert_ne!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::assert_ne!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_assert {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::debug_assert!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug_assert!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_assert_eq {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::debug_assert_eq!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug_assert_eq!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_assert_ne {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::debug_assert_ne!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug_assert_ne!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! todo {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::todo!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::todo!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::core::unreachable!($($x)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! panic {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::panic!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::panic!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! trace {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::trace!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::trace!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::debug!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! info {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::info!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::info!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! warn {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::warn!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::warn!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! error {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::error!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::error!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unwrap!($($x)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($arg:expr) => {
|
||||||
|
match $crate::fmt::Try::into_result($arg) {
|
||||||
|
::core::result::Result::Ok(t) => t,
|
||||||
|
::core::result::Result::Err(e) => {
|
||||||
|
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||||
|
match $crate::fmt::Try::into_result($arg) {
|
||||||
|
::core::result::Result::Ok(t) => t,
|
||||||
|
::core::result::Result::Err(e) => {
|
||||||
|
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct NoneError;
|
||||||
|
|
||||||
|
pub trait Try {
|
||||||
|
type Ok;
|
||||||
|
type Error;
|
||||||
|
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Try for Option<T> {
|
||||||
|
type Ok = T;
|
||||||
|
type Error = NoneError;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_result(self) -> Result<T, NoneError> {
|
||||||
|
self.ok_or(NoneError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> Try for Result<T, E> {
|
||||||
|
type Ok = T;
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_result(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
51
embassy-usb-dfu/src/lib.rs
Normal file
51
embassy-usb-dfu/src/lib.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#![no_std]
|
||||||
|
mod fmt;
|
||||||
|
|
||||||
|
pub mod consts;
|
||||||
|
|
||||||
|
#[cfg(feature = "dfu")]
|
||||||
|
mod bootloader;
|
||||||
|
#[cfg(feature = "dfu")]
|
||||||
|
pub use self::bootloader::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "application")]
|
||||||
|
mod application;
|
||||||
|
#[cfg(feature = "application")]
|
||||||
|
pub use self::application::*;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
all(feature = "dfu", feature = "application"),
|
||||||
|
not(any(feature = "dfu", feature = "application"))
|
||||||
|
))]
|
||||||
|
compile_error!("usb-dfu must be compiled with exactly one of `bootloader`, or `application` features");
|
||||||
|
|
||||||
|
/// Provides a platform-agnostic interface for initiating a system reset.
|
||||||
|
///
|
||||||
|
/// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a
|
||||||
|
/// reset request without interfacing with any other peripherals.
|
||||||
|
///
|
||||||
|
/// If alternate behaviour is desired, a custom implementation of Reset can be provided as a type argument to the usb_dfu function.
|
||||||
|
pub trait Reset {
|
||||||
|
fn sys_reset() -> !;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32c3-hal")]
|
||||||
|
pub struct ResetImmediate;
|
||||||
|
|
||||||
|
#[cfg(feature = "esp32c3-hal")]
|
||||||
|
impl Reset for ResetImmediate {
|
||||||
|
fn sys_reset() -> ! {
|
||||||
|
esp32c3_hal::reset::software_reset();
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cortex-m")]
|
||||||
|
pub struct ResetImmediate;
|
||||||
|
|
||||||
|
#[cfg(feature = "cortex-m")]
|
||||||
|
impl Reset for ResetImmediate {
|
||||||
|
fn sys_reset() -> ! {
|
||||||
|
cortex_m::peripheral::SCB::sys_reset()
|
||||||
|
}
|
||||||
|
}
|
9
examples/boot/application/stm32wb-dfu/.cargo/config.toml
Normal file
9
examples/boot/application/stm32wb-dfu/.cargo/config.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# replace your chip as listed in `probe-rs chip list`
|
||||||
|
runner = "probe-rs run --chip STM32WLE5JCIx"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabihf"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "trace"
|
32
examples/boot/application/stm32wb-dfu/Cargo.toml
Normal file
32
examples/boot/application/stm32wb-dfu/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "embassy-boot-stm32wb-dfu-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy-sync = { version = "0.5.0", path = "../../../../embassy-sync" }
|
||||||
|
embassy-executor = { version = "0.4.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
|
||||||
|
embassy-time = { version = "0.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||||
|
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] }
|
||||||
|
embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = [] }
|
||||||
|
embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" }
|
||||||
|
embassy-usb = { version = "0.1.0", path = "../../../../embassy-usb" }
|
||||||
|
embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] }
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
|
panic-reset = { version = "0.1.1" }
|
||||||
|
embedded-hal = { version = "0.2.6" }
|
||||||
|
|
||||||
|
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
defmt = [
|
||||||
|
"dep:defmt",
|
||||||
|
"dep:defmt-rtt",
|
||||||
|
"embassy-stm32/defmt",
|
||||||
|
"embassy-boot-stm32/defmt",
|
||||||
|
"embassy-sync/defmt",
|
||||||
|
]
|
29
examples/boot/application/stm32wb-dfu/README.md
Normal file
29
examples/boot/application/stm32wb-dfu/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# 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`
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
37
examples/boot/application/stm32wb-dfu/build.rs
Normal file
37
examples/boot/application/stm32wb-dfu/build.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
15
examples/boot/application/stm32wb-dfu/memory.x
Normal file
15
examples/boot/application/stm32wb-dfu/memory.x
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
|
FLASH : ORIGIN = 0x08008000, LENGTH = 128K
|
||||||
|
DFU : ORIGIN = 0x08028000, LENGTH = 132K
|
||||||
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||||
|
}
|
||||||
|
|
||||||
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
|
||||||
|
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
|
64
examples/boot/application/stm32wb-dfu/src/main.rs
Normal file
64
examples/boot/application/stm32wb-dfu/src/main.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareState, FirmwareUpdaterConfig};
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::flash::{Flash, WRITE_SIZE};
|
||||||
|
use embassy_stm32::rcc::WPAN_DEFAULT;
|
||||||
|
use embassy_stm32::usb::{self, Driver};
|
||||||
|
use embassy_stm32::{bind_interrupts, peripherals};
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
use embassy_time::Duration;
|
||||||
|
use embassy_usb::Builder;
|
||||||
|
use embassy_usb_dfu::consts::DfuAttributes;
|
||||||
|
use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate};
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
USB_LP => usb::InterruptHandler<peripherals::USB>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let mut config = embassy_stm32::Config::default();
|
||||||
|
config.rcc = WPAN_DEFAULT;
|
||||||
|
let p = embassy_stm32::init(config);
|
||||||
|
let flash = Flash::new_blocking(p.FLASH);
|
||||||
|
let flash = Mutex::new(RefCell::new(flash));
|
||||||
|
|
||||||
|
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&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");
|
||||||
|
|
||||||
|
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
|
||||||
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-DFU Runtime example");
|
||||||
|
config.serial_number = Some("1235678");
|
||||||
|
|
||||||
|
let mut device_descriptor = [0; 256];
|
||||||
|
let mut config_descriptor = [0; 256];
|
||||||
|
let mut bos_descriptor = [0; 256];
|
||||||
|
let mut control_buf = [0; 64];
|
||||||
|
let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD);
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut device_descriptor,
|
||||||
|
&mut config_descriptor,
|
||||||
|
&mut bos_descriptor,
|
||||||
|
&mut [],
|
||||||
|
&mut control_buf,
|
||||||
|
);
|
||||||
|
|
||||||
|
usb_dfu::<_, _, ResetImmediate>(&mut builder, &mut state, Duration::from_millis(2500));
|
||||||
|
|
||||||
|
let mut dev = builder.build();
|
||||||
|
dev.run().await
|
||||||
|
}
|
63
examples/boot/bootloader/stm32wb-dfu/Cargo.toml
Normal file
63
examples/boot/bootloader/stm32wb-dfu/Cargo.toml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "stm32wb-dfu-bootloader-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Example USB DFUbootloader for the STM32WB series of 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 = ["stm32wb55rg"] }
|
||||||
|
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"
|
||||||
|
embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] }
|
||||||
|
embassy-usb = { version = "0.1.0", path = "../../../../embassy-usb", default-features = false }
|
||||||
|
embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
defmt = [
|
||||||
|
"dep:defmt",
|
||||||
|
"embassy-boot-stm32/defmt",
|
||||||
|
"embassy-stm32/defmt",
|
||||||
|
"embassy-usb/defmt",
|
||||||
|
"embassy-usb-dfu/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
|
11
examples/boot/bootloader/stm32wb-dfu/README.md
Normal file
11
examples/boot/bootloader/stm32wb-dfu/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Bootloader for STM32
|
||||||
|
|
||||||
|
The bootloader uses `embassy-boot` to interact with the flash.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
Flash the bootloader
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx
|
||||||
|
```
|
27
examples/boot/bootloader/stm32wb-dfu/build.rs
Normal file
27
examples/boot/bootloader/stm32wb-dfu/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/stm32wb-dfu/memory.x
Normal file
18
examples/boot/bootloader/stm32wb-dfu/memory.x
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
|
ACTIVE : ORIGIN = 0x08008000, LENGTH = 128K
|
||||||
|
DFU : ORIGIN = 0x08028000, LENGTH = 132K
|
||||||
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
||||||
|
}
|
||||||
|
|
||||||
|
__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(FLASH);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
|
93
examples/boot/bootloader/stm32wb-dfu/src/main.rs
Normal file
93
examples/boot/bootloader/stm32wb-dfu/src/main.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#![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, WRITE_SIZE};
|
||||||
|
use embassy_stm32::rcc::WPAN_DEFAULT;
|
||||||
|
use embassy_stm32::usb::Driver;
|
||||||
|
use embassy_stm32::{bind_interrupts, peripherals, usb};
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
use embassy_usb::Builder;
|
||||||
|
use embassy_usb_dfu::consts::DfuAttributes;
|
||||||
|
use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
USB_LP => usb::InterruptHandler<peripherals::USB>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut config = embassy_stm32::Config::default();
|
||||||
|
config.rcc = WPAN_DEFAULT;
|
||||||
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
|
// Prevent a hard fault when accessing flash 'too early' after boot.
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
for _ in 0..10000000 {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 active_offset = config.active.offset();
|
||||||
|
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
|
||||||
|
if bl.state == State::DfuDetach {
|
||||||
|
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
|
||||||
|
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-DFU Bootloader example");
|
||||||
|
config.serial_number = Some("1235678");
|
||||||
|
|
||||||
|
let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||||
|
let mut buffer = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
|
let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);
|
||||||
|
|
||||||
|
let mut device_descriptor = [0; 256];
|
||||||
|
let mut config_descriptor = [0; 256];
|
||||||
|
let mut bos_descriptor = [0; 256];
|
||||||
|
let mut control_buf = [0; 4096];
|
||||||
|
let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD);
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut device_descriptor,
|
||||||
|
&mut config_descriptor,
|
||||||
|
&mut bos_descriptor,
|
||||||
|
&mut [],
|
||||||
|
&mut control_buf,
|
||||||
|
);
|
||||||
|
|
||||||
|
usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state);
|
||||||
|
|
||||||
|
let mut dev = builder.build();
|
||||||
|
embassy_futures::block_on(dev.run());
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
@ -36,9 +36,9 @@ rand = { version = "0.8.4", default-features = false }
|
|||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.1"
|
||||||
usbd-hid = "0.6.0"
|
usbd-hid = "0.6.0"
|
||||||
serde = { version = "1.0.136", default-features = false }
|
serde = { version = "1.0.136", default-features = false }
|
||||||
embedded-hal = { version = "1.0.0-rc.2" }
|
embedded-hal = { version = "1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "1.0.0-rc.2" }
|
embedded-hal-async = { version = "1.0.0-rc.3" }
|
||||||
embedded-hal-bus = { version = "0.1.0-rc.2", features = ["async"] }
|
embedded-hal-bus = { version = "0.1.0-rc.3", features = ["async"] }
|
||||||
num-integer = { version = "0.1.45", default-features = false }
|
num-integer = { version = "0.1.45", default-features = false }
|
||||||
microfft = "0.5.0"
|
microfft = "0.5.0"
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ smart-leds = "0.3.0"
|
|||||||
heapless = "0.8"
|
heapless = "0.8"
|
||||||
usbd-hid = "0.6.1"
|
usbd-hid = "0.6.1"
|
||||||
|
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = "1.0.0-rc.2"
|
embedded-hal-async = "1.0.0-rc.3"
|
||||||
embedded-hal-bus = { version = "0.1.0-rc.2", features = ["async"] }
|
embedded-hal-bus = { version = "0.1.0-rc.3", features = ["async"] }
|
||||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||||
embedded-storage = { version = "0.3" }
|
embedded-storage = { version = "0.3" }
|
||||||
static_cell = { version = "2", features = ["nightly"]}
|
static_cell = { version = "2", features = ["nightly"]}
|
||||||
|
@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
// Use PIN_28, Pin34 on J0 for RP Pico, as a input.
|
// Use PIN_28, Pin34 on J0 for RP Pico, as a input.
|
||||||
// You need to add your own button.
|
// You need to add your own button.
|
||||||
let button = Input::new(p.PIN_28, Pull::Up);
|
let mut button = Input::new(p.PIN_28, Pull::Up);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_high() {
|
if button.is_high() {
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PC13, Pull::Up);
|
let mut button = Input::new(p.PC13, Pull::Up);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_high() {
|
if button.is_high() {
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PA0, Pull::Down);
|
let mut button = Input::new(p.PA0, Pull::Down);
|
||||||
let mut led1 = Output::new(p.PE9, Level::High, Speed::Low);
|
let mut led1 = Output::new(p.PE9, Level::High, Speed::Low);
|
||||||
let mut led2 = Output::new(p.PE15, Level::High, Speed::Low);
|
let mut led2 = Output::new(p.PE15, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PC13, Pull::Down);
|
let mut button = Input::new(p.PC13, Pull::Down);
|
||||||
let mut led1 = Output::new(p.PB0, Level::High, Speed::Low);
|
let mut led1 = Output::new(p.PB0, Level::High, Speed::Low);
|
||||||
let _led2 = Output::new(p.PB7, Level::High, Speed::Low);
|
let _led2 = Output::new(p.PB7, Level::High, Speed::Low);
|
||||||
let mut led3 = Output::new(p.PB14, Level::High, Speed::Low);
|
let mut led3 = Output::new(p.PB14, Level::High, Speed::Low);
|
||||||
|
@ -31,7 +31,7 @@ fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {
|
|||||||
|
|
||||||
info!("Reading...");
|
info!("Reading...");
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0u8; 32];
|
||||||
unwrap!(f.read(offset, &mut buf));
|
unwrap!(f.blocking_read(offset, &mut buf));
|
||||||
info!("Read: {=[u8]:x}", buf);
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
|
||||||
info!("Erasing...");
|
info!("Erasing...");
|
||||||
@ -39,7 +39,7 @@ fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {
|
|||||||
|
|
||||||
info!("Reading...");
|
info!("Reading...");
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0u8; 32];
|
||||||
unwrap!(f.read(offset, &mut buf));
|
unwrap!(f.blocking_read(offset, &mut buf));
|
||||||
info!("Read after erase: {=[u8]:x}", buf);
|
info!("Read after erase: {=[u8]:x}", buf);
|
||||||
|
|
||||||
info!("Writing...");
|
info!("Writing...");
|
||||||
@ -53,7 +53,7 @@ fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {
|
|||||||
|
|
||||||
info!("Reading...");
|
info!("Reading...");
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0u8; 32];
|
||||||
unwrap!(f.read(offset, &mut buf));
|
unwrap!(f.blocking_read(offset, &mut buf));
|
||||||
info!("Read: {=[u8]:x}", buf);
|
info!("Read: {=[u8]:x}", buf);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&buf[..],
|
&buf[..],
|
||||||
|
@ -48,7 +48,7 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
|
|||||||
|
|
||||||
info!("Reading...");
|
info!("Reading...");
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0u8; 32];
|
||||||
unwrap!(f.read(offset, &mut buf));
|
unwrap!(f.blocking_read(offset, &mut buf));
|
||||||
info!("Read: {=[u8]:x}", buf);
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
|
||||||
info!("Erasing...");
|
info!("Erasing...");
|
||||||
@ -56,7 +56,7 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
|
|||||||
|
|
||||||
info!("Reading...");
|
info!("Reading...");
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0u8; 32];
|
||||||
unwrap!(f.read(offset, &mut buf));
|
unwrap!(f.blocking_read(offset, &mut buf));
|
||||||
info!("Read after erase: {=[u8]:x}", buf);
|
info!("Read after erase: {=[u8]:x}", buf);
|
||||||
|
|
||||||
info!("Writing...");
|
info!("Writing...");
|
||||||
@ -73,7 +73,7 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
|
|||||||
|
|
||||||
info!("Reading...");
|
info!("Reading...");
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0u8; 32];
|
||||||
unwrap!(f.read(offset, &mut buf));
|
unwrap!(f.blocking_read(offset, &mut buf));
|
||||||
info!("Read: {=[u8]:x}", buf);
|
info!("Read: {=[u8]:x}", buf);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&buf[..],
|
&buf[..],
|
||||||
|
131
examples/stm32f4/src/bin/ws2812_pwm_dma.rs
Normal file
131
examples/stm32f4/src/bin/ws2812_pwm_dma.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Configure TIM3 in PWM mode, and start DMA Transfer(s) to send color data into ws2812.
|
||||||
|
// We assume the DIN pin of ws2812 connect to GPIO PB4, and ws2812 is properly powered.
|
||||||
|
//
|
||||||
|
// This demo is a combination of HAL, PAC, and manually invoke `dma::Transfer`
|
||||||
|
//
|
||||||
|
// Warning:
|
||||||
|
// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::gpio::OutputType;
|
||||||
|
use embassy_stm32::pac;
|
||||||
|
use embassy_stm32::time::khz;
|
||||||
|
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||||
|
use embassy_stm32::timer::{Channel, CountingMode};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let mut device_config = embassy_stm32::Config::default();
|
||||||
|
|
||||||
|
// set SYSCLK/HCLK/PCLK2 to 20 MHz, thus each tick is 0.05 us,
|
||||||
|
// and ws2812 timings are integer multiples of 0.05 us
|
||||||
|
{
|
||||||
|
use embassy_stm32::rcc::*;
|
||||||
|
use embassy_stm32::time::*;
|
||||||
|
device_config.enable_debug_during_sleep = true;
|
||||||
|
device_config.rcc.hse = Some(Hse {
|
||||||
|
freq: mhz(12),
|
||||||
|
mode: HseMode::Oscillator,
|
||||||
|
});
|
||||||
|
device_config.rcc.sys = Sysclk::PLL1_P;
|
||||||
|
device_config.rcc.pll_src = PllSource::HSE;
|
||||||
|
device_config.rcc.pll = Some(Pll {
|
||||||
|
prediv: PllPreDiv::DIV6,
|
||||||
|
mul: PllMul::MUL80,
|
||||||
|
divp: Some(PllPDiv::DIV8),
|
||||||
|
divq: None,
|
||||||
|
divr: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dp = embassy_stm32::init(device_config);
|
||||||
|
|
||||||
|
let mut ws2812_pwm = SimplePwm::new(
|
||||||
|
dp.TIM3,
|
||||||
|
Some(PwmPin::new_ch1(dp.PB4, OutputType::PushPull)),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
khz(800), // data rate of ws2812
|
||||||
|
CountingMode::EdgeAlignedUp,
|
||||||
|
);
|
||||||
|
|
||||||
|
// PAC level hacking,
|
||||||
|
// enable auto-reload preload, and enable timer-update-event trigger DMA
|
||||||
|
{
|
||||||
|
pac::TIM3.cr1().modify(|v| v.set_arpe(true));
|
||||||
|
pac::TIM3.dier().modify(|v| v.set_ude(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct ws2812 non-return-to-zero (NRZ) code bit by bit
|
||||||
|
|
||||||
|
let max_duty = ws2812_pwm.get_max_duty();
|
||||||
|
let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
|
||||||
|
let n1 = 2 * n0; // ws2812 Bit 1 high level timing
|
||||||
|
|
||||||
|
let turn_off = [
|
||||||
|
n0, n0, n0, n0, n0, n0, n0, n0, // Green
|
||||||
|
n0, n0, n0, n0, n0, n0, n0, n0, // Red
|
||||||
|
n0, n0, n0, n0, n0, n0, n0, n0, // Blue
|
||||||
|
0, // keep PWM output low after a transfer
|
||||||
|
];
|
||||||
|
|
||||||
|
let dim_white = [
|
||||||
|
n0, n0, n0, n0, n0, n0, n1, n0, // Green
|
||||||
|
n0, n0, n0, n0, n0, n0, n1, n0, // Red
|
||||||
|
n0, n0, n0, n0, n0, n0, n1, n0, // Blue
|
||||||
|
0, // keep PWM output low after a transfer
|
||||||
|
];
|
||||||
|
|
||||||
|
let color_list = [&turn_off, &dim_white];
|
||||||
|
|
||||||
|
let pwm_channel = Channel::Ch1;
|
||||||
|
|
||||||
|
// make sure PWM output keep low on first start
|
||||||
|
ws2812_pwm.set_duty(pwm_channel, 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
use embassy_stm32::dma::{Burst, FifoThreshold, Transfer, TransferOptions};
|
||||||
|
|
||||||
|
// configure FIFO and MBURST of DMA, to minimize DMA occupation on AHB/APB
|
||||||
|
let mut dma_transfer_option = TransferOptions::default();
|
||||||
|
dma_transfer_option.fifo_threshold = Some(FifoThreshold::Full);
|
||||||
|
dma_transfer_option.mburst = Burst::Incr8;
|
||||||
|
|
||||||
|
let mut color_list_index = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// start PWM output
|
||||||
|
ws2812_pwm.enable(pwm_channel);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Transfer::new_write(
|
||||||
|
// with &mut, we can easily reuse same DMA channel multiple times
|
||||||
|
&mut dp.DMA1_CH2,
|
||||||
|
5,
|
||||||
|
color_list[color_list_index],
|
||||||
|
pac::TIM3.ccr(pwm_channel.raw()).as_ptr() as *mut _,
|
||||||
|
dma_transfer_option,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
// ws2812 need at least 50 us low level input to confirm the input data and change it's state
|
||||||
|
Timer::after_micros(50).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop PWM output for saving some energy
|
||||||
|
ws2812_pwm.disable(pwm_channel);
|
||||||
|
|
||||||
|
// wait another half second, so that we can see color change
|
||||||
|
Timer::after_millis(500).await;
|
||||||
|
|
||||||
|
// flip the index bit so that next round DMA transfer the other color data
|
||||||
|
color_list_index ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PC13, Pull::Down);
|
let mut button = Input::new(p.PC13, Pull::Down);
|
||||||
let mut led1 = Output::new(p.PB0, Level::High, Speed::Low);
|
let mut led1 = Output::new(p.PB0, Level::High, Speed::Low);
|
||||||
let _led2 = Output::new(p.PB7, Level::High, Speed::Low);
|
let _led2 = Output::new(p.PB7, Level::High, Speed::Low);
|
||||||
let mut led3 = Output::new(p.PB14, Level::High, Speed::Low);
|
let mut led3 = Output::new(p.PB14, Level::High, Speed::Low);
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PC13, Pull::Up);
|
let mut button = Input::new(p.PC13, Pull::Up);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_high() {
|
if button.is_high() {
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PC13, Pull::Down);
|
let mut button = Input::new(p.PC13, Pull::Down);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_high() {
|
if button.is_high() {
|
||||||
|
@ -19,8 +19,8 @@ defmt-rtt = "0.4"
|
|||||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
embedded-hal = "0.2.6"
|
embedded-hal = "0.2.6"
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-io-async = { version = "0.6.1" }
|
embedded-io-async = { version = "0.6.1" }
|
||||||
embedded-nal-async = { version = "0.7.1" }
|
embedded-nal-async = { version = "0.7.1" }
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
|
@ -19,8 +19,8 @@ defmt-rtt = "0.4"
|
|||||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
embedded-hal = "0.2.6"
|
embedded-hal = "0.2.6"
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-nal-async = { version = "0.7.1" }
|
embedded-nal-async = { version = "0.7.1" }
|
||||||
embedded-io-async = { version = "0.6.1" }
|
embedded-io-async = { version = "0.6.1" }
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
|
@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let button = Input::new(p.PB2, Pull::Up);
|
let mut button = Input::new(p.PB2, Pull::Up);
|
||||||
let mut led1 = Output::new(p.PA5, Level::High, Speed::Low);
|
let mut led1 = Output::new(p.PA5, Level::High, Speed::Low);
|
||||||
let mut led2 = Output::new(p.PB5, Level::High, Speed::Low);
|
let mut led2 = Output::new(p.PB5, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ defmt-rtt = "0.4"
|
|||||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
embedded-hal = "0.2.6"
|
embedded-hal = "0.2.6"
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
embedded-hal-async = { version = "=1.0.0-rc.3" }
|
||||||
embedded-hal-bus = { version = "=0.1.0-rc.2", features = ["async"] }
|
embedded-hal-bus = { version = "=0.1.0-rc.3", features = ["async"] }
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||||
heapless = { version = "0.8", default-features = false }
|
heapless = { version = "0.8", default-features = false }
|
||||||
|
@ -12,7 +12,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PC13, Pull::Up);
|
let mut button = Input::new(p.PC13, Pull::Up);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if button.is_high() {
|
if button.is_high() {
|
||||||
|
@ -114,8 +114,8 @@ async fn main(spawner: Spawner) {
|
|||||||
let led_uc4_blue = Output::new(dp.PG15, Level::High, Speed::Low);
|
let led_uc4_blue = Output::new(dp.PG15, Level::High, Speed::Low);
|
||||||
|
|
||||||
// Read the uc_cfg switches
|
// Read the uc_cfg switches
|
||||||
let uc_cfg0 = Input::new(dp.PB2, Pull::None);
|
let mut uc_cfg0 = Input::new(dp.PB2, Pull::None);
|
||||||
let uc_cfg1 = Input::new(dp.PF11, Pull::None);
|
let mut uc_cfg1 = Input::new(dp.PF11, Pull::None);
|
||||||
let _uc_cfg2 = Input::new(dp.PG6, Pull::None);
|
let _uc_cfg2 = Input::new(dp.PG6, Pull::None);
|
||||||
let _uc_cfg3 = Input::new(dp.PG11, Pull::None);
|
let _uc_cfg3 = Input::new(dp.PG11, Pull::None);
|
||||||
|
|
||||||
@ -133,8 +133,8 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// Setup IO and SPI for the SPE chip
|
// Setup IO and SPI for the SPE chip
|
||||||
let spe_reset_n = Output::new(dp.PC7, Level::Low, Speed::Low);
|
let spe_reset_n = Output::new(dp.PC7, Level::Low, Speed::Low);
|
||||||
let spe_cfg0 = Input::new(dp.PC8, Pull::None);
|
let mut spe_cfg0 = Input::new(dp.PC8, Pull::None);
|
||||||
let spe_cfg1 = Input::new(dp.PC9, Pull::None);
|
let mut spe_cfg1 = Input::new(dp.PC9, Pull::None);
|
||||||
let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low);
|
let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low);
|
||||||
|
|
||||||
let spe_int = Input::new(dp.PB11, Pull::None);
|
let spe_int = Input::new(dp.PB11, Pull::None);
|
||||||
|
@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);
|
let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);
|
||||||
let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);
|
let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);
|
||||||
let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
|
let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
|
||||||
let ready = Input::new(p.PE1, Pull::Up);
|
let mut ready = Input::new(p.PE1, Pull::Up);
|
||||||
|
|
||||||
cortex_m::asm::delay(100_000);
|
cortex_m::asm::delay(100_000);
|
||||||
reset.set_high();
|
reset.set_high();
|
||||||
|
@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);
|
let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);
|
||||||
let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);
|
let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);
|
||||||
let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
|
let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
|
||||||
let ready = Input::new(p.PE1, Pull::Up);
|
let mut ready = Input::new(p.PE1, Pull::Up);
|
||||||
|
|
||||||
cortex_m::asm::delay(100_000);
|
cortex_m::asm::delay(100_000);
|
||||||
reset.set_high();
|
reset.set_high();
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let p = embassy_stm32::init(Default::default());
|
let p = embassy_stm32::init(Default::default());
|
||||||
|
|
||||||
let button = Input::new(p.PA0, Pull::Up);
|
let mut button = Input::new(p.PA0, Pull::Up);
|
||||||
let mut led1 = Output::new(p.PB15, Level::High, Speed::Low);
|
let mut led1 = Output::new(p.PB15, Level::High, Speed::Low);
|
||||||
let mut led2 = Output::new(p.PB9, Level::High, Speed::Low);
|
let mut led2 = Output::new(p.PB9, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user