mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-10-01 06:10:41 +00:00
Merge branch 'embassy-rs:main' into stm32h755-intercore
This commit is contained in:
commit
a4676f8b94
@ -12,8 +12,8 @@ Rust's [async/await](https://rust-lang.github.io/async-book/) allows for unprece
|
||||
|
||||
## Batteries included
|
||||
|
||||
- **Hardware Abstraction Layers
|
||||
** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||
- **Hardware Abstraction Layers**
|
||||
- HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||
- [embassy-stm32](https://docs.embassy.dev/embassy-stm32/), for all STM32 microcontroller families.
|
||||
- [embassy-nrf](https://docs.embassy.dev/embassy-nrf/), for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series.
|
||||
- [embassy-rp](https://docs.embassy.dev/embassy-rp/), for the Raspberry Pi RP2040 and RP23xx microcontrollers.
|
||||
|
19
ci.sh
19
ci.sh
@ -19,7 +19,7 @@ fi
|
||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||
|
||||
BUILD_EXTRA=""
|
||||
if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then
|
||||
if [ $TARGET = "x86_64-unknown-linux-gnu" ] || [ $TARGET = "aarch64-unknown-linux-gnu" ]; then
|
||||
BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --artifact-dir out/examples/std"
|
||||
fi
|
||||
|
||||
@ -174,11 +174,18 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c110x,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g350x,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g351x,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l130x,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l222x,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1306rhb,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l2228pn,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1345dgs28,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1106dgs28,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0l1228pm,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1107ycj,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3105rhb,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1505pt,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g1519rhb,defmt,time-driver-any \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
||||
|
@ -13,7 +13,7 @@ documentation = "https://docs.embassy.dev/cyw43-pio"
|
||||
cyw43 = { version = "0.3.0", path = "../cyw43" }
|
||||
embassy-rp = { version = "0.4.0", path = "../embassy-rp" }
|
||||
fixed = "1.23.1"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-pio-v$VERSION/cyw43-pio/src/"
|
||||
|
@ -23,7 +23,7 @@ embassy-sync = { version = "0.6.2", path = "../embassy-sync"}
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"}
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.17", optional = true }
|
||||
|
||||
cortex-m = "0.7.6"
|
||||
|
@ -10,9 +10,9 @@ embassy-executor = { version = "0.7.0", path = "../../../embassy-executor", feat
|
||||
embassy-time = { version = "0.4.0", path = "../../../embassy-time", features = ["defmt"] }
|
||||
embassy-nrf = { version = "0.3.1", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.3"
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
|
||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||
|
@ -10,6 +10,6 @@ cortex-m-rt = "0.7"
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "memory-x", "exti"] }
|
||||
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||
|
@ -9,6 +9,6 @@ cortex-m-rt = "0.7"
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "memory-x"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||
|
@ -11,6 +11,6 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "memory-x", "unstable-pac"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||
|
@ -9,6 +9,6 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
stm32-metapac = { version = "16", features = ["stm32l475vg"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||
|
@ -9,5 +9,8 @@ The link: link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Emb
|
||||
|
||||
The following peripherals have a HAL implementation at present
|
||||
|
||||
* CRC
|
||||
* DMA
|
||||
* GPIO
|
||||
|
||||
* RNG
|
||||
* UART
|
||||
|
@ -21,7 +21,7 @@ target = "thumbv7em-none-eabi"
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.17", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
|
@ -21,7 +21,7 @@ features = ["embassy-rp/rp2040"]
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
|
@ -21,7 +21,7 @@ target = "thumbv7em-none-eabi"
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
|
@ -24,7 +24,7 @@ features = ["defmt"]
|
||||
[lib]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
digest = "0.10"
|
||||
log = { version = "0.4", optional = true }
|
||||
ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true }
|
||||
|
@ -35,7 +35,7 @@ embedded-storage = "0.3.1"
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
nb = "1.0.0"
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
critical-section = { version = "1.1.1", features = ["std"] }
|
||||
|
@ -29,7 +29,7 @@ targets = ["thumbv7em-none-eabi"]
|
||||
features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
rtos-trace = { version = "0.1.3", optional = true }
|
||||
|
||||
|
@ -18,7 +18,7 @@ mod state;
|
||||
|
||||
pub mod timer_queue;
|
||||
#[cfg(feature = "trace")]
|
||||
mod trace;
|
||||
pub mod trace;
|
||||
pub(crate) mod util;
|
||||
#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
|
||||
mod waker;
|
||||
@ -89,6 +89,12 @@ pub(crate) struct TaskHeader {
|
||||
|
||||
/// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
|
||||
pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) name: Option<&'static str>,
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) id: u32,
|
||||
#[cfg(feature = "trace")]
|
||||
all_tasks_next: AtomicPtr<TaskHeader>,
|
||||
}
|
||||
|
||||
/// This is essentially a `&'static TaskStorage<F>` where the type of the future has been erased.
|
||||
@ -143,12 +149,6 @@ impl TaskRef {
|
||||
pub(crate) fn as_ptr(self) -> *const TaskHeader {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
|
||||
/// Get the ID for a task
|
||||
#[cfg(feature = "trace")]
|
||||
pub fn as_id(self) -> u32 {
|
||||
self.ptr.as_ptr() as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw storage in which a task can be spawned.
|
||||
@ -190,6 +190,12 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
poll_fn: SyncUnsafeCell::new(None),
|
||||
|
||||
timer_queue_item: timer_queue::TimerQueueItem::new(),
|
||||
#[cfg(feature = "trace")]
|
||||
name: None,
|
||||
#[cfg(feature = "trace")]
|
||||
id: 0,
|
||||
#[cfg(feature = "trace")]
|
||||
all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
|
||||
},
|
||||
future: UninitCell::uninit(),
|
||||
}
|
||||
|
@ -81,7 +81,131 @@
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::raw::{SyncExecutor, TaskRef};
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
|
||||
|
||||
use rtos_trace::TaskInfo;
|
||||
|
||||
use crate::raw::{SyncExecutor, TaskHeader, TaskRef};
|
||||
use crate::spawner::{SpawnError, SpawnToken, Spawner};
|
||||
|
||||
/// Global task tracker instance
|
||||
///
|
||||
/// This static provides access to the global task tracker which maintains
|
||||
/// a list of all tasks in the system. It's automatically updated by the
|
||||
/// task lifecycle hooks in the trace module.
|
||||
pub static TASK_TRACKER: TaskTracker = TaskTracker::new();
|
||||
|
||||
/// A thread-safe tracker for all tasks in the system
|
||||
///
|
||||
/// This struct uses an intrusive linked list approach to track all tasks
|
||||
/// without additional memory allocations. It maintains a global list of
|
||||
/// tasks that can be traversed to find all currently existing tasks.
|
||||
pub struct TaskTracker {
|
||||
head: AtomicPtr<TaskHeader>,
|
||||
}
|
||||
|
||||
impl TaskTracker {
|
||||
/// Creates a new empty task tracker
|
||||
///
|
||||
/// Initializes a tracker with no tasks in its list.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
head: AtomicPtr::new(core::ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a task to the tracker
|
||||
///
|
||||
/// This method inserts a task at the head of the intrusive linked list.
|
||||
/// The operation is thread-safe and lock-free, using atomic operations
|
||||
/// to ensure consistency even when called from different contexts.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `task` - The task reference to add to the tracker
|
||||
pub fn add(&self, task: TaskRef) {
|
||||
let task_ptr = task.as_ptr() as *mut TaskHeader;
|
||||
|
||||
loop {
|
||||
let current_head = self.head.load(Ordering::Acquire);
|
||||
unsafe {
|
||||
(*task_ptr).all_tasks_next.store(current_head, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
if self
|
||||
.head
|
||||
.compare_exchange(current_head, task_ptr, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an operation on each task in the tracker
|
||||
///
|
||||
/// This method traverses the entire list of tasks and calls the provided
|
||||
/// function for each task. This allows inspecting or processing all tasks
|
||||
/// in the system without modifying the tracker's structure.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `f` - A function to call for each task in the tracker
|
||||
pub fn for_each<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(TaskRef),
|
||||
{
|
||||
let mut current = self.head.load(Ordering::Acquire);
|
||||
while !current.is_null() {
|
||||
let task = unsafe { TaskRef::from_ptr(current) };
|
||||
f(task);
|
||||
|
||||
current = unsafe { (*current).all_tasks_next.load(Ordering::Acquire) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for `TaskRef` that provides tracing functionality.
|
||||
///
|
||||
/// This trait is only available when the `trace` feature is enabled.
|
||||
/// It extends `TaskRef` with methods for accessing and modifying task identifiers
|
||||
/// and names, which are useful for debugging, logging, and performance analysis.
|
||||
pub trait TaskRefTrace {
|
||||
/// Get the name for a task
|
||||
fn name(&self) -> Option<&'static str>;
|
||||
|
||||
/// Set the name for a task
|
||||
fn set_name(&self, name: Option<&'static str>);
|
||||
|
||||
/// Get the ID for a task
|
||||
fn id(&self) -> u32;
|
||||
|
||||
/// Set the ID for a task
|
||||
fn set_id(&self, id: u32);
|
||||
}
|
||||
|
||||
impl TaskRefTrace for TaskRef {
|
||||
fn name(&self) -> Option<&'static str> {
|
||||
self.header().name
|
||||
}
|
||||
|
||||
fn set_name(&self, name: Option<&'static str>) {
|
||||
unsafe {
|
||||
let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
|
||||
(*header_ptr).name = name;
|
||||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> u32 {
|
||||
self.header().id
|
||||
}
|
||||
|
||||
fn set_id(&self, id: u32) {
|
||||
unsafe {
|
||||
let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
|
||||
(*header_ptr).id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rtos-trace"))]
|
||||
extern "Rust" {
|
||||
@ -160,6 +284,9 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
rtos_trace::trace::task_new(task.as_ptr() as u32);
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
TASK_TRACKER.add(*task);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -210,10 +337,62 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) {
|
||||
rtos_trace::trace::system_idle();
|
||||
}
|
||||
|
||||
/// Returns an iterator over all active tasks in the system
|
||||
///
|
||||
/// This function provides a convenient way to iterate over all tasks
|
||||
/// that are currently tracked in the system. The returned iterator
|
||||
/// yields each task in the global task tracker.
|
||||
///
|
||||
/// # Returns
|
||||
/// An iterator that yields `TaskRef` items for each task
|
||||
fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
|
||||
struct TaskIterator<'a> {
|
||||
tracker: &'a TaskTracker,
|
||||
current: *mut TaskHeader,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TaskIterator<'a> {
|
||||
type Item = TaskRef;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let task = unsafe { TaskRef::from_ptr(self.current) };
|
||||
self.current = unsafe { (*self.current).all_tasks_next.load(Ordering::Acquire) };
|
||||
|
||||
Some(task)
|
||||
}
|
||||
}
|
||||
|
||||
TaskIterator {
|
||||
tracker: &TASK_TRACKER,
|
||||
current: TASK_TRACKER.head.load(Ordering::Acquire),
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform an action on each active task
|
||||
fn with_all_active_tasks<F>(f: F)
|
||||
where
|
||||
F: FnMut(TaskRef),
|
||||
{
|
||||
TASK_TRACKER.for_each(f);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {
|
||||
fn task_list() {
|
||||
// We don't know what tasks exist, so we can't send them.
|
||||
with_all_active_tasks(|task| {
|
||||
let name = task.name().unwrap_or("unnamed task\0");
|
||||
let info = rtos_trace::TaskInfo {
|
||||
name,
|
||||
priority: 0,
|
||||
stack_base: 0,
|
||||
stack_size: 0,
|
||||
};
|
||||
rtos_trace::trace::task_send_info(task.id(), info);
|
||||
});
|
||||
}
|
||||
fn time() -> u64 {
|
||||
const fn gcd(a: u64, b: u64) -> u64 {
|
||||
|
@ -5,6 +5,8 @@ use core::sync::atomic::Ordering;
|
||||
use core::task::Poll;
|
||||
|
||||
use super::raw;
|
||||
#[cfg(feature = "trace")]
|
||||
use crate::raw::trace::TaskRefTrace;
|
||||
|
||||
/// Token to spawn a newly-created task in an executor.
|
||||
///
|
||||
@ -22,7 +24,7 @@ use super::raw;
|
||||
/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
|
||||
#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
|
||||
pub struct SpawnToken<S> {
|
||||
raw_task: Option<raw::TaskRef>,
|
||||
pub(crate) raw_task: Option<raw::TaskRef>,
|
||||
phantom: PhantomData<*mut S>,
|
||||
}
|
||||
|
||||
@ -103,7 +105,7 @@ impl core::error::Error for SpawnError {}
|
||||
/// If you want to spawn tasks from another thread, use [SendSpawner].
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Spawner {
|
||||
executor: &'static raw::Executor,
|
||||
pub(crate) executor: &'static raw::Executor,
|
||||
not_send: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
@ -180,6 +182,53 @@ impl Spawner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait adding tracing capabilities to the Spawner
|
||||
///
|
||||
/// This trait provides an additional method to spawn tasks with an associated name,
|
||||
/// which can be useful for debugging and tracing purposes.
|
||||
pub trait SpawnerTraceExt {
|
||||
/// Spawns a new task with a specified name.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `name` - Static string name to associate with the task
|
||||
/// * `token` - Token representing the task to spawn
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating whether the spawn was successful
|
||||
fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
|
||||
}
|
||||
|
||||
/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled
|
||||
#[cfg(feature = "trace")]
|
||||
impl SpawnerTraceExt for Spawner {
|
||||
fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
|
||||
let task = token.raw_task;
|
||||
core::mem::forget(token);
|
||||
|
||||
match task {
|
||||
Some(task) => {
|
||||
// Set the name and ID when trace is enabled
|
||||
task.set_name(Some(name));
|
||||
let task_id = task.as_ptr() as u32;
|
||||
task.set_id(task_id);
|
||||
|
||||
unsafe { self.executor.spawn(task) };
|
||||
Ok(())
|
||||
}
|
||||
None => Err(SpawnError::Busy),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the SpawnerTraceExt trait for Spawner when trace is disabled
|
||||
#[cfg(not(feature = "trace"))]
|
||||
impl SpawnerTraceExt for Spawner {
|
||||
fn spawn_named<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
|
||||
// When trace is disabled, just forward to regular spawn and ignore the name
|
||||
self.spawn(token)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to spawn tasks into an executor from any thread.
|
||||
///
|
||||
/// This Spawner can be used from any thread (it is Send), but it can
|
||||
|
@ -24,5 +24,5 @@ target = "thumbv7em-none-eabi"
|
||||
features = ["defmt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
@ -28,7 +28,7 @@ prio-bits-8 = []
|
||||
cortex-m = ["dep:cortex-m", "dep:critical-section"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
num-traits = { version = "0.2.14", default-features = false }
|
||||
|
@ -8,6 +8,8 @@ macro_rules! peripherals_definition {
|
||||
$(#[$cfg])?
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc = concat!(stringify!($name), " peripheral")]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct $name { _private: () }
|
||||
|
||||
$(#[$cfg])?
|
||||
|
@ -14,6 +14,8 @@ use core::ops::Deref;
|
||||
/// the driver code would be monomorphized two times. With Peri, the driver is generic
|
||||
/// over a lifetime only. `SPI4` becomes `Peri<'static, SPI4>`, and `&mut SPI4` becomes
|
||||
/// `Peri<'a, SPI4>`. Lifetimes don't cause monomorphization.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Peri<'a, T: PeripheralType> {
|
||||
inner: T,
|
||||
_lifetime: PhantomData<&'a mut T>,
|
||||
|
@ -71,7 +71,7 @@ embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", fe
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false }
|
||||
embassy-futures = { version = "0.1.1", path = "../embassy-futures" }
|
||||
|
||||
defmt = { version = "1.0", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
nb = "1.0.0"
|
||||
cfg-if = "1.0.0"
|
||||
@ -80,9 +80,11 @@ cortex-m = "0.7.6"
|
||||
critical-section = "1.1"
|
||||
embedded-io = { version = "0.6.1" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
rand_core = "0.6.4"
|
||||
fixed = "1.23.1"
|
||||
|
||||
rand-core-06 = { package = "rand_core", version = "0.6" }
|
||||
rand-core-09 = { package = "rand_core", version = "0.9" }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||
"unproven",
|
||||
] }
|
||||
|
190
embassy-imxrt/src/crc.rs
Normal file
190
embassy-imxrt/src/crc.rs
Normal file
@ -0,0 +1,190 @@
|
||||
//! Cyclic Redundancy Check (CRC)
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::clocks::{enable_and_reset, SysconPeripheral};
|
||||
pub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial;
|
||||
use crate::{peripherals, Peri, PeripheralType};
|
||||
|
||||
/// CRC driver.
|
||||
pub struct Crc<'d> {
|
||||
info: Info,
|
||||
_config: Config,
|
||||
_lifetime: PhantomData<&'d ()>,
|
||||
}
|
||||
|
||||
/// CRC configuration
|
||||
pub struct Config {
|
||||
/// Polynomial to be used
|
||||
pub polynomial: Polynomial,
|
||||
|
||||
/// Reverse bit order of input?
|
||||
pub reverse_in: bool,
|
||||
|
||||
/// 1's complement input?
|
||||
pub complement_in: bool,
|
||||
|
||||
/// Reverse CRC bit order?
|
||||
pub reverse_out: bool,
|
||||
|
||||
/// 1's complement CRC?
|
||||
pub complement_out: bool,
|
||||
|
||||
/// CRC Seed
|
||||
pub seed: u32,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Create a new CRC config.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
polynomial: Polynomial,
|
||||
reverse_in: bool,
|
||||
complement_in: bool,
|
||||
reverse_out: bool,
|
||||
complement_out: bool,
|
||||
seed: u32,
|
||||
) -> Self {
|
||||
Config {
|
||||
polynomial,
|
||||
reverse_in,
|
||||
complement_in,
|
||||
reverse_out,
|
||||
complement_out,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
polynomial: Polynomial::CrcCcitt,
|
||||
reverse_in: false,
|
||||
complement_in: false,
|
||||
reverse_out: false,
|
||||
complement_out: false,
|
||||
seed: 0xffff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Crc<'d> {
|
||||
/// Instantiates new CRC peripheral and initializes to default values.
|
||||
pub fn new<T: Instance>(_peripheral: Peri<'d, T>, config: Config) -> Self {
|
||||
// enable CRC clock
|
||||
enable_and_reset::<T>();
|
||||
|
||||
let mut instance = Self {
|
||||
info: T::info(),
|
||||
_config: config,
|
||||
_lifetime: PhantomData,
|
||||
};
|
||||
|
||||
instance.reconfigure();
|
||||
instance
|
||||
}
|
||||
|
||||
/// Reconfigured the CRC peripheral.
|
||||
fn reconfigure(&mut self) {
|
||||
self.info.regs.mode().write(|w| {
|
||||
w.crc_poly()
|
||||
.variant(self._config.polynomial)
|
||||
.bit_rvs_wr()
|
||||
.variant(self._config.reverse_in)
|
||||
.cmpl_wr()
|
||||
.variant(self._config.complement_in)
|
||||
.bit_rvs_sum()
|
||||
.variant(self._config.reverse_out)
|
||||
.cmpl_sum()
|
||||
.variant(self._config.complement_out)
|
||||
});
|
||||
|
||||
// Init CRC value
|
||||
self.info
|
||||
.regs
|
||||
.seed()
|
||||
.write(|w| unsafe { w.crc_seed().bits(self._config.seed) });
|
||||
}
|
||||
|
||||
/// Feeds a byte into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_byte(&mut self, byte: u8) -> u32 {
|
||||
self.info.regs.wr_data8().write(|w| unsafe { w.bits(byte) });
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 {
|
||||
let (prefix, data, suffix) = unsafe { bytes.align_to::<u32>() };
|
||||
|
||||
for b in prefix {
|
||||
self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });
|
||||
}
|
||||
|
||||
for d in data {
|
||||
self.info.regs.wr_data32().write(|w| unsafe { w.bits(*d) });
|
||||
}
|
||||
|
||||
for b in suffix {
|
||||
self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });
|
||||
}
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds a halfword into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_halfword(&mut self, halfword: u16) -> u32 {
|
||||
self.info.regs.wr_data16().write(|w| unsafe { w.bits(halfword) });
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {
|
||||
for halfword in halfwords {
|
||||
self.info.regs.wr_data16().write(|w| unsafe { w.bits(*halfword) });
|
||||
}
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds a words into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_word(&mut self, word: u32) -> u32 {
|
||||
self.info.regs.wr_data32().write(|w| unsafe { w.bits(word) });
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
|
||||
/// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.
|
||||
pub fn feed_words(&mut self, words: &[u32]) -> u32 {
|
||||
for word in words {
|
||||
self.info.regs.wr_data32().write(|w| unsafe { w.bits(*word) });
|
||||
}
|
||||
|
||||
self.info.regs.sum().read().bits()
|
||||
}
|
||||
}
|
||||
|
||||
struct Info {
|
||||
regs: crate::pac::CrcEngine,
|
||||
}
|
||||
|
||||
trait SealedInstance {
|
||||
fn info() -> Info;
|
||||
}
|
||||
|
||||
/// CRC instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {}
|
||||
|
||||
impl Instance for peripherals::CRC {}
|
||||
|
||||
impl SealedInstance for peripherals::CRC {
|
||||
fn info() -> Info {
|
||||
// SAFETY: safe from single executor
|
||||
Info {
|
||||
regs: unsafe { crate::pac::CrcEngine::steal() },
|
||||
}
|
||||
}
|
||||
}
|
418
embassy-imxrt/src/dma.rs
Normal file
418
embassy-imxrt/src/dma.rs
Normal file
@ -0,0 +1,418 @@
|
||||
//! DMA driver.
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use pac::dma0::channel::cfg::Periphreqen;
|
||||
use pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width};
|
||||
|
||||
use crate::clocks::enable_and_reset;
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::DMA0;
|
||||
use crate::sealed::Sealed;
|
||||
use crate::{interrupt, pac, peripherals, BitIter};
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn DMA0() {
|
||||
let reg = unsafe { crate::pac::Dma0::steal() };
|
||||
|
||||
if reg.intstat().read().activeerrint().bit() {
|
||||
let err = reg.errint0().read().bits();
|
||||
|
||||
for channel in BitIter(err) {
|
||||
error!("DMA error interrupt on channel {}!", channel);
|
||||
reg.errint0().write(|w| unsafe { w.err().bits(1 << channel) });
|
||||
CHANNEL_WAKERS[channel as usize].wake();
|
||||
}
|
||||
}
|
||||
|
||||
if reg.intstat().read().activeint().bit() {
|
||||
let ia = reg.inta0().read().bits();
|
||||
|
||||
for channel in BitIter(ia) {
|
||||
reg.inta0().write(|w| unsafe { w.ia().bits(1 << channel) });
|
||||
CHANNEL_WAKERS[channel as usize].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize DMA controllers (DMA0 only, for now)
|
||||
pub(crate) unsafe fn init() {
|
||||
let sysctl0 = crate::pac::Sysctl0::steal();
|
||||
let dmactl0 = crate::pac::Dma0::steal();
|
||||
|
||||
enable_and_reset::<DMA0>();
|
||||
|
||||
interrupt::DMA0.disable();
|
||||
interrupt::DMA0.set_priority(interrupt::Priority::P3);
|
||||
|
||||
dmactl0.ctrl().modify(|_, w| w.enable().set_bit());
|
||||
|
||||
// Set channel descriptor SRAM base address
|
||||
// Descriptor base must be 1K aligned
|
||||
let descriptor_base = core::ptr::addr_of!(DESCRIPTORS.descs) as u32;
|
||||
dmactl0.srambase().write(|w| w.bits(descriptor_base));
|
||||
|
||||
// Ensure AHB priority it highest (M4 == DMAC0)
|
||||
sysctl0.ahbmatrixprior().modify(|_, w| w.m4().bits(0));
|
||||
|
||||
interrupt::DMA0.unpend();
|
||||
interrupt::DMA0.enable();
|
||||
}
|
||||
|
||||
/// DMA read.
|
||||
///
|
||||
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
||||
pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> {
|
||||
let count = ((to.len() / W::size() as usize) - 1) as isize;
|
||||
|
||||
copy_inner(
|
||||
ch,
|
||||
from as *const u32,
|
||||
(to as *mut u32).byte_offset(count * W::size()),
|
||||
W::width(),
|
||||
count,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// DMA write.
|
||||
///
|
||||
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
||||
pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> {
|
||||
let count = ((from.len() / W::size() as usize) - 1) as isize;
|
||||
|
||||
copy_inner(
|
||||
ch,
|
||||
(from as *const u32).byte_offset(count * W::size()),
|
||||
to as *mut u32,
|
||||
W::width(),
|
||||
count,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// DMA copy between slices.
|
||||
///
|
||||
/// SAFETY: Slices must point to locations reachable by DMA.
|
||||
pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> {
|
||||
let from_len = from.len();
|
||||
let to_len = to.len();
|
||||
assert_eq!(from_len, to_len);
|
||||
|
||||
let count = ((from_len / W::size() as usize) - 1) as isize;
|
||||
|
||||
copy_inner(
|
||||
ch,
|
||||
from.as_ptr().byte_offset(count * W::size()) as *const u32,
|
||||
to.as_mut_ptr().byte_offset(count * W::size()) as *mut u32,
|
||||
W::width(),
|
||||
count,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn copy_inner<'a, C: Channel>(
|
||||
ch: Peri<'a, C>,
|
||||
from: *const u32,
|
||||
to: *mut u32,
|
||||
width: Width,
|
||||
count: isize,
|
||||
incr_read: bool,
|
||||
incr_write: bool,
|
||||
periph: bool,
|
||||
) -> Transfer<'a, C> {
|
||||
let p = ch.regs();
|
||||
|
||||
unsafe {
|
||||
DESCRIPTORS.descs[ch.number() as usize].src = from as u32;
|
||||
DESCRIPTORS.descs[ch.number() as usize].dest = to as u32;
|
||||
}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
p.errint0().write(|w| unsafe { w.err().bits(1 << ch.number()) });
|
||||
p.inta0().write(|w| unsafe { w.ia().bits(1 << ch.number()) });
|
||||
|
||||
p.channel(ch.number().into()).cfg().write(|w| {
|
||||
unsafe { w.chpriority().bits(0) }
|
||||
.periphreqen()
|
||||
.variant(match periph {
|
||||
false => Periphreqen::Disabled,
|
||||
true => Periphreqen::Enabled,
|
||||
})
|
||||
.hwtrigen()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
p.intenset0().write(|w| unsafe { w.inten().bits(1 << ch.number()) });
|
||||
|
||||
p.channel(ch.number().into()).xfercfg().write(|w| {
|
||||
unsafe { w.xfercount().bits(count as u16) }
|
||||
.cfgvalid()
|
||||
.set_bit()
|
||||
.clrtrig()
|
||||
.set_bit()
|
||||
.reload()
|
||||
.clear_bit()
|
||||
.setinta()
|
||||
.set_bit()
|
||||
.width()
|
||||
.variant(width)
|
||||
.srcinc()
|
||||
.variant(match incr_read {
|
||||
false => Srcinc::NoIncrement,
|
||||
true => Srcinc::WidthX1,
|
||||
// REVISIT: what about WidthX2 and WidthX4?
|
||||
})
|
||||
.dstinc()
|
||||
.variant(match incr_write {
|
||||
false => Dstinc::NoIncrement,
|
||||
true => Dstinc::WidthX1,
|
||||
// REVISIT: what about WidthX2 and WidthX4?
|
||||
})
|
||||
});
|
||||
|
||||
p.enableset0().write(|w| unsafe { w.ena().bits(1 << ch.number()) });
|
||||
|
||||
p.channel(ch.number().into())
|
||||
.xfercfg()
|
||||
.modify(|_, w| w.swtrig().set_bit());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
Transfer::new(ch)
|
||||
}
|
||||
|
||||
/// DMA transfer driver.
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Transfer<'a, C: Channel> {
|
||||
channel: Peri<'a, C>,
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Transfer<'a, C> {
|
||||
pub(crate) fn new(channel: Peri<'a, C>) -> Self {
|
||||
Self { channel }
|
||||
}
|
||||
|
||||
pub(crate) fn abort(&mut self) -> usize {
|
||||
let p = self.channel.regs();
|
||||
|
||||
p.abort0().write(|w| w.channel(self.channel.number()).set_bit());
|
||||
while p.busy0().read().bsy().bits() & (1 << self.channel.number()) != 0 {}
|
||||
|
||||
p.enableclr0()
|
||||
.write(|w| unsafe { w.clr().bits(1 << self.channel.number()) });
|
||||
|
||||
let width: u8 = p
|
||||
.channel(self.channel.number().into())
|
||||
.xfercfg()
|
||||
.read()
|
||||
.width()
|
||||
.variant()
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let count = p
|
||||
.channel(self.channel.number().into())
|
||||
.xfercfg()
|
||||
.read()
|
||||
.xfercount()
|
||||
.bits()
|
||||
+ 1;
|
||||
|
||||
usize::from(count) * usize::from(width)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
||||
fn drop(&mut self) {
|
||||
self.abort();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
||||
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// Re-register the waker on each call to poll() because any calls to
|
||||
// wake will deregister the waker.
|
||||
CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
|
||||
|
||||
if self.channel.regs().active0().read().act().bits() & (1 << self.channel.number()) == 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// DMA channel descriptor
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
struct Descriptor {
|
||||
reserved: u32,
|
||||
src: u32,
|
||||
dest: u32,
|
||||
link: u32,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
reserved: 0,
|
||||
src: 0,
|
||||
dest: 0,
|
||||
link: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(align(1024))]
|
||||
struct Descriptors {
|
||||
descs: [Descriptor; CHANNEL_COUNT],
|
||||
}
|
||||
|
||||
impl Descriptors {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
descs: [const { Descriptor::new() }; CHANNEL_COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut DESCRIPTORS: Descriptors = Descriptors::new();
|
||||
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
|
||||
pub(crate) const CHANNEL_COUNT: usize = 33;
|
||||
|
||||
/// DMA channel interface.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Channel: PeripheralType + Sealed + Into<AnyChannel> + Sized + 'static {
|
||||
/// Channel number.
|
||||
fn number(&self) -> u8;
|
||||
|
||||
/// Channel registry block.
|
||||
fn regs(&self) -> &'static pac::dma0::RegisterBlock {
|
||||
unsafe { &*crate::pac::Dma0::ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
/// DMA word.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Word: Sealed {
|
||||
/// Transfer width.
|
||||
fn width() -> Width;
|
||||
|
||||
/// Size in bytes for the width.
|
||||
fn size() -> isize;
|
||||
}
|
||||
|
||||
impl Sealed for u8 {}
|
||||
impl Word for u8 {
|
||||
fn width() -> Width {
|
||||
Width::Bit8
|
||||
}
|
||||
|
||||
fn size() -> isize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for u16 {}
|
||||
impl Word for u16 {
|
||||
fn width() -> Width {
|
||||
Width::Bit16
|
||||
}
|
||||
|
||||
fn size() -> isize {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for u32 {}
|
||||
impl Word for u32 {
|
||||
fn width() -> Width {
|
||||
Width::Bit32
|
||||
}
|
||||
|
||||
fn size() -> isize {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
/// Type erased DMA channel.
|
||||
pub struct AnyChannel {
|
||||
number: u8,
|
||||
}
|
||||
|
||||
impl_peripheral!(AnyChannel);
|
||||
|
||||
impl Sealed for AnyChannel {}
|
||||
impl Channel for AnyChannel {
|
||||
fn number(&self) -> u8 {
|
||||
self.number
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! channel {
|
||||
($name:ident, $num:expr) => {
|
||||
impl Sealed for peripherals::$name {}
|
||||
impl Channel for peripherals::$name {
|
||||
fn number(&self) -> u8 {
|
||||
$num
|
||||
}
|
||||
}
|
||||
|
||||
impl From<peripherals::$name> for crate::dma::AnyChannel {
|
||||
fn from(val: peripherals::$name) -> Self {
|
||||
Self { number: val.number() }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
channel!(DMA0_CH0, 0);
|
||||
channel!(DMA0_CH1, 1);
|
||||
channel!(DMA0_CH2, 2);
|
||||
channel!(DMA0_CH3, 3);
|
||||
channel!(DMA0_CH4, 4);
|
||||
channel!(DMA0_CH5, 5);
|
||||
channel!(DMA0_CH6, 6);
|
||||
channel!(DMA0_CH7, 7);
|
||||
channel!(DMA0_CH8, 8);
|
||||
channel!(DMA0_CH9, 9);
|
||||
channel!(DMA0_CH10, 10);
|
||||
channel!(DMA0_CH11, 11);
|
||||
channel!(DMA0_CH12, 12);
|
||||
channel!(DMA0_CH13, 13);
|
||||
channel!(DMA0_CH14, 14);
|
||||
channel!(DMA0_CH15, 15);
|
||||
channel!(DMA0_CH16, 16);
|
||||
channel!(DMA0_CH17, 17);
|
||||
channel!(DMA0_CH18, 18);
|
||||
channel!(DMA0_CH19, 19);
|
||||
channel!(DMA0_CH20, 20);
|
||||
channel!(DMA0_CH21, 21);
|
||||
channel!(DMA0_CH22, 22);
|
||||
channel!(DMA0_CH23, 23);
|
||||
channel!(DMA0_CH24, 24);
|
||||
channel!(DMA0_CH25, 25);
|
||||
channel!(DMA0_CH26, 26);
|
||||
channel!(DMA0_CH27, 27);
|
||||
channel!(DMA0_CH28, 28);
|
||||
channel!(DMA0_CH29, 29);
|
||||
channel!(DMA0_CH30, 30);
|
||||
channel!(DMA0_CH31, 31);
|
||||
channel!(DMA0_CH32, 32);
|
252
embassy-imxrt/src/flexcomm/mod.rs
Normal file
252
embassy-imxrt/src/flexcomm/mod.rs
Normal file
@ -0,0 +1,252 @@
|
||||
//! Implements Flexcomm interface wrapper for easier usage across modules
|
||||
|
||||
pub mod uart;
|
||||
|
||||
use paste::paste;
|
||||
|
||||
use crate::clocks::{enable_and_reset, SysconPeripheral};
|
||||
use crate::peripherals::{
|
||||
FLEXCOMM0, FLEXCOMM1, FLEXCOMM14, FLEXCOMM15, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7,
|
||||
};
|
||||
use crate::{pac, PeripheralType};
|
||||
|
||||
/// clock selection option
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Clock {
|
||||
/// SFRO
|
||||
Sfro,
|
||||
|
||||
/// FFRO
|
||||
Ffro,
|
||||
|
||||
/// `AUDIO_PLL`
|
||||
AudioPll,
|
||||
|
||||
/// MASTER
|
||||
Master,
|
||||
|
||||
/// FCn_FRG with Main clock source
|
||||
FcnFrgMain,
|
||||
|
||||
/// FCn_FRG with Pll clock source
|
||||
FcnFrgPll,
|
||||
|
||||
/// FCn_FRG with Sfro clock source
|
||||
FcnFrgSfro,
|
||||
|
||||
/// FCn_FRG with Ffro clock source
|
||||
FcnFrgFfro,
|
||||
|
||||
/// disabled
|
||||
None,
|
||||
}
|
||||
|
||||
/// do not allow implementation of trait outside this mod
|
||||
mod sealed {
|
||||
/// trait does not get re-exported outside flexcomm mod, allowing us to safely expose only desired APIs
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// primary low-level flexcomm interface
|
||||
pub(crate) trait FlexcommLowLevel: sealed::Sealed + PeripheralType + SysconPeripheral + 'static + Send {
|
||||
// fetch the flexcomm register block for direct manipulation
|
||||
fn reg() -> &'static pac::flexcomm0::RegisterBlock;
|
||||
|
||||
// set the clock select for this flexcomm instance and remove from reset
|
||||
fn enable(clk: Clock);
|
||||
}
|
||||
|
||||
macro_rules! impl_flexcomm {
|
||||
($($idx:expr),*) => {
|
||||
$(
|
||||
paste!{
|
||||
impl sealed::Sealed for crate::peripherals::[<FLEXCOMM $idx>] {}
|
||||
|
||||
impl FlexcommLowLevel for crate::peripherals::[<FLEXCOMM $idx>] {
|
||||
fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
|
||||
// SAFETY: safe from single executor, enforce
|
||||
// via peripheral reference lifetime tracking
|
||||
unsafe {
|
||||
&*crate::pac::[<Flexcomm $idx>]::ptr()
|
||||
}
|
||||
}
|
||||
|
||||
fn enable(clk: Clock) {
|
||||
// SAFETY: safe from single executor
|
||||
let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
|
||||
|
||||
clkctl1.flexcomm($idx).fcfclksel().write(|w| match clk {
|
||||
Clock::Sfro => w.sel().sfro_clk(),
|
||||
Clock::Ffro => w.sel().ffro_clk(),
|
||||
Clock::AudioPll => w.sel().audio_pll_clk(),
|
||||
Clock::Master => w.sel().master_clk(),
|
||||
Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
|
||||
Clock::None => w.sel().none(), // no clock? throw an error?
|
||||
});
|
||||
|
||||
clkctl1.flexcomm($idx).frgclksel().write(|w| match clk {
|
||||
Clock::FcnFrgMain => w.sel().main_clk(),
|
||||
Clock::FcnFrgPll => w.sel().frg_pll_clk(),
|
||||
Clock::FcnFrgSfro => w.sel().sfro_clk(),
|
||||
Clock::FcnFrgFfro => w.sel().ffro_clk(),
|
||||
_ => w.sel().none(), // not using frg ...
|
||||
});
|
||||
|
||||
// todo: add support for frg div/mult
|
||||
clkctl1
|
||||
.flexcomm($idx)
|
||||
.frgctl()
|
||||
.write(|w|
|
||||
// SAFETY: unsafe only used for .bits() call
|
||||
unsafe { w.mult().bits(0) });
|
||||
|
||||
enable_and_reset::<[<FLEXCOMM $idx>]>();
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_flexcomm!(0, 1, 2, 3, 4, 5, 6, 7);
|
||||
|
||||
// TODO: FLEXCOMM 14 is untested. Enable SPI support on FLEXCOMM14
|
||||
// Add special case FLEXCOMM14
|
||||
impl sealed::Sealed for crate::peripherals::FLEXCOMM14 {}
|
||||
|
||||
impl FlexcommLowLevel for crate::peripherals::FLEXCOMM14 {
|
||||
fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
|
||||
// SAFETY: safe from single executor, enforce
|
||||
// via peripheral reference lifetime tracking
|
||||
unsafe { &*crate::pac::Flexcomm14::ptr() }
|
||||
}
|
||||
|
||||
fn enable(clk: Clock) {
|
||||
// SAFETY: safe from single executor
|
||||
let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
|
||||
|
||||
clkctl1.fc14fclksel().write(|w| match clk {
|
||||
Clock::Sfro => w.sel().sfro_clk(),
|
||||
Clock::Ffro => w.sel().ffro_clk(),
|
||||
Clock::AudioPll => w.sel().audio_pll_clk(),
|
||||
Clock::Master => w.sel().master_clk(),
|
||||
Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
|
||||
Clock::None => w.sel().none(), // no clock? throw an error?
|
||||
});
|
||||
|
||||
clkctl1.frg14clksel().write(|w| match clk {
|
||||
Clock::FcnFrgMain => w.sel().main_clk(),
|
||||
Clock::FcnFrgPll => w.sel().frg_pll_clk(),
|
||||
Clock::FcnFrgSfro => w.sel().sfro_clk(),
|
||||
Clock::FcnFrgFfro => w.sel().ffro_clk(),
|
||||
_ => w.sel().none(), // not using frg ...
|
||||
});
|
||||
|
||||
// todo: add support for frg div/mult
|
||||
clkctl1.frg14ctl().write(|w|
|
||||
// SAFETY: unsafe only used for .bits() call
|
||||
unsafe { w.mult().bits(0) });
|
||||
|
||||
enable_and_reset::<FLEXCOMM14>();
|
||||
}
|
||||
}
|
||||
|
||||
// Add special case FLEXCOMM15
|
||||
impl sealed::Sealed for crate::peripherals::FLEXCOMM15 {}
|
||||
|
||||
impl FlexcommLowLevel for crate::peripherals::FLEXCOMM15 {
|
||||
fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {
|
||||
// SAFETY: safe from single executor, enforce
|
||||
// via peripheral reference lifetime tracking
|
||||
unsafe { &*crate::pac::Flexcomm15::ptr() }
|
||||
}
|
||||
|
||||
fn enable(clk: Clock) {
|
||||
// SAFETY: safe from single executor
|
||||
let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };
|
||||
|
||||
clkctl1.fc15fclksel().write(|w| match clk {
|
||||
Clock::Sfro => w.sel().sfro_clk(),
|
||||
Clock::Ffro => w.sel().ffro_clk(),
|
||||
Clock::AudioPll => w.sel().audio_pll_clk(),
|
||||
Clock::Master => w.sel().master_clk(),
|
||||
Clock::FcnFrgMain => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgPll => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),
|
||||
Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),
|
||||
Clock::None => w.sel().none(), // no clock? throw an error?
|
||||
});
|
||||
clkctl1.frg15clksel().write(|w| match clk {
|
||||
Clock::FcnFrgMain => w.sel().main_clk(),
|
||||
Clock::FcnFrgPll => w.sel().frg_pll_clk(),
|
||||
Clock::FcnFrgSfro => w.sel().sfro_clk(),
|
||||
Clock::FcnFrgFfro => w.sel().ffro_clk(),
|
||||
_ => w.sel().none(), // not using frg ...
|
||||
});
|
||||
// todo: add support for frg div/mult
|
||||
clkctl1.frg15ctl().write(|w|
|
||||
// SAFETY: unsafe only used for .bits() call
|
||||
unsafe { w.mult().bits(0) });
|
||||
|
||||
enable_and_reset::<FLEXCOMM15>();
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! into_mode {
|
||||
($mode:ident, $($fc:ident),*) => {
|
||||
paste! {
|
||||
/// Sealed Mode trait
|
||||
trait [<SealedInto $mode:camel>]: FlexcommLowLevel {}
|
||||
|
||||
/// Select mode of operation
|
||||
#[allow(private_bounds)]
|
||||
pub trait [<Into $mode:camel>]: [<SealedInto $mode:camel>] {
|
||||
/// Set mode of operation
|
||||
fn [<into_ $mode>]() {
|
||||
Self::reg().pselid().write(|w| w.persel().[<$mode>]());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
paste!{
|
||||
impl [<SealedInto $mode:camel>] for crate::peripherals::$fc {}
|
||||
impl [<Into $mode:camel>] for crate::peripherals::$fc {}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
into_mode!(usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7);
|
||||
into_mode!(spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14);
|
||||
into_mode!(i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15);
|
||||
|
||||
into_mode!(
|
||||
i2s_transmit,
|
||||
FLEXCOMM0,
|
||||
FLEXCOMM1,
|
||||
FLEXCOMM2,
|
||||
FLEXCOMM3,
|
||||
FLEXCOMM4,
|
||||
FLEXCOMM5,
|
||||
FLEXCOMM6,
|
||||
FLEXCOMM7
|
||||
);
|
||||
|
||||
into_mode!(
|
||||
i2s_receive,
|
||||
FLEXCOMM0,
|
||||
FLEXCOMM1,
|
||||
FLEXCOMM2,
|
||||
FLEXCOMM3,
|
||||
FLEXCOMM4,
|
||||
FLEXCOMM5,
|
||||
FLEXCOMM6,
|
||||
FLEXCOMM7
|
||||
);
|
1230
embassy-imxrt/src/flexcomm/uart.rs
Normal file
1230
embassy-imxrt/src/flexcomm/uart.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ use crate::clocks::enable_and_reset;
|
||||
use crate::iopctl::IopctlPin;
|
||||
pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};
|
||||
use crate::sealed::Sealed;
|
||||
use crate::{interrupt, peripherals, Peri, PeripheralType};
|
||||
use crate::{interrupt, peripherals, BitIter, Peri, PeripheralType};
|
||||
|
||||
// This should be unique per IMXRT package
|
||||
const PORT_COUNT: usize = 8;
|
||||
@ -63,24 +63,6 @@ fn GPIO_INTA() {
|
||||
irq_handler(&GPIO_WAKERS);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
struct BitIter(u32);
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
impl Iterator for BitIter {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.0.trailing_zeros() {
|
||||
32 => None,
|
||||
b => {
|
||||
self.0 &= !(1 << b);
|
||||
Some(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
fn irq_handler(port_wakers: &[Option<&PortWaker>]) {
|
||||
let reg = unsafe { crate::pac::Gpio::steal() };
|
||||
|
@ -18,8 +18,12 @@ compile_error!(
|
||||
pub(crate) mod fmt;
|
||||
|
||||
pub mod clocks;
|
||||
pub mod crc;
|
||||
pub mod dma;
|
||||
pub mod flexcomm;
|
||||
pub mod gpio;
|
||||
pub mod iopctl;
|
||||
pub mod rng;
|
||||
|
||||
#[cfg(feature = "_time-driver")]
|
||||
pub mod time_driver;
|
||||
@ -52,16 +56,20 @@ pub use crate::pac::NVIC_PRIO_BITS;
|
||||
/// ```rust,ignore
|
||||
/// use embassy_imxrt::{bind_interrupts, flexspi, peripherals};
|
||||
///
|
||||
/// bind_interrupts!(struct Irqs {
|
||||
/// FLEXSPI_IRQ => flexspi::InterruptHandler<peripherals::FLEXSPI>;
|
||||
/// });
|
||||
/// bind_interrupts!(
|
||||
/// /// Binds the FLEXSPI interrupt.
|
||||
/// struct Irqs {
|
||||
/// FLEXSPI_IRQ => flexspi::InterruptHandler<peripherals::FLEXSPI>;
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
||||
($(#[$attr:meta])* $vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$(#[$attr])*
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
@ -127,14 +135,16 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
// before doing anything important.
|
||||
let peripherals = Peripherals::take();
|
||||
|
||||
#[cfg(feature = "_time-driver")]
|
||||
time_driver::init(config.time_interrupt_priority);
|
||||
|
||||
unsafe {
|
||||
if let Err(e) = clocks::init(config.clocks) {
|
||||
error!("unable to initialize Clocks for reason: {:?}", e);
|
||||
// Panic here?
|
||||
}
|
||||
dma::init();
|
||||
}
|
||||
#[cfg(feature = "_time-driver")]
|
||||
time_driver::init(config.time_interrupt_priority);
|
||||
gpio::init();
|
||||
|
||||
peripherals
|
||||
@ -143,3 +153,21 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
pub(crate) mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
struct BitIter(u32);
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
impl Iterator for BitIter {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.0.trailing_zeros() {
|
||||
32 => None,
|
||||
b => {
|
||||
self.0 &= !(1 << b);
|
||||
Some(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
287
embassy-imxrt/src/rng.rs
Normal file
287
embassy-imxrt/src/rng.rs
Normal file
@ -0,0 +1,287 @@
|
||||
//! True Random Number Generator (TRNG)
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_futures::block_on;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::clocks::{enable_and_reset, SysconPeripheral};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, peripherals, Peri, PeripheralType};
|
||||
|
||||
static RNG_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
/// RNG ;error
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// Seed error.
|
||||
SeedError,
|
||||
|
||||
/// HW Error.
|
||||
HwError,
|
||||
|
||||
/// Frequency Count Fail
|
||||
FreqCountFail,
|
||||
}
|
||||
|
||||
/// RNG interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let regs = T::info().regs;
|
||||
let int_status = regs.int_status().read();
|
||||
|
||||
if int_status.ent_val().bit_is_set()
|
||||
|| int_status.hw_err().bit_is_set()
|
||||
|| int_status.frq_ct_fail().bit_is_set()
|
||||
{
|
||||
regs.int_ctrl().modify(|_, w| {
|
||||
w.ent_val()
|
||||
.ent_val_0()
|
||||
.hw_err()
|
||||
.hw_err_0()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_0()
|
||||
});
|
||||
RNG_WAKER.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RNG driver.
|
||||
pub struct Rng<'d> {
|
||||
info: Info,
|
||||
_lifetime: PhantomData<&'d ()>,
|
||||
}
|
||||
|
||||
impl<'d> Rng<'d> {
|
||||
/// Create a new RNG driver.
|
||||
pub fn new<T: Instance>(
|
||||
_inner: Peri<'d, T>,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
enable_and_reset::<T>();
|
||||
|
||||
let mut random = Self {
|
||||
info: T::info(),
|
||||
_lifetime: PhantomData,
|
||||
};
|
||||
random.init();
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
random
|
||||
}
|
||||
|
||||
/// Reset the RNG.
|
||||
pub fn reset(&mut self) {
|
||||
self.info.regs.mctl().write(|w| w.rst_def().set_bit().prgm().set_bit());
|
||||
}
|
||||
|
||||
/// Fill the given slice with random values.
|
||||
pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
// We have a total of 16 words (512 bits) of entropy at our
|
||||
// disposal. The idea here is to read all bits and copy the
|
||||
// necessary bytes to the slice.
|
||||
for chunk in dest.chunks_mut(64) {
|
||||
self.async_fill_chunk(chunk).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn async_fill_chunk(&mut self, chunk: &mut [u8]) -> Result<(), Error> {
|
||||
// wait for interrupt
|
||||
let res = poll_fn(|cx| {
|
||||
// Check if already ready.
|
||||
// TODO: Is this necessary? Could we just check once after
|
||||
// the waker has been registered?
|
||||
if self.info.regs.int_status().read().ent_val().bit_is_set() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
RNG_WAKER.register(cx.waker());
|
||||
|
||||
self.unmask_interrupts();
|
||||
|
||||
let mctl = self.info.regs.mctl().read();
|
||||
|
||||
// Check again if interrupt fired
|
||||
if mctl.ent_val().bit_is_set() {
|
||||
Poll::Ready(Ok(()))
|
||||
} else if mctl.err().bit_is_set() {
|
||||
Poll::Ready(Err(Error::HwError))
|
||||
} else if mctl.fct_fail().bit_is_set() {
|
||||
Poll::Ready(Err(Error::FreqCountFail))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let bits = self.info.regs.mctl().read();
|
||||
|
||||
if bits.ent_val().bit_is_set() {
|
||||
let mut entropy = [0; 16];
|
||||
|
||||
for (i, item) in entropy.iter_mut().enumerate() {
|
||||
*item = self.info.regs.ent(i).read().bits();
|
||||
}
|
||||
|
||||
// Read MCTL after reading ENT15
|
||||
let _ = self.info.regs.mctl().read();
|
||||
|
||||
if entropy.iter().any(|e| *e == 0) {
|
||||
return Err(Error::SeedError);
|
||||
}
|
||||
|
||||
// SAFETY: entropy is the same for input and output types in
|
||||
// native endianness.
|
||||
let entropy: [u8; 64] = unsafe { core::mem::transmute(entropy) };
|
||||
|
||||
// write bytes to chunk
|
||||
chunk.copy_from_slice(&entropy[..chunk.len()]);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn mask_interrupts(&mut self) {
|
||||
self.info.regs.int_mask().write(|w| {
|
||||
w.ent_val()
|
||||
.ent_val_0()
|
||||
.hw_err()
|
||||
.hw_err_0()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_0()
|
||||
});
|
||||
}
|
||||
|
||||
fn unmask_interrupts(&mut self) {
|
||||
self.info.regs.int_mask().modify(|_, w| {
|
||||
w.ent_val()
|
||||
.ent_val_1()
|
||||
.hw_err()
|
||||
.hw_err_1()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_1()
|
||||
});
|
||||
}
|
||||
|
||||
fn enable_interrupts(&mut self) {
|
||||
self.info.regs.int_ctrl().write(|w| {
|
||||
w.ent_val()
|
||||
.ent_val_1()
|
||||
.hw_err()
|
||||
.hw_err_1()
|
||||
.frq_ct_fail()
|
||||
.frq_ct_fail_1()
|
||||
});
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.mask_interrupts();
|
||||
|
||||
// Switch TRNG to programming mode
|
||||
self.info.regs.mctl().modify(|_, w| w.prgm().set_bit());
|
||||
|
||||
self.enable_interrupts();
|
||||
|
||||
// Switch TRNG to Run Mode
|
||||
self.info
|
||||
.regs
|
||||
.mctl()
|
||||
.modify(|_, w| w.trng_acc().set_bit().prgm().clear_bit());
|
||||
}
|
||||
|
||||
/// Generate a random u32
|
||||
pub fn blocking_next_u32(&mut self) -> u32 {
|
||||
let mut bytes = [0u8; 4];
|
||||
block_on(self.async_fill_bytes(&mut bytes)).unwrap();
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Generate a random u64
|
||||
pub fn blocking_next_u64(&mut self) -> u64 {
|
||||
let mut bytes = [0u8; 8];
|
||||
block_on(self.async_fill_bytes(&mut bytes)).unwrap();
|
||||
u64::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Fill a slice with random bytes.
|
||||
pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
block_on(self.async_fill_bytes(dest)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> rand_core_06::RngCore for Rng<'d> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.blocking_next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.blocking_next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
|
||||
self.blocking_fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> rand_core_06::CryptoRng for Rng<'d> {}
|
||||
|
||||
impl<'d> rand_core_09::RngCore for Rng<'d> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.blocking_next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.blocking_next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> rand_core_09::CryptoRng for Rng<'d> {}
|
||||
|
||||
struct Info {
|
||||
regs: crate::pac::Trng,
|
||||
}
|
||||
|
||||
trait SealedInstance {
|
||||
fn info() -> Info;
|
||||
}
|
||||
|
||||
/// RNG instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {
|
||||
/// Interrupt for this RNG instance.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
impl Instance for peripherals::RNG {
|
||||
type Interrupt = crate::interrupt::typelevel::RNG;
|
||||
}
|
||||
|
||||
impl SealedInstance for peripherals::RNG {
|
||||
fn info() -> Info {
|
||||
// SAFETY: safe from single executor
|
||||
Info {
|
||||
regs: unsafe { crate::pac::Trng::steal() },
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional =
|
||||
embedded-hal = { version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
fixed = "1.29"
|
||||
log = { version = "0.4.14", optional = true }
|
||||
cortex-m-rt = ">=0.6.15,<0.8"
|
||||
@ -46,14 +46,14 @@ cortex-m = "0.7.6"
|
||||
critical-section = "1.2.0"
|
||||
|
||||
# mspm0-metapac = { version = "" }
|
||||
mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-119240dd23ef5748d2a7bef219ca298d37ba604a" }
|
||||
mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-66a55c7bf38a2201ff48c299843e741f2d537f0b" }
|
||||
|
||||
[build-dependencies]
|
||||
proc-macro2 = "1.0.94"
|
||||
quote = "1.0.40"
|
||||
|
||||
# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
|
||||
mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-119240dd23ef5748d2a7bef219ca298d37ba604a", default-features = false, features = ["metadata"] }
|
||||
mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-66a55c7bf38a2201ff48c299843e741f2d537f0b", default-features = false, features = ["metadata"] }
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
@ -120,14 +120,138 @@ time-driver-tima0 = ["_time-driver"]
|
||||
time-driver-tima1 = ["_time-driver"]
|
||||
|
||||
#! ## Chip-selection features
|
||||
#! Select your chip by specifying the model as a feature, e.g. `mspm0g350x`.
|
||||
#! Select your chip by specifying the model as a feature, e.g. `mspm0g3507pm`.
|
||||
#! Check the `Cargo.toml` for the latest list of supported chips.
|
||||
#!
|
||||
#! **Important:** Do not forget to adapt the target chip in your toolchain,
|
||||
#! e.g. in `.cargo/config.toml`.
|
||||
|
||||
mspm0c110x = [ "mspm0-metapac/mspm0c110x" ]
|
||||
mspm0g350x = [ "mspm0-metapac/mspm0g350x" ]
|
||||
mspm0g351x = [ "mspm0-metapac/mspm0g351x" ]
|
||||
mspm0l130x = [ "mspm0-metapac/mspm0l130x" ]
|
||||
mspm0l222x = [ "mspm0-metapac/mspm0l222x" ]
|
||||
mspm0c1103dgs20 = ["mspm0-metapac/mspm0c1103dgs20"]
|
||||
mspm0c1103dsg = ["mspm0-metapac/mspm0c1103dsg"]
|
||||
mspm0c1103dyy = ["mspm0-metapac/mspm0c1103dyy"]
|
||||
mspm0c1103ruk = ["mspm0-metapac/mspm0c1103ruk"]
|
||||
mspm0c1104dgs20 = ["mspm0-metapac/mspm0c1104dgs20"]
|
||||
mspm0c1104dsg = ["mspm0-metapac/mspm0c1104dsg"]
|
||||
mspm0c1104dyy = ["mspm0-metapac/mspm0c1104dyy"]
|
||||
mspm0c1104ruk = ["mspm0-metapac/mspm0c1104ruk"]
|
||||
mspm0c1104ycj = ["mspm0-metapac/mspm0c1104ycj"]
|
||||
mspm0g1105dgs28 = ["mspm0-metapac/mspm0g1105dgs28"]
|
||||
mspm0g1105pm = ["mspm0-metapac/mspm0g1105pm"]
|
||||
mspm0g1105pt = ["mspm0-metapac/mspm0g1105pt"]
|
||||
mspm0g1105rge = ["mspm0-metapac/mspm0g1105rge"]
|
||||
mspm0g1105rgz = ["mspm0-metapac/mspm0g1105rgz"]
|
||||
mspm0g1105rhb = ["mspm0-metapac/mspm0g1105rhb"]
|
||||
mspm0g1106dgs28 = ["mspm0-metapac/mspm0g1106dgs28"]
|
||||
mspm0g1106pm = ["mspm0-metapac/mspm0g1106pm"]
|
||||
mspm0g1106pt = ["mspm0-metapac/mspm0g1106pt"]
|
||||
mspm0g1106rge = ["mspm0-metapac/mspm0g1106rge"]
|
||||
mspm0g1106rgz = ["mspm0-metapac/mspm0g1106rgz"]
|
||||
mspm0g1106rhb = ["mspm0-metapac/mspm0g1106rhb"]
|
||||
mspm0g1107dgs28 = ["mspm0-metapac/mspm0g1107dgs28"]
|
||||
mspm0g1107pm = ["mspm0-metapac/mspm0g1107pm"]
|
||||
mspm0g1107pt = ["mspm0-metapac/mspm0g1107pt"]
|
||||
mspm0g1107rge = ["mspm0-metapac/mspm0g1107rge"]
|
||||
mspm0g1107rgz = ["mspm0-metapac/mspm0g1107rgz"]
|
||||
mspm0g1107rhb = ["mspm0-metapac/mspm0g1107rhb"]
|
||||
mspm0g1107ycj = ["mspm0-metapac/mspm0g1107ycj"]
|
||||
mspm0g1505pm = ["mspm0-metapac/mspm0g1505pm"]
|
||||
mspm0g1505pt = ["mspm0-metapac/mspm0g1505pt"]
|
||||
mspm0g1505rge = ["mspm0-metapac/mspm0g1505rge"]
|
||||
mspm0g1505rgz = ["mspm0-metapac/mspm0g1505rgz"]
|
||||
mspm0g1505rhb = ["mspm0-metapac/mspm0g1505rhb"]
|
||||
mspm0g1506pm = ["mspm0-metapac/mspm0g1506pm"]
|
||||
mspm0g1506pt = ["mspm0-metapac/mspm0g1506pt"]
|
||||
mspm0g1506rge = ["mspm0-metapac/mspm0g1506rge"]
|
||||
mspm0g1506rgz = ["mspm0-metapac/mspm0g1506rgz"]
|
||||
mspm0g1506rhb = ["mspm0-metapac/mspm0g1506rhb"]
|
||||
mspm0g1507pm = ["mspm0-metapac/mspm0g1507pm"]
|
||||
mspm0g1507pt = ["mspm0-metapac/mspm0g1507pt"]
|
||||
mspm0g1507rge = ["mspm0-metapac/mspm0g1507rge"]
|
||||
mspm0g1507rgz = ["mspm0-metapac/mspm0g1507rgz"]
|
||||
mspm0g1507rhb = ["mspm0-metapac/mspm0g1507rhb"]
|
||||
mspm0g1507ycj = ["mspm0-metapac/mspm0g1507ycj"]
|
||||
mspm0g1519rgz = ["mspm0-metapac/mspm0g1519rgz"]
|
||||
mspm0g1519rhb = ["mspm0-metapac/mspm0g1519rhb"]
|
||||
mspm0g3105dgs20 = ["mspm0-metapac/mspm0g3105dgs20"]
|
||||
mspm0g3105dgs28 = ["mspm0-metapac/mspm0g3105dgs28"]
|
||||
mspm0g3105rhb = ["mspm0-metapac/mspm0g3105rhb"]
|
||||
mspm0g3106dgs20 = ["mspm0-metapac/mspm0g3106dgs20"]
|
||||
mspm0g3106dgs28 = ["mspm0-metapac/mspm0g3106dgs28"]
|
||||
mspm0g3106rhb = ["mspm0-metapac/mspm0g3106rhb"]
|
||||
mspm0g3107dgs20 = ["mspm0-metapac/mspm0g3107dgs20"]
|
||||
mspm0g3107dgs28 = ["mspm0-metapac/mspm0g3107dgs28"]
|
||||
mspm0g3107rhb = ["mspm0-metapac/mspm0g3107rhb"]
|
||||
mspm0g3505dgs28 = ["mspm0-metapac/mspm0g3505dgs28"]
|
||||
mspm0g3505pm = ["mspm0-metapac/mspm0g3505pm"]
|
||||
mspm0g3505pt = ["mspm0-metapac/mspm0g3505pt"]
|
||||
mspm0g3505rgz = ["mspm0-metapac/mspm0g3505rgz"]
|
||||
mspm0g3505rhb = ["mspm0-metapac/mspm0g3505rhb"]
|
||||
mspm0g3506dgs28 = ["mspm0-metapac/mspm0g3506dgs28"]
|
||||
mspm0g3506pm = ["mspm0-metapac/mspm0g3506pm"]
|
||||
mspm0g3506pt = ["mspm0-metapac/mspm0g3506pt"]
|
||||
mspm0g3506rgz = ["mspm0-metapac/mspm0g3506rgz"]
|
||||
mspm0g3506rhb = ["mspm0-metapac/mspm0g3506rhb"]
|
||||
mspm0g3507dgs28 = ["mspm0-metapac/mspm0g3507dgs28"]
|
||||
mspm0g3507pm = ["mspm0-metapac/mspm0g3507pm"]
|
||||
mspm0g3507pt = ["mspm0-metapac/mspm0g3507pt"]
|
||||
mspm0g3507rgz = ["mspm0-metapac/mspm0g3507rgz"]
|
||||
mspm0g3507rhb = ["mspm0-metapac/mspm0g3507rhb"]
|
||||
mspm0g3519pm = ["mspm0-metapac/mspm0g3519pm"]
|
||||
mspm0g3519pn = ["mspm0-metapac/mspm0g3519pn"]
|
||||
mspm0g3519pz = ["mspm0-metapac/mspm0g3519pz"]
|
||||
mspm0g3519rgz = ["mspm0-metapac/mspm0g3519rgz"]
|
||||
mspm0g3519rhb = ["mspm0-metapac/mspm0g3519rhb"]
|
||||
mspm0l1105dgs20 = ["mspm0-metapac/mspm0l1105dgs20"]
|
||||
mspm0l1105dgs28 = ["mspm0-metapac/mspm0l1105dgs28"]
|
||||
mspm0l1105dyy = ["mspm0-metapac/mspm0l1105dyy"]
|
||||
mspm0l1105rge = ["mspm0-metapac/mspm0l1105rge"]
|
||||
mspm0l1105rtr = ["mspm0-metapac/mspm0l1105rtr"]
|
||||
mspm0l1106dgs20 = ["mspm0-metapac/mspm0l1106dgs20"]
|
||||
mspm0l1106dgs28 = ["mspm0-metapac/mspm0l1106dgs28"]
|
||||
mspm0l1106dyy = ["mspm0-metapac/mspm0l1106dyy"]
|
||||
mspm0l1106rge = ["mspm0-metapac/mspm0l1106rge"]
|
||||
mspm0l1106rhb = ["mspm0-metapac/mspm0l1106rhb"]
|
||||
mspm0l1106rtr = ["mspm0-metapac/mspm0l1106rtr"]
|
||||
mspm0l1227pm = ["mspm0-metapac/mspm0l1227pm"]
|
||||
mspm0l1227pn = ["mspm0-metapac/mspm0l1227pn"]
|
||||
mspm0l1227pt = ["mspm0-metapac/mspm0l1227pt"]
|
||||
mspm0l1227rge = ["mspm0-metapac/mspm0l1227rge"]
|
||||
mspm0l1227rgz = ["mspm0-metapac/mspm0l1227rgz"]
|
||||
mspm0l1227rhb = ["mspm0-metapac/mspm0l1227rhb"]
|
||||
mspm0l1228pm = ["mspm0-metapac/mspm0l1228pm"]
|
||||
mspm0l1228pn = ["mspm0-metapac/mspm0l1228pn"]
|
||||
mspm0l1228pt = ["mspm0-metapac/mspm0l1228pt"]
|
||||
mspm0l1228rge = ["mspm0-metapac/mspm0l1228rge"]
|
||||
mspm0l1228rgz = ["mspm0-metapac/mspm0l1228rgz"]
|
||||
mspm0l1228rhb = ["mspm0-metapac/mspm0l1228rhb"]
|
||||
mspm0l1303rge = ["mspm0-metapac/mspm0l1303rge"]
|
||||
mspm0l1304dgs20 = ["mspm0-metapac/mspm0l1304dgs20"]
|
||||
mspm0l1304dgs28 = ["mspm0-metapac/mspm0l1304dgs28"]
|
||||
mspm0l1304dyy = ["mspm0-metapac/mspm0l1304dyy"]
|
||||
mspm0l1304rge = ["mspm0-metapac/mspm0l1304rge"]
|
||||
mspm0l1304rhb = ["mspm0-metapac/mspm0l1304rhb"]
|
||||
mspm0l1304rtr = ["mspm0-metapac/mspm0l1304rtr"]
|
||||
mspm0l1305dgs20 = ["mspm0-metapac/mspm0l1305dgs20"]
|
||||
mspm0l1305dgs28 = ["mspm0-metapac/mspm0l1305dgs28"]
|
||||
mspm0l1305dyy = ["mspm0-metapac/mspm0l1305dyy"]
|
||||
mspm0l1305rge = ["mspm0-metapac/mspm0l1305rge"]
|
||||
mspm0l1305rtr = ["mspm0-metapac/mspm0l1305rtr"]
|
||||
mspm0l1306dgs20 = ["mspm0-metapac/mspm0l1306dgs20"]
|
||||
mspm0l1306dgs28 = ["mspm0-metapac/mspm0l1306dgs28"]
|
||||
mspm0l1306dyy = ["mspm0-metapac/mspm0l1306dyy"]
|
||||
mspm0l1306rge = ["mspm0-metapac/mspm0l1306rge"]
|
||||
mspm0l1306rhb = ["mspm0-metapac/mspm0l1306rhb"]
|
||||
mspm0l1343dgs20 = ["mspm0-metapac/mspm0l1343dgs20"]
|
||||
mspm0l1344dgs20 = ["mspm0-metapac/mspm0l1344dgs20"]
|
||||
mspm0l1345dgs28 = ["mspm0-metapac/mspm0l1345dgs28"]
|
||||
mspm0l1346dgs28 = ["mspm0-metapac/mspm0l1346dgs28"]
|
||||
mspm0l2227pm = ["mspm0-metapac/mspm0l2227pm"]
|
||||
mspm0l2227pn = ["mspm0-metapac/mspm0l2227pn"]
|
||||
mspm0l2227pt = ["mspm0-metapac/mspm0l2227pt"]
|
||||
mspm0l2227rgz = ["mspm0-metapac/mspm0l2227rgz"]
|
||||
mspm0l2228pm = ["mspm0-metapac/mspm0l2228pm"]
|
||||
mspm0l2228pn = ["mspm0-metapac/mspm0l2228pn"]
|
||||
mspm0l2228pt = ["mspm0-metapac/mspm0l2228pt"]
|
||||
mspm0l2228rgz = ["mspm0-metapac/mspm0l2228rgz"]
|
||||
msps003f3pw20 = ["mspm0-metapac/msps003f3pw20"]
|
||||
msps003f4pw20 = ["mspm0-metapac/msps003f4pw20"]
|
||||
|
28
embassy-mspm0/README.md
Normal file
28
embassy-mspm0/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Embassy MSPM0 HAL
|
||||
|
||||
The embassy-mspm0 HAL aims to provide a safe, idiomatic hardware abstraction layer for all MSPM0 and MSPS003 chips.
|
||||
|
||||
* [Documentation](https://docs.embassy.dev/embassy-mspm0/) (**Important:** use docs.embassy.dev rather than docs.rs to see the specific docs for the chip you’re using!)
|
||||
* [Source](https://github.com/embassy-rs/embassy/tree/main/embassy-mspm0)
|
||||
* [Examples](https://github.com/embassy-rs/embassy/tree/main/examples)
|
||||
|
||||
## Embedded-hal
|
||||
|
||||
The `embassy-mspm0` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async).
|
||||
|
||||
## A note on feature flag names
|
||||
|
||||
Feature flag names for chips do not include temperature rating or distribution format.
|
||||
|
||||
Usually chapter 10 of your device's datasheet will explain the device nomenclature and how to decode it. Feature names in embassy-mspm0 only use the following from device nomenclature:
|
||||
- MCU platform
|
||||
- Product family
|
||||
- Device subfamily
|
||||
- Flash memory
|
||||
- Package type
|
||||
|
||||
This means for a part such as `MSPM0G3507SPMR`, the feature name is `mspm0g3507pm`. This also means that `MSPM0G3507QPMRQ1` uses the feature `mspm0g3507pm`, since the Q1 parts are just qualified variants of the base G3507 with a PM (QFP-64) package.
|
||||
|
||||
## Interoperability
|
||||
|
||||
This crate can run on any executor.
|
@ -7,7 +7,7 @@ use std::sync::LazyLock;
|
||||
use std::{env, fs};
|
||||
|
||||
use common::CfgSet;
|
||||
use mspm0_metapac::metadata::METADATA;
|
||||
use mspm0_metapac::metadata::{ALL_CHIPS, METADATA};
|
||||
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
@ -24,6 +24,27 @@ fn generate_code() {
|
||||
|
||||
cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]);
|
||||
|
||||
let chip_name = match env::vars()
|
||||
.map(|(a, _)| a)
|
||||
.filter(|x| x.starts_with("CARGO_FEATURE_MSPM0") || x.starts_with("CARGO_FEATURE_MSPS"))
|
||||
.get_one()
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(GetOneError::None) => panic!("No mspm0xx/mspsxx Cargo feature enabled"),
|
||||
Err(GetOneError::Multiple) => panic!("Multiple mspm0xx/mspsxx Cargo features enabled"),
|
||||
}
|
||||
.strip_prefix("CARGO_FEATURE_")
|
||||
.unwrap()
|
||||
.to_ascii_lowercase()
|
||||
.replace('_', "-");
|
||||
|
||||
eprintln!("chip: {chip_name}");
|
||||
|
||||
cfgs.enable_all(&get_chip_cfgs(&chip_name));
|
||||
for chip in ALL_CHIPS {
|
||||
cfgs.declare_all(&get_chip_cfgs(&chip));
|
||||
}
|
||||
|
||||
let mut singletons = get_singletons(&mut cfgs);
|
||||
|
||||
time_driver(&mut singletons, &mut cfgs);
|
||||
@ -44,6 +65,64 @@ fn generate_code() {
|
||||
rustfmt(&out_file);
|
||||
}
|
||||
|
||||
fn get_chip_cfgs(chip_name: &str) -> Vec<String> {
|
||||
let mut cfgs = Vec::new();
|
||||
|
||||
// GPIO on C110x is special as it does not belong to an interrupt group.
|
||||
if chip_name.starts_with("mspm0c110") || chip_name.starts_with("msps003f") {
|
||||
cfgs.push("mspm0c110x".to_string());
|
||||
}
|
||||
|
||||
// Family ranges (temporary until int groups are generated)
|
||||
//
|
||||
// TODO: Remove this once int group stuff is generated.
|
||||
if chip_name.starts_with("mspm0g110") {
|
||||
cfgs.push("mspm0g110x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0g150") {
|
||||
cfgs.push("mspm0g150x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0g151") {
|
||||
cfgs.push("mspm0g151x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0g310") {
|
||||
cfgs.push("mspm0g310x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0g350") {
|
||||
cfgs.push("mspm0g350x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0g351") {
|
||||
cfgs.push("mspm0g351x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0l110") {
|
||||
cfgs.push("mspm0l110x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0l122") {
|
||||
cfgs.push("mspm0l122x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0l130") {
|
||||
cfgs.push("mspm0l130x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0l134") {
|
||||
cfgs.push("mspm0l134x".to_string());
|
||||
}
|
||||
|
||||
if chip_name.starts_with("mspm0l222") {
|
||||
cfgs.push("mspm0l222x".to_string());
|
||||
}
|
||||
|
||||
cfgs
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Singleton {
|
||||
name: String,
|
||||
@ -146,7 +225,7 @@ fn make_valid_identifier(s: &str) -> Singleton {
|
||||
}
|
||||
|
||||
fn generate_pincm_mapping() -> TokenStream {
|
||||
let pincms = METADATA.pincm_mappings.iter().map(|mapping| {
|
||||
let pincms = METADATA.pins.iter().map(|mapping| {
|
||||
let port_letter = mapping.pin.strip_prefix("P").unwrap();
|
||||
let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;
|
||||
// This assumes all ports are single letter length.
|
||||
@ -174,11 +253,11 @@ fn generate_pincm_mapping() -> TokenStream {
|
||||
}
|
||||
|
||||
fn generate_pin() -> TokenStream {
|
||||
let pin_impls = METADATA.pincm_mappings.iter().map(|pincm_mapping| {
|
||||
let name = Ident::new(&pincm_mapping.pin, Span::call_site());
|
||||
let port_letter = pincm_mapping.pin.strip_prefix("P").unwrap();
|
||||
let pin_impls = METADATA.pins.iter().map(|pin| {
|
||||
let name = Ident::new(&pin.pin, Span::call_site());
|
||||
let port_letter = pin.pin.strip_prefix("P").unwrap();
|
||||
let port_letter = port_letter.chars().next().unwrap();
|
||||
let pin_number = Literal::u8_unsuffixed(pincm_mapping.pin[2..].parse::<u8>().unwrap());
|
||||
let pin_number = Literal::u8_unsuffixed(pin.pin[2..].parse::<u8>().unwrap());
|
||||
|
||||
let port = Ident::new(&format!("Port{}", port_letter), Span::call_site());
|
||||
|
||||
|
@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::pac::gpio::vals::*;
|
||||
use crate::pac::gpio::{self};
|
||||
#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
|
||||
#[cfg(all(feature = "rt", any(mspm0c110x, mspm0l110x)))]
|
||||
use crate::pac::interrupt;
|
||||
use crate::pac::{self};
|
||||
|
||||
@ -1120,7 +1120,7 @@ impl Iterator for BitIter {
|
||||
}
|
||||
|
||||
// C110x has a dedicated interrupt just for GPIOA, as it does not have a GROUP1 interrupt.
|
||||
#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
|
||||
#[cfg(all(feature = "rt", any(mspm0c110x, mspm0l110x)))]
|
||||
#[interrupt]
|
||||
fn GPIOA() {
|
||||
gpioa_interrupt();
|
||||
|
47
embassy-mspm0/src/int_group/g110x.rs
Normal file
47
embassy-mspm0/src/int_group/g110x.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::pac;
|
||||
use crate::pac::interrupt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP0() {
|
||||
use mspm0_metapac::Group0;
|
||||
|
||||
let group = pac::CPUSS.int_group(0);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group0::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 0: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group0::WWDT0 => todo!("implement WWDT0"),
|
||||
Group0::WWDT1 => todo!("implement WWDT1"),
|
||||
Group0::DEBUGSS => todo!("implement DEBUGSS"),
|
||||
Group0::FLASHCTL => todo!("implement FLASHCTL"),
|
||||
Group0::SYSCTL => todo!("implement SYSCTL"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP1() {
|
||||
use mspm0_metapac::Group1;
|
||||
|
||||
let group = pac::CPUSS.int_group(1);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group1::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 1: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group1::GPIOA => crate::gpio::gpioa_interrupt(),
|
||||
Group1::GPIOB => crate::gpio::gpiob_interrupt(),
|
||||
}
|
||||
}
|
51
embassy-mspm0/src/int_group/g150x.rs
Normal file
51
embassy-mspm0/src/int_group/g150x.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use crate::pac;
|
||||
use crate::pac::interrupt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP0() {
|
||||
use mspm0_metapac::Group0;
|
||||
|
||||
let group = pac::CPUSS.int_group(0);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group0::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 0: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group0::WWDT0 => todo!("implement WWDT0"),
|
||||
Group0::WWDT1 => todo!("implement WWDT1"),
|
||||
Group0::DEBUGSS => todo!("implement DEBUGSS"),
|
||||
Group0::FLASHCTL => todo!("implement FLASHCTL"),
|
||||
Group0::SYSCTL => todo!("implement SYSCTL"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP1() {
|
||||
use mspm0_metapac::Group1;
|
||||
|
||||
let group = pac::CPUSS.int_group(1);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group1::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 1: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group1::GPIOA => crate::gpio::gpioa_interrupt(),
|
||||
Group1::GPIOB => crate::gpio::gpiob_interrupt(),
|
||||
Group1::COMP0 => todo!("implement COMP0"),
|
||||
Group1::COMP1 => todo!("implement COMP1"),
|
||||
Group1::COMP2 => todo!("implement COMP2"),
|
||||
Group1::TRNG => todo!("implement TRNG"),
|
||||
}
|
||||
}
|
52
embassy-mspm0/src/int_group/g151x.rs
Normal file
52
embassy-mspm0/src/int_group/g151x.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::pac;
|
||||
use crate::pac::interrupt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP0() {
|
||||
use mspm0_metapac::Group0;
|
||||
|
||||
let group = pac::CPUSS.int_group(0);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group0::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 0: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group0::WWDT0 => todo!("implement WWDT0"),
|
||||
Group0::WWDT1 => todo!("implement WWDT1"),
|
||||
Group0::DEBUGSS => todo!("implement DEBUGSS"),
|
||||
Group0::FLASHCTL => todo!("implement FLASHCTL"),
|
||||
Group0::SYSCTL => todo!("implement SYSCTL"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP1() {
|
||||
use mspm0_metapac::Group1;
|
||||
|
||||
let group = pac::CPUSS.int_group(1);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group1::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 1: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group1::GPIOA => crate::gpio::gpioa_interrupt(),
|
||||
Group1::GPIOB => crate::gpio::gpiob_interrupt(),
|
||||
Group1::COMP0 => todo!("implement COMP0"),
|
||||
Group1::COMP1 => todo!("implement COMP1"),
|
||||
Group1::COMP2 => todo!("implement COMP2"),
|
||||
Group1::TRNG => todo!("implement TRNG"),
|
||||
Group1::GPIOC => crate::gpio::gpioc_interrupt(),
|
||||
}
|
||||
}
|
48
embassy-mspm0/src/int_group/g310x.rs
Normal file
48
embassy-mspm0/src/int_group/g310x.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use crate::pac;
|
||||
use crate::pac::interrupt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP0() {
|
||||
use mspm0_metapac::Group0;
|
||||
|
||||
let group = pac::CPUSS.int_group(0);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group0::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 0: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group0::WWDT0 => todo!("implement WWDT0"),
|
||||
Group0::WWDT1 => todo!("implement WWDT1"),
|
||||
Group0::DEBUGSS => todo!("implement DEBUGSS"),
|
||||
Group0::FLASHCTL => todo!("implement FLASHCTL"),
|
||||
Group0::SYSCTL => todo!("implement SYSCTL"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP1() {
|
||||
use mspm0_metapac::Group1;
|
||||
|
||||
let group = pac::CPUSS.int_group(1);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group1::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 1: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group1::GPIOA => crate::gpio::gpioa_interrupt(),
|
||||
Group1::GPIOB => crate::gpio::gpiob_interrupt(),
|
||||
Group1::TRNG => todo!("implement TRNG"),
|
||||
}
|
||||
}
|
25
embassy-mspm0/src/int_group/l11xx.rs
Normal file
25
embassy-mspm0/src/int_group/l11xx.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::pac;
|
||||
use crate::pac::interrupt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP0() {
|
||||
use mspm0_metapac::Group0;
|
||||
|
||||
let group = pac::CPUSS.int_group(0);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group0::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 0: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group0::WWDT0 => todo!("implement WWDT0"),
|
||||
Group0::DEBUGSS => todo!("implement DEBUGSS"),
|
||||
Group0::FLASHCTL => todo!("implement FLASHCTL"),
|
||||
Group0::SYSCTL => todo!("implement SYSCTL"),
|
||||
}
|
||||
}
|
49
embassy-mspm0/src/int_group/l12xx.rs
Normal file
49
embassy-mspm0/src/int_group/l12xx.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use crate::pac;
|
||||
use crate::pac::interrupt;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP0() {
|
||||
use mspm0_metapac::Group0;
|
||||
|
||||
let group = pac::CPUSS.int_group(0);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group0::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 0: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group0::WWDT0 => todo!("implement WWDT0"),
|
||||
Group0::DEBUGSS => todo!("implement DEBUGSS"),
|
||||
Group0::FLASHCTL => todo!("implement FLASHCTL"),
|
||||
Group0::SYSCTL => todo!("implement SYSCTL"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn GROUP1() {
|
||||
use mspm0_metapac::Group1;
|
||||
|
||||
let group = pac::CPUSS.int_group(1);
|
||||
|
||||
// Must subtract by 1 since NO_INTR is value 0
|
||||
let iidx = group.iidx().read().stat().to_bits() - 1;
|
||||
|
||||
let Ok(group) = pac::Group1::try_from(iidx as u8) else {
|
||||
debug!("Invalid IIDX for group 1: {}", iidx);
|
||||
return;
|
||||
};
|
||||
|
||||
match group {
|
||||
Group1::GPIOA => crate::gpio::gpioa_interrupt(),
|
||||
Group1::GPIOB => crate::gpio::gpiob_interrupt(),
|
||||
Group1::COMP0 => todo!("implement COMP0"),
|
||||
Group1::TRNG => todo!("implement TRNG"),
|
||||
Group1::GPIOC => crate::gpio::gpioc_interrupt(),
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
#![no_std]
|
||||
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
|
||||
#![cfg_attr(
|
||||
docsrs,
|
||||
doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-mspm0'>browse the `embassy-mspm0` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only, while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\n\n"
|
||||
)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
@ -35,11 +40,17 @@ pub mod mode {
|
||||
mod time_driver;
|
||||
|
||||
// Interrupt group handlers.
|
||||
#[cfg_attr(feature = "mspm0c110x", path = "int_group/c110x.rs")]
|
||||
#[cfg_attr(feature = "mspm0g350x", path = "int_group/g350x.rs")]
|
||||
#[cfg_attr(feature = "mspm0g351x", path = "int_group/g351x.rs")]
|
||||
#[cfg_attr(feature = "mspm0l130x", path = "int_group/l130x.rs")]
|
||||
#[cfg_attr(feature = "mspm0l222x", path = "int_group/l222x.rs")]
|
||||
#[cfg_attr(mspm0c110x, path = "int_group/c110x.rs")]
|
||||
#[cfg_attr(mspm0g110x, path = "int_group/g110x.rs")]
|
||||
#[cfg_attr(mspm0g150x, path = "int_group/g150x.rs")]
|
||||
#[cfg_attr(mspm0g350x, path = "int_group/g350x.rs")]
|
||||
#[cfg_attr(mspm0g151x, path = "int_group/g151x.rs")]
|
||||
#[cfg_attr(mspm0g351x, path = "int_group/g351x.rs")]
|
||||
#[cfg_attr(mspm0g310x, path = "int_group/g310x.rs")]
|
||||
#[cfg_attr(mspm0l110x, path = "int_group/l11xx.rs")]
|
||||
#[cfg_attr(mspm0l122x, path = "int_group/l12xx.rs")]
|
||||
#[cfg_attr(any(mspm0l130x, mspm0l134x), path = "int_group/l13xx.rs")]
|
||||
#[cfg_attr(mspm0l222x, path = "int_group/l222x.rs")]
|
||||
mod int_group;
|
||||
|
||||
pub(crate) mod _generated {
|
||||
@ -109,7 +120,7 @@ pub fn init(_config: Config) -> Peripherals {
|
||||
|
||||
_generated::enable_group_interrupts(cs);
|
||||
|
||||
#[cfg(feature = "mspm0c110x")]
|
||||
#[cfg(mspm0c110x)]
|
||||
unsafe {
|
||||
use crate::_generated::interrupt::typelevel::Interrupt;
|
||||
crate::interrupt::typelevel::GPIOA::enable();
|
||||
|
@ -11,7 +11,7 @@ documentation = "https://docs.embassy.dev/embassy-net-adin1110"
|
||||
|
||||
[dependencies]
|
||||
heapless = "0.8"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0" }
|
||||
@ -29,7 +29,7 @@ critical-section = { version = "1.1.2", features = ["std"] }
|
||||
futures-test = "0.3.28"
|
||||
|
||||
[features]
|
||||
defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ]
|
||||
defmt = ["dep:defmt", "embedded-hal-1/defmt-03"]
|
||||
log = ["dep:log"]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
|
@ -22,7 +22,7 @@ target = "thumbv7em-none-eabi"
|
||||
features = ["defmt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
|
@ -22,4 +22,4 @@ target = "thumbv7em-none-eabi"
|
||||
features = ["defmt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
|
@ -16,7 +16,7 @@ embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||
embassy-time = { version = "0.4.0", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
|
@ -10,11 +10,11 @@ repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-net-esp-hosted"
|
||||
|
||||
[features]
|
||||
defmt = [ "dep:defmt", "heapless/defmt-03" ]
|
||||
log = [ "dep:log" ]
|
||||
defmt = ["dep:defmt", "heapless/defmt-03"]
|
||||
log = ["dep:log"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embassy-time = { version = "0.4.0", path = "../embassy-time" }
|
||||
|
@ -10,20 +10,20 @@ repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-net-nrf91"
|
||||
|
||||
[features]
|
||||
defmt = [ "dep:defmt", "heapless/defmt-03" ]
|
||||
log = [ "dep:log" ]
|
||||
defmt = ["dep:defmt", "heapless/defmt-03"]
|
||||
log = ["dep:log"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
nrf-pac = "0.1.0"
|
||||
cortex-m = "0.7.7"
|
||||
|
||||
embassy-time = { version = "0.4.0", path = "../embassy-time" }
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync"}
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"}
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
|
||||
|
||||
heapless = "0.8"
|
||||
embedded-io = "0.6.1"
|
||||
|
@ -14,7 +14,7 @@ defmt = ["dep:defmt", "ppproto/defmt"]
|
||||
log = ["dep:log", "ppproto/log"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
|
@ -15,7 +15,7 @@ embedded-hal-async = { version = "1.0" }
|
||||
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
|
||||
embassy-time = { version = "0.4.0", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/"
|
||||
|
@ -68,7 +68,7 @@ alloc = ["smoltcp/alloc"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
defmt = { version = "0.3.8", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
smoltcp = { version = "0.12.0", default-features = false, features = [
|
||||
|
@ -154,15 +154,17 @@ embedded-hal-async = { version = "1.0" }
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
|
||||
rand-core-06 = { package = "rand_core", version = "0.6" }
|
||||
rand-core-09 = { package = "rand_core", version = "0.9" }
|
||||
|
||||
nrf-pac = "0.1.0"
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
bitflags = "2.4.2"
|
||||
log = { version = "0.4.14", optional = true }
|
||||
cortex-m-rt = ">=0.6.15,<0.8"
|
||||
cortex-m = "0.7.6"
|
||||
critical-section = "1.1"
|
||||
rand_core = "0.6.3"
|
||||
fixed = "1.10.0"
|
||||
embedded-storage = "0.3.1"
|
||||
embedded-storage-async = "0.4.1"
|
||||
|
@ -262,6 +262,9 @@ embassy_hal_internal::peripherals! {
|
||||
PPI_GROUP4,
|
||||
PPI_GROUP5,
|
||||
|
||||
// IPC
|
||||
IPC,
|
||||
|
||||
// GPIO port 0
|
||||
#[cfg(feature = "lfxo-pins-as-gpio")]
|
||||
P0_00,
|
||||
@ -327,6 +330,8 @@ embassy_hal_internal::peripherals! {
|
||||
EGU5,
|
||||
}
|
||||
|
||||
impl_ipc!(IPC, IPC, IPC);
|
||||
|
||||
impl_usb!(USBD, USBD, USBD);
|
||||
|
||||
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
|
||||
|
@ -141,6 +141,9 @@ embassy_hal_internal::peripherals! {
|
||||
PPI_GROUP4,
|
||||
PPI_GROUP5,
|
||||
|
||||
// IPC
|
||||
IPC,
|
||||
|
||||
// GPIO port 0
|
||||
P0_00,
|
||||
P0_01,
|
||||
@ -200,6 +203,8 @@ embassy_hal_internal::peripherals! {
|
||||
EGU0,
|
||||
}
|
||||
|
||||
impl_ipc!(IPC, IPC, IPC);
|
||||
|
||||
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
|
||||
impl_spim!(SERIAL0, SPIM0, SERIAL0);
|
||||
impl_spis!(SERIAL0, SPIS0, SERIAL0);
|
||||
|
363
embassy-nrf/src/ipc.rs
Normal file
363
embassy-nrf/src/ipc.rs
Normal file
@ -0,0 +1,363 @@
|
||||
//! InterProcessor Communication (IPC)
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::{Peri, PeripheralType};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, pac, ppi};
|
||||
|
||||
const EVENT_COUNT: usize = 16;
|
||||
|
||||
/// IPC Event
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum EventNumber {
|
||||
/// IPC Event 0
|
||||
Event0 = 0,
|
||||
/// IPC Event 1
|
||||
Event1 = 1,
|
||||
/// IPC Event 2
|
||||
Event2 = 2,
|
||||
/// IPC Event 3
|
||||
Event3 = 3,
|
||||
/// IPC Event 4
|
||||
Event4 = 4,
|
||||
/// IPC Event 5
|
||||
Event5 = 5,
|
||||
/// IPC Event 6
|
||||
Event6 = 6,
|
||||
/// IPC Event 7
|
||||
Event7 = 7,
|
||||
/// IPC Event 8
|
||||
Event8 = 8,
|
||||
/// IPC Event 9
|
||||
Event9 = 9,
|
||||
/// IPC Event 10
|
||||
Event10 = 10,
|
||||
/// IPC Event 11
|
||||
Event11 = 11,
|
||||
/// IPC Event 12
|
||||
Event12 = 12,
|
||||
/// IPC Event 13
|
||||
Event13 = 13,
|
||||
/// IPC Event 14
|
||||
Event14 = 14,
|
||||
/// IPC Event 15
|
||||
Event15 = 15,
|
||||
}
|
||||
|
||||
const EVENTS: [EventNumber; EVENT_COUNT] = [
|
||||
EventNumber::Event0,
|
||||
EventNumber::Event1,
|
||||
EventNumber::Event2,
|
||||
EventNumber::Event3,
|
||||
EventNumber::Event4,
|
||||
EventNumber::Event5,
|
||||
EventNumber::Event6,
|
||||
EventNumber::Event7,
|
||||
EventNumber::Event8,
|
||||
EventNumber::Event9,
|
||||
EventNumber::Event10,
|
||||
EventNumber::Event11,
|
||||
EventNumber::Event12,
|
||||
EventNumber::Event13,
|
||||
EventNumber::Event14,
|
||||
EventNumber::Event15,
|
||||
];
|
||||
|
||||
/// IPC Channel
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum IpcChannel {
|
||||
/// IPC Channel 0
|
||||
Channel0,
|
||||
/// IPC Channel 1
|
||||
Channel1,
|
||||
/// IPC Channel 2
|
||||
Channel2,
|
||||
/// IPC Channel 3
|
||||
Channel3,
|
||||
/// IPC Channel 4
|
||||
Channel4,
|
||||
/// IPC Channel 5
|
||||
Channel5,
|
||||
/// IPC Channel 6
|
||||
Channel6,
|
||||
/// IPC Channel 7
|
||||
Channel7,
|
||||
/// IPC Channel 8
|
||||
Channel8,
|
||||
/// IPC Channel 9
|
||||
Channel9,
|
||||
/// IPC Channel 10
|
||||
Channel10,
|
||||
/// IPC Channel 11
|
||||
Channel11,
|
||||
/// IPC Channel 12
|
||||
Channel12,
|
||||
/// IPC Channel 13
|
||||
Channel13,
|
||||
/// IPC Channel 14
|
||||
Channel14,
|
||||
/// IPC Channel 15
|
||||
Channel15,
|
||||
}
|
||||
|
||||
impl IpcChannel {
|
||||
fn mask(self) -> u32 {
|
||||
1 << (self as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt Handler
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let regs = T::regs();
|
||||
|
||||
// Check if an event was generated, and if it was, trigger the corresponding waker
|
||||
for event in EVENTS {
|
||||
if regs.events_receive(event as usize).read() & 0x01 == 0x01 {
|
||||
regs.intenclr().write(|w| w.0 = 0x01 << event as u32);
|
||||
T::state().wakers[event as usize].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// IPC driver
|
||||
#[non_exhaustive]
|
||||
pub struct Ipc<'d, T: Instance> {
|
||||
/// Event 0
|
||||
pub event0: Event<'d, T>,
|
||||
/// Event 1
|
||||
pub event1: Event<'d, T>,
|
||||
/// Event 2
|
||||
pub event2: Event<'d, T>,
|
||||
/// Event 3
|
||||
pub event3: Event<'d, T>,
|
||||
/// Event 4
|
||||
pub event4: Event<'d, T>,
|
||||
/// Event 5
|
||||
pub event5: Event<'d, T>,
|
||||
/// Event 6
|
||||
pub event6: Event<'d, T>,
|
||||
/// Event 7
|
||||
pub event7: Event<'d, T>,
|
||||
/// Event 8
|
||||
pub event8: Event<'d, T>,
|
||||
/// Event 9
|
||||
pub event9: Event<'d, T>,
|
||||
/// Event 10
|
||||
pub event10: Event<'d, T>,
|
||||
/// Event 11
|
||||
pub event11: Event<'d, T>,
|
||||
/// Event 12
|
||||
pub event12: Event<'d, T>,
|
||||
/// Event 13
|
||||
pub event13: Event<'d, T>,
|
||||
/// Event 14
|
||||
pub event14: Event<'d, T>,
|
||||
/// Event 15
|
||||
pub event15: Event<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Ipc<'d, T> {
|
||||
/// Create a new IPC driver.
|
||||
pub fn new(
|
||||
_p: Peri<'d, T>,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
) -> Self {
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
let _phantom = PhantomData;
|
||||
#[rustfmt::skip]
|
||||
let r = Self { // attributes on expressions are experimental
|
||||
event0: Event { number: EventNumber::Event0, _phantom },
|
||||
event1: Event { number: EventNumber::Event1, _phantom },
|
||||
event2: Event { number: EventNumber::Event2, _phantom },
|
||||
event3: Event { number: EventNumber::Event3, _phantom },
|
||||
event4: Event { number: EventNumber::Event4, _phantom },
|
||||
event5: Event { number: EventNumber::Event5, _phantom },
|
||||
event6: Event { number: EventNumber::Event6, _phantom },
|
||||
event7: Event { number: EventNumber::Event7, _phantom },
|
||||
event8: Event { number: EventNumber::Event8, _phantom },
|
||||
event9: Event { number: EventNumber::Event9, _phantom },
|
||||
event10: Event { number: EventNumber::Event10, _phantom },
|
||||
event11: Event { number: EventNumber::Event11, _phantom },
|
||||
event12: Event { number: EventNumber::Event12, _phantom },
|
||||
event13: Event { number: EventNumber::Event13, _phantom },
|
||||
event14: Event { number: EventNumber::Event14, _phantom },
|
||||
event15: Event { number: EventNumber::Event15, _phantom },
|
||||
};
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
/// IPC event
|
||||
pub struct Event<'d, T: Instance> {
|
||||
number: EventNumber,
|
||||
_phantom: PhantomData<&'d T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Event<'d, T> {
|
||||
/// Trigger the event.
|
||||
pub fn trigger(&self) {
|
||||
let nr = self.number;
|
||||
T::regs().tasks_send(nr as usize).write_value(1);
|
||||
}
|
||||
|
||||
/// Wait for the event to be triggered.
|
||||
pub async fn wait(&mut self) {
|
||||
let regs = T::regs();
|
||||
let nr = self.number as usize;
|
||||
regs.intenset().write(|w| w.0 = 1 << nr);
|
||||
poll_fn(|cx| {
|
||||
T::state().wakers[nr].register(cx.waker());
|
||||
|
||||
if regs.events_receive(nr).read() == 1 {
|
||||
regs.events_receive(nr).write_value(0x00);
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Returns the [`EventNumber`] of the event.
|
||||
pub fn number(&self) -> EventNumber {
|
||||
self.number
|
||||
}
|
||||
|
||||
/// Create a handle that can trigger the event.
|
||||
pub fn trigger_handle(&self) -> EventTrigger<'d, T> {
|
||||
EventTrigger {
|
||||
number: self.number,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the channels the event will broadcast to
|
||||
pub fn configure_trigger<I: IntoIterator<Item = IpcChannel>>(&mut self, channels: I) {
|
||||
T::regs().send_cnf(self.number as usize).write(|w| {
|
||||
for channel in channels {
|
||||
w.0 |= channel.mask();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Configure the channels the event will listen on
|
||||
pub fn configure_wait<I: IntoIterator<Item = IpcChannel>>(&mut self, channels: I) {
|
||||
T::regs().receive_cnf(self.number as usize).write(|w| {
|
||||
for channel in channels {
|
||||
w.0 |= channel.mask();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the task for the IPC event to use with PPI.
|
||||
pub fn task(&self) -> ppi::Task<'d> {
|
||||
let nr = self.number as usize;
|
||||
let regs = T::regs();
|
||||
ppi::Task::from_reg(regs.tasks_send(nr))
|
||||
}
|
||||
|
||||
/// Get the event for the IPC event to use with PPI.
|
||||
pub fn event(&self) -> ppi::Event<'d> {
|
||||
let nr = self.number as usize;
|
||||
let regs = T::regs();
|
||||
ppi::Event::from_reg(regs.events_receive(nr))
|
||||
}
|
||||
|
||||
/// Reborrow into a "child" Event.
|
||||
///
|
||||
/// `self` will stay borrowed until the child Event is dropped.
|
||||
pub fn reborrow(&mut self) -> Event<'_, T> {
|
||||
Self { ..*self }
|
||||
}
|
||||
|
||||
/// Steal an IPC event by number.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The event number must not be in use by another [`Event`].
|
||||
pub unsafe fn steal(number: EventNumber) -> Self {
|
||||
Self {
|
||||
number,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle that can trigger an IPC event.
|
||||
///
|
||||
/// This `struct` is returned by [`Event::trigger_handle`].
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EventTrigger<'d, T: Instance> {
|
||||
number: EventNumber,
|
||||
_phantom: PhantomData<&'d T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> EventTrigger<'_, T> {
|
||||
/// Trigger the event.
|
||||
pub fn trigger(&self) {
|
||||
let nr = self.number;
|
||||
T::regs().tasks_send(nr as usize).write_value(1);
|
||||
}
|
||||
|
||||
/// Returns the [`EventNumber`] of the event.
|
||||
pub fn number(&self) -> EventNumber {
|
||||
self.number
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct State {
|
||||
wakers: [AtomicWaker; EVENT_COUNT],
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
wakers: [const { AtomicWaker::new() }; EVENT_COUNT],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait SealedInstance {
|
||||
fn regs() -> pac::ipc::Ipc;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
/// IPC peripheral instance.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: PeripheralType + SealedInstance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
macro_rules! impl_ipc {
|
||||
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||
impl crate::ipc::SealedInstance for peripherals::$type {
|
||||
fn regs() -> pac::ipc::Ipc {
|
||||
pac::$pac_type
|
||||
}
|
||||
|
||||
fn state() -> &'static crate::ipc::State {
|
||||
static STATE: crate::ipc::State = crate::ipc::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
impl crate::ipc::Instance for peripherals::$type {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
};
|
||||
}
|
@ -88,6 +88,8 @@ pub mod gpiote;
|
||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
|
||||
pub mod i2s;
|
||||
#[cfg(feature = "_nrf5340")]
|
||||
pub mod ipc;
|
||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||
#[cfg(any(
|
||||
feature = "nrf52832",
|
||||
@ -198,9 +200,12 @@ mod chip;
|
||||
/// ```rust,ignore
|
||||
/// use embassy_nrf::{bind_interrupts, spim, peripherals};
|
||||
///
|
||||
/// bind_interrupts!(struct Irqs {
|
||||
/// SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
|
||||
/// });
|
||||
/// bind_interrupts!(
|
||||
/// /// Binds the SPIM3 interrupt.
|
||||
/// struct Irqs {
|
||||
/// SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Example of how to bind multiple interrupts in a single macro invocation:
|
||||
@ -217,7 +222,7 @@ mod chip;
|
||||
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident {
|
||||
($(#[$attr:meta])* $vis:vis struct $name:ident {
|
||||
$(
|
||||
$(#[cfg($cond_irq:meta)])?
|
||||
$irq:ident => $(
|
||||
@ -227,6 +232,7 @@ macro_rules! bind_interrupts {
|
||||
)*
|
||||
}) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$(#[$attr])*
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
@ -337,7 +343,7 @@ pub mod config {
|
||||
/// 3.0 V
|
||||
_3V0 = 4,
|
||||
/// 3.3 V
|
||||
_3v3 = 5,
|
||||
_3V3 = 5,
|
||||
//ERASED = 7, means 1.8V
|
||||
}
|
||||
|
||||
@ -369,7 +375,7 @@ pub mod config {
|
||||
/// 3.0 V
|
||||
_3V0 = 4,
|
||||
/// 3.3 V
|
||||
_3v3 = 5,
|
||||
_3V3 = 5,
|
||||
//ERASED = 7, means 1.8V
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,21 @@ impl<'d, T: Instance> Rng<'d, T> {
|
||||
|
||||
self.stop();
|
||||
}
|
||||
|
||||
/// Generate a random u32
|
||||
pub fn blocking_next_u32(&mut self) -> u32 {
|
||||
let mut bytes = [0; 4];
|
||||
self.blocking_fill_bytes(&mut bytes);
|
||||
// We don't care about the endianness, so just use the native one.
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Generate a random u64
|
||||
pub fn blocking_next_u64(&mut self) -> u64 {
|
||||
let mut bytes = [0; 8];
|
||||
self.blocking_fill_bytes(&mut bytes);
|
||||
u64::from_ne_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Rng<'d, T> {
|
||||
@ -180,31 +195,37 @@ impl<'d, T: Instance> Drop for Rng<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> {
|
||||
impl<'d, T: Instance> rand_core_06::RngCore for Rng<'d, T> {
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut bytes = [0; 4];
|
||||
self.blocking_fill_bytes(&mut bytes);
|
||||
// We don't care about the endianness, so just use the native one.
|
||||
u32::from_ne_bytes(bytes)
|
||||
self.blocking_next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut bytes = [0; 8];
|
||||
self.blocking_fill_bytes(&mut bytes);
|
||||
u64::from_ne_bytes(bytes)
|
||||
self.blocking_next_u64()
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
|
||||
self.blocking_fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {}
|
||||
impl<'d, T: Instance> rand_core_06::CryptoRng for Rng<'d, T> {}
|
||||
|
||||
impl<'d, T: Instance> rand_core_09::RngCore for Rng<'d, T> {
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.blocking_next_u32()
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.blocking_next_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core_09::CryptoRng for Rng<'d, T> {}
|
||||
|
||||
/// Peripheral static state
|
||||
pub(crate) struct State {
|
||||
|
@ -10,8 +10,11 @@ critical-section = "1.1.2"
|
||||
embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
lpc55-pac = "0.5.0"
|
||||
defmt = "0.3.8"
|
||||
defmt = { version = "1", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["lpc55-pac/rt"]
|
||||
rt = ["lpc55-pac/rt"]
|
||||
|
||||
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
|
||||
defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"]
|
||||
|
@ -26,7 +26,10 @@ features = ["defmt", "unstable-pac", "time-driver", "rp2040"]
|
||||
|
||||
[features]
|
||||
default = [ "rt" ]
|
||||
## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
|
||||
|
||||
## Enable the `rt` feature of [`rp-pac`](https://docs.rs/rp-pac).
|
||||
## With `rt` enabled the PAC provides interrupt vectors instead of letting [`cortex-m-rt`](https://docs.rs/cortex-m-rt) do that.
|
||||
## See <https://docs.rs/cortex-m-rt/latest/cortex_m_rt/#device> for more info.
|
||||
rt = [ "rp-pac/rt" ]
|
||||
|
||||
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
|
||||
@ -142,7 +145,7 @@ embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", fe
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" }
|
||||
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
||||
atomic-polyfill = "1.0.1"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
nb = "1.1.0"
|
||||
cfg-if = "1.0.0"
|
||||
@ -154,7 +157,6 @@ embedded-io = { version = "0.6.1" }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
embedded-storage = { version = "0.3" }
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
rand_core = "0.6.4"
|
||||
fixed = "1.28.0"
|
||||
|
||||
rp-pac = { version = "7.0.0" }
|
||||
@ -164,6 +166,9 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0" }
|
||||
embedded-hal-nb = { version = "1.0" }
|
||||
|
||||
rand-core-06 = { package = "rand_core", version = "0.6" }
|
||||
rand-core-09 = { package = "rand_core", version = "0.9" }
|
||||
|
||||
pio = { version = "0.3" }
|
||||
rp2040-boot2 = "0.3"
|
||||
document-features = "0.2.10"
|
||||
|
@ -21,6 +21,8 @@ static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
#[derive(Default)]
|
||||
pub struct Config {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
enum Source<'p> {
|
||||
Pin(Peri<'p, AnyPin>),
|
||||
TempSensor(Peri<'p, ADC_TEMP_SENSOR>),
|
||||
|
@ -38,7 +38,7 @@
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ### Standard 125MHz configuration
|
||||
//! ### Standard 125MHz (rp2040) or 150Mhz (rp235x) configuration
|
||||
//! ```rust,ignore
|
||||
//! let config = ClockConfig::crystal(12_000_000);
|
||||
//! ```
|
||||
@ -82,6 +82,18 @@ use crate::{pac, reset, Peri};
|
||||
// be very useful until we have runtime clock reconfiguration. once this
|
||||
// happens we can resurrect the commented-out gpin bits.
|
||||
|
||||
/// Clock error types.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ClockError {
|
||||
/// PLL failed to lock within the timeout period.
|
||||
PllLockTimedOut,
|
||||
/// Could not find valid PLL parameters for system clock.
|
||||
InvalidPllParameters,
|
||||
/// Reading the core voltage failed due to an unexpected value in the register.
|
||||
UnexpectedCoreVoltageRead,
|
||||
}
|
||||
|
||||
struct Clocks {
|
||||
xosc: AtomicU32,
|
||||
sys: AtomicU32,
|
||||
@ -136,15 +148,16 @@ pub enum PeriClkSrc {
|
||||
// Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
|
||||
}
|
||||
|
||||
/// Core voltage regulator settings for RP2040.
|
||||
/// Core voltage regulator settings.
|
||||
///
|
||||
/// The RP2040 voltage regulator can be configured for different output voltages.
|
||||
/// The voltage regulator can be configured for different output voltages.
|
||||
/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
|
||||
#[cfg(feature = "rp2040")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum CoreVoltage {
|
||||
/// 0.80V - Suitable for lower frequencies
|
||||
/// 0.80V
|
||||
V0_80 = 0b0000,
|
||||
/// 0.85V
|
||||
V0_85 = 0b0110,
|
||||
@ -168,11 +181,58 @@ pub enum CoreVoltage {
|
||||
V1_30 = 0b1111,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
/// Core voltage regulator settings.
|
||||
///
|
||||
/// The voltage regulator can be configured for different output voltages.
|
||||
/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
|
||||
///
|
||||
/// **Note**: The maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit
|
||||
/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this
|
||||
/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now.
|
||||
#[cfg(feature = "_rp235x")]
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum CoreVoltage {
|
||||
/// 0.55V
|
||||
V0_55 = 0b00000,
|
||||
/// 0.60V
|
||||
V0_60 = 0b00001,
|
||||
/// 0.65V
|
||||
V0_65 = 0b00010,
|
||||
/// 0.70V
|
||||
V0_70 = 0b00011,
|
||||
/// 0.75V
|
||||
V0_75 = 0b00100,
|
||||
/// 0.80V
|
||||
V0_80 = 0b00101,
|
||||
/// 0.85V
|
||||
V0_85 = 0b00110,
|
||||
/// 0.90V
|
||||
V0_90 = 0b00111,
|
||||
/// 0.95V
|
||||
V0_95 = 0b01000,
|
||||
/// 1.00V
|
||||
V1_00 = 0b01001,
|
||||
/// 1.05V
|
||||
V1_05 = 0b01010,
|
||||
/// 1.10V - Default voltage level
|
||||
V1_10 = 0b01011,
|
||||
/// 1.15V
|
||||
V1_15 = 0b01100,
|
||||
/// 1.20V
|
||||
V1_20 = 0b01101,
|
||||
/// 1.25V
|
||||
V1_25 = 0b01110,
|
||||
/// 1.30V
|
||||
V1_30 = 0b01111,
|
||||
}
|
||||
|
||||
impl CoreVoltage {
|
||||
/// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
|
||||
/// Sets the BOD threshold to approximately 80% of the core voltage.
|
||||
fn recommended_bod(self) -> u8 {
|
||||
#[cfg(feature = "rp2040")]
|
||||
match self {
|
||||
CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V)
|
||||
CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V)
|
||||
@ -180,12 +240,32 @@ impl CoreVoltage {
|
||||
CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V)
|
||||
CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V)
|
||||
CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V)
|
||||
CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V)
|
||||
CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V), the default
|
||||
CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V)
|
||||
CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V)
|
||||
CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V)
|
||||
CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V)
|
||||
}
|
||||
#[cfg(feature = "_rp235x")]
|
||||
match self {
|
||||
CoreVoltage::V0_55 => 0b00001, // 0.516V (~94% of 0.55V)
|
||||
CoreVoltage::V0_60 => 0b00010, // 0.559V (~93% of 0.60V)
|
||||
CoreVoltage::V0_65 => 0b00011, // 0.602V (~93% of 0.65V)
|
||||
CoreVoltage::V0_70 => 0b00011, // 0.602V (~86% of 0.70V)
|
||||
CoreVoltage::V0_75 => 0b00100, // 0.645V (~86% of 0.75V)
|
||||
CoreVoltage::V0_80 => 0b00101, // 0.688V (~86% of 0.80V)
|
||||
CoreVoltage::V0_85 => 0b00110, // 0.731V (~86% of 0.85V)
|
||||
CoreVoltage::V0_90 => 0b00110, // 0.731V (~81% of 0.90V)
|
||||
CoreVoltage::V0_95 => 0b00111, // 0.774V (~81% of 0.95V)
|
||||
CoreVoltage::V1_00 => 0b01000, // 0.817V (~82% of 1.00V)
|
||||
CoreVoltage::V1_05 => 0b01000, // 0.817V (~78% of 1.05V)
|
||||
CoreVoltage::V1_10 => 0b01001, // 0.860V (~78% of 1.10V), the default
|
||||
CoreVoltage::V1_15 => 0b01001, // 0.860V (~75% of 1.15V)
|
||||
CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V)
|
||||
CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V)
|
||||
CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V)
|
||||
// all others: 0.946V (see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,12 +289,10 @@ pub struct ClockConfig {
|
||||
/// RTC clock configuration.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub rtc_clk: Option<RtcClkConfig>,
|
||||
/// Core voltage scaling (RP2040 only). Defaults to 1.10V.
|
||||
#[cfg(feature = "rp2040")]
|
||||
/// Core voltage scaling. Defaults to 1.10V.
|
||||
pub core_voltage: CoreVoltage,
|
||||
/// Voltage stabilization delay in microseconds.
|
||||
/// If not set, defaults will be used based on voltage level.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub voltage_stabilization_delay_us: Option<u32>,
|
||||
// See above re gpin handling being commented out
|
||||
// gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
|
||||
@ -250,9 +328,7 @@ impl Default for ClockConfig {
|
||||
adc_clk: None,
|
||||
#[cfg(feature = "rp2040")]
|
||||
rtc_clk: None,
|
||||
#[cfg(feature = "rp2040")]
|
||||
core_voltage: CoreVoltage::V1_10,
|
||||
#[cfg(feature = "rp2040")]
|
||||
voltage_stabilization_delay_us: None,
|
||||
// See above re gpin handling being commented out
|
||||
// gpin0: None,
|
||||
@ -323,9 +399,7 @@ impl ClockConfig {
|
||||
div_frac: 0,
|
||||
phase: 0,
|
||||
}),
|
||||
#[cfg(feature = "rp2040")]
|
||||
core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)
|
||||
#[cfg(feature = "rp2040")]
|
||||
voltage_stabilization_delay_us: None,
|
||||
// See above re gpin handling being commented out
|
||||
// gpin0: None,
|
||||
@ -368,9 +442,7 @@ impl ClockConfig {
|
||||
div_frac: 171,
|
||||
phase: 0,
|
||||
}),
|
||||
#[cfg(feature = "rp2040")]
|
||||
core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)
|
||||
#[cfg(feature = "rp2040")]
|
||||
voltage_stabilization_delay_us: None,
|
||||
// See above re gpin handling being commented out
|
||||
// gpin0: None,
|
||||
@ -391,29 +463,42 @@ impl ClockConfig {
|
||||
/// # Returns
|
||||
///
|
||||
/// A ClockConfig configured to achieve the requested system frequency using the
|
||||
/// the usual 12Mhz crystal, or panic if no valid parameters can be found.
|
||||
/// the usual 12Mhz crystal, or an error if no valid parameters can be found.
|
||||
///
|
||||
/// # Note on core voltage:
|
||||
///
|
||||
/// **For RP2040**:
|
||||
/// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are:
|
||||
/// - Up to 133MHz: V1_10 (default)
|
||||
/// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz
|
||||
/// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here.
|
||||
/// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub fn system_freq(hz: u32) -> Self {
|
||||
///
|
||||
/// **For RP235x**:
|
||||
/// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults.
|
||||
/// Using this function is experimental and may not work as expected or even damage the chip.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result containing either the configured ClockConfig or a ClockError.
|
||||
pub fn system_freq(hz: u32) -> Result<Self, ClockError> {
|
||||
// Start with the standard configuration from crystal()
|
||||
const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
|
||||
let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);
|
||||
|
||||
// No need to modify anything if target frequency is already 125MHz
|
||||
// (which is what crystal() configures by default)
|
||||
#[cfg(feature = "rp2040")]
|
||||
if hz == 125_000_000 {
|
||||
return config;
|
||||
return Ok(config);
|
||||
}
|
||||
#[cfg(feature = "_rp235x")]
|
||||
if hz == 150_000_000 {
|
||||
return Ok(config);
|
||||
}
|
||||
|
||||
// Find optimal PLL parameters for the requested frequency
|
||||
let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz)
|
||||
.unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock"));
|
||||
let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?;
|
||||
|
||||
// Replace the sys_pll configuration with our custom parameters
|
||||
if let Some(xosc) = &mut config.xosc {
|
||||
@ -429,8 +514,16 @@ impl ClockConfig {
|
||||
_ => CoreVoltage::V1_10, // Use default voltage (V1_10)
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "_rp235x")]
|
||||
{
|
||||
config.core_voltage = match hz {
|
||||
// There is no official support for running the chip on other core voltages and/or other clock speeds than the defaults.
|
||||
// So for now we have not way of knowing what the voltage should be. Change this if the manufacturer provides more information.
|
||||
_ => CoreVoltage::V1_10, // Use default voltage (V1_10)
|
||||
};
|
||||
}
|
||||
|
||||
config
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Configure with manual PLL settings for full control over system clock
|
||||
@ -525,6 +618,7 @@ impl ClockConfig {
|
||||
#[repr(u16)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum RoscRange {
|
||||
/// Low range.
|
||||
Low = pac::rosc::vals::FreqRange::LOW.0,
|
||||
@ -631,6 +725,7 @@ pub struct RefClkConfig {
|
||||
/// Reference clock source.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum RefClkSrc {
|
||||
/// XOSC.
|
||||
Xosc,
|
||||
@ -646,6 +741,7 @@ pub enum RefClkSrc {
|
||||
/// SYS clock source.
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum SysClkSrc {
|
||||
/// REF.
|
||||
Ref,
|
||||
@ -684,6 +780,7 @@ pub struct SysClkConfig {
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum UsbClkSrc {
|
||||
/// PLL USB.
|
||||
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||
@ -712,6 +809,7 @@ pub struct UsbClkConfig {
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AdcClkSrc {
|
||||
/// PLL USB.
|
||||
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||
@ -740,6 +838,7 @@ pub struct AdcClkConfig {
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub enum RtcClkSrc {
|
||||
/// PLL USB.
|
||||
@ -791,7 +890,6 @@ pub struct RtcClkConfig {
|
||||
/// // Find parameters for 133MHz system clock from 12MHz crystal
|
||||
/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();
|
||||
/// ```
|
||||
#[cfg(feature = "rp2040")]
|
||||
fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
|
||||
// Fixed reference divider for system PLL
|
||||
const PLL_SYS_REFDIV: u8 = 1;
|
||||
@ -925,18 +1023,31 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
};
|
||||
CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
|
||||
|
||||
// Set Core Voltage (RP2040 only), if we have config for it and we're not using the default
|
||||
#[cfg(feature = "rp2040")]
|
||||
// Set Core Voltage, if we have config for it and we're not using the default
|
||||
{
|
||||
let voltage = config.core_voltage;
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
let vreg = pac::VREG_AND_CHIP_RESET;
|
||||
#[cfg(feature = "_rp235x")]
|
||||
let vreg = pac::POWMAN;
|
||||
|
||||
let current_vsel = vreg.vreg().read().vsel();
|
||||
let target_vsel = voltage as u8;
|
||||
|
||||
// If the target voltage is different from the current one, we need to change it
|
||||
if target_vsel != current_vsel {
|
||||
// Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage
|
||||
// Set the voltage regulator to the target voltage
|
||||
#[cfg(feature = "rp2040")]
|
||||
vreg.vreg().modify(|w| w.set_vsel(target_vsel));
|
||||
#[cfg(feature = "_rp235x")]
|
||||
// For rp235x changes to the voltage regulator are protected by a password, see datasheet section 6.4 Power Management (POWMAN) Registers
|
||||
// The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register
|
||||
vreg.vreg().modify(|w| {
|
||||
w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password
|
||||
w.set_vsel(target_vsel);
|
||||
*w
|
||||
});
|
||||
|
||||
// Wait for the voltage to stabilize. Use the provided delay or default based on voltage
|
||||
let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
|
||||
@ -955,10 +1066,17 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
}
|
||||
|
||||
// Only now set the BOD level. At this point the voltage is considered stable.
|
||||
#[cfg(feature = "rp2040")]
|
||||
vreg.bod().write(|w| {
|
||||
w.set_vsel(voltage.recommended_bod());
|
||||
w.set_en(true); // Enable brownout detection
|
||||
});
|
||||
#[cfg(feature = "_rp235x")]
|
||||
vreg.bod().write(|w| {
|
||||
w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password
|
||||
w.set_vsel(voltage.recommended_bod());
|
||||
w.set_en(true); // Enable brownout detection
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -970,14 +1088,14 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
let pll_sys_freq = match config.sys_pll {
|
||||
Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
|
||||
Ok(freq) => freq,
|
||||
Err(e) => panic!("Failed to configure PLL_SYS: {}", e),
|
||||
Err(e) => panic!("Failed to configure PLL_SYS: {:?}", e),
|
||||
},
|
||||
None => 0,
|
||||
};
|
||||
let pll_usb_freq = match config.usb_pll {
|
||||
Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
|
||||
Ok(freq) => freq,
|
||||
Err(e) => panic!("Failed to configure PLL_USB: {}", e),
|
||||
Err(e) => panic!("Failed to configure PLL_USB: {:?}", e),
|
||||
},
|
||||
None => 0,
|
||||
};
|
||||
@ -1283,6 +1401,58 @@ pub fn clk_rtc_freq() -> u16 {
|
||||
CLOCKS.rtc.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// The core voltage of the chip.
|
||||
///
|
||||
/// Returns the current core voltage or an error if the voltage register
|
||||
/// contains an unknown value.
|
||||
pub fn core_voltage() -> Result<CoreVoltage, ClockError> {
|
||||
#[cfg(feature = "rp2040")]
|
||||
{
|
||||
let vreg = pac::VREG_AND_CHIP_RESET;
|
||||
let vsel = vreg.vreg().read().vsel();
|
||||
match vsel {
|
||||
0b0000 => Ok(CoreVoltage::V0_80),
|
||||
0b0110 => Ok(CoreVoltage::V0_85),
|
||||
0b0111 => Ok(CoreVoltage::V0_90),
|
||||
0b1000 => Ok(CoreVoltage::V0_95),
|
||||
0b1001 => Ok(CoreVoltage::V1_00),
|
||||
0b1010 => Ok(CoreVoltage::V1_05),
|
||||
0b1011 => Ok(CoreVoltage::V1_10),
|
||||
0b1100 => Ok(CoreVoltage::V1_15),
|
||||
0b1101 => Ok(CoreVoltage::V1_20),
|
||||
0b1110 => Ok(CoreVoltage::V1_25),
|
||||
0b1111 => Ok(CoreVoltage::V1_30),
|
||||
_ => Err(ClockError::UnexpectedCoreVoltageRead),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "_rp235x")]
|
||||
{
|
||||
let vreg = pac::POWMAN;
|
||||
let vsel = vreg.vreg().read().vsel();
|
||||
match vsel {
|
||||
0b00000 => Ok(CoreVoltage::V0_55),
|
||||
0b00001 => Ok(CoreVoltage::V0_60),
|
||||
0b00010 => Ok(CoreVoltage::V0_65),
|
||||
0b00011 => Ok(CoreVoltage::V0_70),
|
||||
0b00100 => Ok(CoreVoltage::V0_75),
|
||||
0b00101 => Ok(CoreVoltage::V0_80),
|
||||
0b00110 => Ok(CoreVoltage::V0_85),
|
||||
0b00111 => Ok(CoreVoltage::V0_90),
|
||||
0b01000 => Ok(CoreVoltage::V0_95),
|
||||
0b01001 => Ok(CoreVoltage::V1_00),
|
||||
0b01010 => Ok(CoreVoltage::V1_05),
|
||||
0b01011 => Ok(CoreVoltage::V1_10),
|
||||
0b01100 => Ok(CoreVoltage::V1_15),
|
||||
0b01101 => Ok(CoreVoltage::V1_20),
|
||||
0b01110 => Ok(CoreVoltage::V1_25),
|
||||
0b01111 => Ok(CoreVoltage::V1_30),
|
||||
_ => Err(ClockError::UnexpectedCoreVoltageRead),
|
||||
// see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
|
||||
let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256;
|
||||
pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
|
||||
@ -1295,7 +1465,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
|
||||
|
||||
/// PLL (Phase-Locked Loop) configuration
|
||||
#[inline(always)]
|
||||
fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, &'static str> {
|
||||
fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> {
|
||||
// Calculate reference frequency
|
||||
let ref_freq = input_freq / config.refdiv as u32;
|
||||
|
||||
@ -1366,7 +1536,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result
|
||||
timeout -= 1;
|
||||
if timeout == 0 {
|
||||
// PLL failed to lock, return 0 to indicate failure
|
||||
return Err("PLL failed to lock");
|
||||
return Err(ClockError::PllLockTimedOut);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1606,7 +1776,8 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
|
||||
pub struct RoscRng;
|
||||
|
||||
impl RoscRng {
|
||||
fn next_u8() -> u8 {
|
||||
/// Get a random u8
|
||||
pub fn next_u8() -> u8 {
|
||||
let random_reg = pac::ROSC.randombit();
|
||||
let mut acc = 0;
|
||||
for _ in 0..u8::BITS {
|
||||
@ -1615,26 +1786,60 @@ impl RoscRng {
|
||||
}
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl rand_core::RngCore for RoscRng {
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
Ok(self.fill_bytes(dest))
|
||||
/// Get a random u32
|
||||
pub fn next_u32(&mut self) -> u32 {
|
||||
rand_core_09::impls::next_u32_via_fill(self)
|
||||
}
|
||||
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
rand_core::impls::next_u32_via_fill(self)
|
||||
/// Get a random u64
|
||||
pub fn next_u64(&mut self) -> u64 {
|
||||
rand_core_09::impls::next_u64_via_fill(self)
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
rand_core::impls::next_u64_via_fill(self)
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
/// Fill a slice with random bytes
|
||||
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
dest.fill_with(Self::next_u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl rand_core_06::RngCore for RoscRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.fill_bytes(dest);
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl rand_core_06::CryptoRng for RoscRng {}
|
||||
|
||||
impl rand_core_09::RngCore for RoscRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.fill_bytes(dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl rand_core_09::CryptoRng for RoscRng {}
|
||||
|
||||
/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks
|
||||
/// and can only be exited through resets, dormant-wake GPIO interrupts,
|
||||
/// and RTC interrupts. If RTC is clocked from an internal clock source
|
||||
@ -1937,21 +2142,21 @@ mod tests {
|
||||
{
|
||||
// Test automatic voltage scaling based on frequency
|
||||
// Under 133 MHz should use default voltage (V1_10)
|
||||
let config = ClockConfig::system_freq(125_000_000);
|
||||
let config = ClockConfig::system_freq(125_000_000).unwrap();
|
||||
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
|
||||
|
||||
// 133-200 MHz should use V1_15
|
||||
let config = ClockConfig::system_freq(150_000_000);
|
||||
let config = ClockConfig::system_freq(150_000_000).unwrap();
|
||||
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
||||
let config = ClockConfig::system_freq(200_000_000);
|
||||
let config = ClockConfig::system_freq(200_000_000).unwrap();
|
||||
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
||||
|
||||
// Above 200 MHz should use V1_25
|
||||
let config = ClockConfig::system_freq(250_000_000);
|
||||
// Above 200 MHz should use V1_15
|
||||
let config = ClockConfig::system_freq(250_000_000).unwrap();
|
||||
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
||||
|
||||
// Below 125 MHz should use V1_10
|
||||
let config = ClockConfig::system_freq(100_000_000);
|
||||
let config = ClockConfig::system_freq(100_000_000).unwrap();
|
||||
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
|
||||
}
|
||||
}
|
||||
|
@ -932,6 +932,8 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
|
||||
}
|
||||
|
||||
/// Type-erased GPIO pin
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct AnyPin {
|
||||
pin_bank: u8,
|
||||
}
|
||||
|
@ -160,15 +160,18 @@ embassy_hal_internal::interrupt_mod!(
|
||||
/// ```rust,ignore
|
||||
/// use embassy_rp::{bind_interrupts, usb, peripherals};
|
||||
///
|
||||
/// bind_interrupts!(struct Irqs {
|
||||
/// USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;
|
||||
/// });
|
||||
/// bind_interrupts!(
|
||||
/// /// Binds the USB Interrupts.
|
||||
/// struct Irqs {
|
||||
/// USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident {
|
||||
($(#[$attr:meta])* $vis:vis struct $name:ident {
|
||||
$(
|
||||
$(#[cfg($cond_irq:meta)])?
|
||||
$irq:ident => $(
|
||||
@ -178,6 +181,7 @@ macro_rules! bind_interrupts {
|
||||
)*
|
||||
}) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$(#[$attr])*
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
|
@ -9,6 +9,10 @@ use crate::pio::{
|
||||
use crate::Peri;
|
||||
|
||||
/// This struct represents an i2s output driver program
|
||||
///
|
||||
/// The sample bit-depth is set through scratch register `Y`.
|
||||
/// `Y` has to be set to sample bit-depth - 2.
|
||||
/// (14 = 16bit, 22 = 24bit, 30 = 32bit)
|
||||
pub struct PioI2sOutProgram<'d, PIO: Instance> {
|
||||
prg: LoadedProgram<'d, PIO>,
|
||||
}
|
||||
@ -17,13 +21,13 @@ impl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {
|
||||
/// Load the program into the given pio
|
||||
pub fn new(common: &mut Common<'d, PIO>) -> Self {
|
||||
let prg = pio::pio_asm!(
|
||||
".side_set 2",
|
||||
" set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
|
||||
".side_set 2", // side 0bWB - W = Word Clock, B = Bit Clock
|
||||
" mov x, y side 0b01", // y stores sample depth - 2 (14 = 16bit, 22 = 24bit, 30 = 32bit)
|
||||
"left_data:",
|
||||
" out pins, 1 side 0b00",
|
||||
" jmp x-- left_data side 0b01",
|
||||
" out pins 1 side 0b10",
|
||||
" set x, 14 side 0b11",
|
||||
" mov x, y side 0b11",
|
||||
"right_data:",
|
||||
" out pins 1 side 0b10",
|
||||
" jmp x-- right_data side 0b11",
|
||||
@ -53,7 +57,6 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
|
||||
lr_clock_pin: Peri<'d, impl PioPin>,
|
||||
sample_rate: u32,
|
||||
bit_depth: u32,
|
||||
channels: u32,
|
||||
program: &PioI2sOutProgram<'d, P>,
|
||||
) -> Self {
|
||||
let data_pin = common.make_pio_pin(data_pin);
|
||||
@ -64,7 +67,7 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
|
||||
cfg.set_out_pins(&[&data_pin]);
|
||||
let clock_frequency = sample_rate * bit_depth * channels;
|
||||
let clock_frequency = sample_rate * bit_depth * 2;
|
||||
cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed();
|
||||
cfg.shift_out = ShiftConfig {
|
||||
threshold: 32,
|
||||
@ -78,6 +81,11 @@ impl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {
|
||||
sm.set_config(&cfg);
|
||||
sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]);
|
||||
|
||||
// Set the `y` register up to configure the sample depth
|
||||
// The SM counts down to 0 and uses one clock cycle to set up the counter,
|
||||
// which results in bit_depth - 2 as register value.
|
||||
unsafe { sm.set_y(bit_depth - 2) };
|
||||
|
||||
sm.set_enable(true);
|
||||
|
||||
Self { dma: dma.into(), sm }
|
||||
|
@ -464,6 +464,10 @@ impl<'d> Drop for Pwm<'d> {
|
||||
pac::PWM.ch(self.slice).csr().write_clear(|w| w.set_en(false));
|
||||
if let Some(pin) = &self.pin_a {
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(31));
|
||||
// Enable pin PULL-DOWN
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
w.set_pde(true);
|
||||
});
|
||||
}
|
||||
if let Some(pin) = &self.pin_b {
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(31));
|
||||
@ -472,6 +476,10 @@ impl<'d> Drop for Pwm<'d> {
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
w.set_ie(false);
|
||||
});
|
||||
// Enable pin PULL-DOWN
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
w.set_pde(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::{Peri, PeripheralType};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use rand_core::Error;
|
||||
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::peripherals::TRNG;
|
||||
@ -369,7 +368,7 @@ impl<'d, T: Instance> Trng<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> {
|
||||
impl<'d, T: Instance> rand_core_06::RngCore for Trng<'d, T> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.blocking_next_u32()
|
||||
}
|
||||
@ -379,16 +378,32 @@ impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> {
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest)
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
|
||||
self.blocking_fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core::CryptoRng for Trng<'d, T> {}
|
||||
impl<'d, T: Instance> rand_core_06::CryptoRng for Trng<'d, T> {}
|
||||
|
||||
impl<'d, T: Instance> rand_core_09::RngCore for Trng<'d, T> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.blocking_next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.blocking_next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core_09::CryptoRng for Trng<'d, T> {}
|
||||
|
||||
/// TRNG interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
|
@ -27,7 +27,7 @@ embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal" }
|
||||
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.17", optional = true }
|
||||
|
||||
cortex-m = "0.7.6"
|
||||
|
@ -19,16 +19,22 @@ flavors = [
|
||||
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
|
||||
{ regex_feature = "stm32f4[2367]..[ig]", target = "thumbv7em-none-eabi", features = ["low-power", "dual-bank"] },
|
||||
{ regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power", "single-bank"] },
|
||||
{ regex_feature = "stm32f7[67]..[ig]", target = "thumbv7em-none-eabi", features = ["dual-bank"] },
|
||||
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32g0...c", target = "thumbv6m-none-eabi", features = ["dual-bank"] },
|
||||
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32g4[78].*", target = "thumbv7em-none-eabi", features = ["low-power", "dual-bank"] },
|
||||
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
|
||||
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] },
|
||||
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
|
||||
{ regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32l4[pqrs].*", target = "thumbv7em-none-eabi", features = ["dual-bank"] },
|
||||
{ regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32l5...e", target = "thumbv8m.main-none-eabihf", features = ["low-power", "dual-bank"] },
|
||||
{ regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] },
|
||||
{ regex_feature = "stm32u0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" },
|
||||
@ -38,7 +44,7 @@ flavors = [
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["defmt", "unstable-pac", "exti", "time-driver-any", "time", "stm32h755zi-cm7"]
|
||||
features = ["defmt", "unstable-pac", "exti", "time-driver-any", "time", "stm32h755zi-cm7", "single-bank"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
@ -63,13 +69,15 @@ embedded-can = "0.4"
|
||||
embedded-storage = "0.3.1"
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
|
||||
rand-core-06 = { package = "rand_core", version = "0.6" }
|
||||
rand-core-09 = { package = "rand_core", version = "0.9" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
cortex-m-rt = ">=0.6.15,<0.8"
|
||||
cortex-m = "0.7.6"
|
||||
futures-util = { version = "0.3.30", default-features = false }
|
||||
rand_core = "0.6.3"
|
||||
sdio-host = "0.9.0"
|
||||
critical-section = "1.1"
|
||||
#stm32-metapac = { version = "16" }
|
||||
|
@ -371,8 +371,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
/// let mut adc_pin1 = p.PA1.into();
|
||||
/// let mut measurements = [0u16; 2];
|
||||
///
|
||||
/// adc.read_async(
|
||||
/// p.DMA1_CH2,
|
||||
/// adc.read(
|
||||
/// p.DMA1_CH2.reborrow(),
|
||||
/// [
|
||||
/// (&mut *adc_pin0, SampleTime::CYCLES160_5),
|
||||
/// (&mut *adc_pin1, SampleTime::CYCLES160_5),
|
||||
|
@ -272,8 +272,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
/// let mut adc_pin1 = p.PA1.degrade_adc();
|
||||
/// let mut measurements = [0u16; 2];
|
||||
///
|
||||
/// adc.read_async(
|
||||
/// p.DMA1_CH2,
|
||||
/// adc.read(
|
||||
/// p.DMA1_CH2.reborrow(),
|
||||
/// [
|
||||
/// (&mut *adc_pin0, SampleTime::CYCLES160_5),
|
||||
/// (&mut *adc_pin1, SampleTime::CYCLES160_5),
|
||||
|
@ -347,8 +347,8 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
/// let mut adc_pin2 = p.PA2.into();
|
||||
/// let mut measurements = [0u16; 2];
|
||||
///
|
||||
/// adc.read_async(
|
||||
/// p.DMA2_CH0,
|
||||
/// adc.read(
|
||||
/// p.DMA2_CH0.reborrow(),
|
||||
/// [
|
||||
/// (&mut *adc_pin0, SampleTime::CYCLES112),
|
||||
/// (&mut *adc_pin2, SampleTime::CYCLES112),
|
||||
|
@ -163,18 +163,22 @@ pub use crate::_generated::interrupt;
|
||||
/// ```rust,ignore
|
||||
/// use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||
///
|
||||
/// bind_interrupts!(struct Irqs {
|
||||
/// I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
/// I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>,
|
||||
/// i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||
/// });
|
||||
/// bind_interrupts!(
|
||||
/// /// Binds the I2C interrupts.
|
||||
/// struct Irqs {
|
||||
/// I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
/// I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>,
|
||||
/// i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>;
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
|
||||
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
||||
#[macro_export]
|
||||
macro_rules! bind_interrupts {
|
||||
($vis:vis struct $name:ident {
|
||||
($(#[$outer:meta])* $vis:vis struct $name:ident {
|
||||
$(
|
||||
$(#[$inner:meta])*
|
||||
$(#[cfg($cond_irq:meta)])?
|
||||
$irq:ident => $(
|
||||
$(#[cfg($cond_handler:meta)])?
|
||||
@ -183,12 +187,14 @@ macro_rules! bind_interrupts {
|
||||
)*
|
||||
}) => {
|
||||
#[derive(Copy, Clone)]
|
||||
$(#[$outer])*
|
||||
$vis struct $name;
|
||||
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
$(#[cfg($cond_irq)])?
|
||||
$(#[$inner])*
|
||||
unsafe extern "C" fn $irq() {
|
||||
$(
|
||||
$(#[cfg($cond_handler)])?
|
||||
@ -600,17 +606,7 @@ fn init_hw(config: Config) -> Peripherals {
|
||||
#[cfg(feature = "exti")]
|
||||
exti::init(cs);
|
||||
|
||||
rcc::init(config.rcc);
|
||||
|
||||
// must be after rcc init
|
||||
#[cfg(feature = "_time-driver")]
|
||||
time_driver::init(cs);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
{
|
||||
crate::rcc::REFCOUNT_STOP2 = 0;
|
||||
crate::rcc::REFCOUNT_STOP1 = 0;
|
||||
}
|
||||
rcc::init_rcc(cs, config.rcc);
|
||||
}
|
||||
|
||||
p
|
||||
|
@ -371,3 +371,32 @@ pub fn enable_and_reset<T: RccPeripheral>() {
|
||||
pub fn disable<T: RccPeripheral>() {
|
||||
T::RCC_INFO.disable();
|
||||
}
|
||||
|
||||
/// Re-initialize the `embassy-stm32` clock configuration with the provided configuration.
|
||||
///
|
||||
/// This is useful when you need to alter the CPU clock after configuring peripherals.
|
||||
/// For instance, configure an external clock via spi or i2c.
|
||||
///
|
||||
/// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc).
|
||||
///
|
||||
/// This should only be called after `init`.
|
||||
#[cfg(not(feature = "_dual-core"))]
|
||||
pub fn reinit(config: Config) {
|
||||
critical_section::with(|cs| init_rcc(cs, config))
|
||||
}
|
||||
|
||||
pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
|
||||
unsafe {
|
||||
init(config);
|
||||
|
||||
// must be after rcc init
|
||||
#[cfg(feature = "_time-driver")]
|
||||
crate::time_driver::init(_cs);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
{
|
||||
REFCOUNT_STOP2 = 0;
|
||||
REFCOUNT_STOP1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::PeripheralType;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, pac, peripherals, rcc, Peri};
|
||||
@ -184,10 +183,9 @@ impl<'d, T: Instance> Rng<'d, T> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RngCore for Rng<'d, T> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
/// Get a random u32
|
||||
pub fn next_u32(&mut self) -> u32 {
|
||||
loop {
|
||||
let sr = T::regs().sr().read();
|
||||
if sr.seis() | sr.ceis() {
|
||||
@ -198,13 +196,15 @@ impl<'d, T: Instance> RngCore for Rng<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
/// Get a random u64
|
||||
pub fn next_u64(&mut self) -> u64 {
|
||||
let mut rand = self.next_u32() as u64;
|
||||
rand |= (self.next_u32() as u64) << 32;
|
||||
rand
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
/// Fill a slice with random bytes
|
||||
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
for chunk in dest.chunks_mut(4) {
|
||||
let rand = self.next_u32();
|
||||
for (slot, num) in chunk.iter_mut().zip(rand.to_ne_bytes().iter()) {
|
||||
@ -212,14 +212,53 @@ impl<'d, T: Instance> RngCore for Rng<'d, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
impl<'d, T: Instance> Drop for Rng<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
T::regs().cr().modify(|reg| {
|
||||
reg.set_rngen(false);
|
||||
});
|
||||
rcc::disable::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core_06::RngCore for Rng<'d, T> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.fill_bytes(dest);
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CryptoRng for Rng<'d, T> {}
|
||||
impl<'d, T: Instance> rand_core_06::CryptoRng for Rng<'d, T> {}
|
||||
|
||||
impl<'d, T: Instance> rand_core_09::RngCore for Rng<'d, T> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.next_u32()
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.next_u64()
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.fill_bytes(dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> rand_core_09::CryptoRng for Rng<'d, T> {}
|
||||
|
||||
trait SealedInstance {
|
||||
fn regs() -> pac::rng::Rng;
|
||||
|
@ -24,7 +24,7 @@ std = []
|
||||
turbowakers = []
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
futures-sink = { version = "0.3", default-features = false, features = [] }
|
||||
|
@ -420,7 +420,7 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"]
|
||||
embassy-time-driver = { version = "0.2", path = "../embassy-time-driver" }
|
||||
embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true}
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
|
||||
|
@ -26,7 +26,7 @@ flavors = [
|
||||
features = ["defmt", "cortex-m", "dfu"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3.5", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.17", optional = true }
|
||||
|
||||
bitflags = "2.4.1"
|
||||
|
@ -2,7 +2,7 @@ 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 embassy_usb::{Builder, FunctionBuilder, Handler};
|
||||
use embedded_storage::nor_flash::NorFlash;
|
||||
|
||||
use crate::consts::{
|
||||
@ -130,8 +130,14 @@ pub fn usb_dfu<'d, D: Driver<'d>, MARK: DfuMarker, RST: Reset>(
|
||||
builder: &mut Builder<'d, D>,
|
||||
handler: &'d mut Control<MARK, RST>,
|
||||
timeout: Duration,
|
||||
func_modifier: impl Fn(&mut FunctionBuilder<'_, 'd, D>),
|
||||
) {
|
||||
let mut func = builder.function(0x00, 0x00, 0x00);
|
||||
|
||||
// Here we give users the opportunity to add their own function level MSOS headers for instance.
|
||||
// This is useful when DFU functionality is part of a composite USB device.
|
||||
func_modifier(&mut func);
|
||||
|
||||
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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError};
|
||||
use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType};
|
||||
use embassy_usb::driver::Driver;
|
||||
use embassy_usb::{Builder, Handler};
|
||||
use embassy_usb::{Builder, FunctionBuilder, Handler};
|
||||
use embedded_storage::nor_flash::{NorFlash, NorFlashErrorKind};
|
||||
|
||||
use crate::consts::{
|
||||
@ -186,8 +186,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha
|
||||
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>,
|
||||
func_modifier: impl Fn(&mut FunctionBuilder<'_, 'd, D>),
|
||||
) {
|
||||
let mut func = builder.function(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU);
|
||||
|
||||
// Here we give users the opportunity to add their own function level MSOS headers for instance.
|
||||
// This is useful when DFU functionality is part of a composite USB device.
|
||||
func_modifier(&mut func);
|
||||
|
||||
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(
|
||||
|
@ -19,5 +19,5 @@ target = "thumbv7em-none-eabi"
|
||||
features = ["defmt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
embedded-io-async = "0.6.1"
|
||||
defmt = { version = "1", optional = true }
|
||||
|
@ -21,5 +21,5 @@ critical-section = "1.1"
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
@ -51,10 +51,10 @@ embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
||||
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
|
||||
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
embedded-io-async = "0.6.1"
|
||||
defmt = { version = "1", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
heapless = "0.8"
|
||||
embedded-io-async = "0.6.1"
|
||||
|
||||
# for HID
|
||||
usbd-hid = { version = "0.8.1", optional = true }
|
||||
|
@ -13,8 +13,8 @@ embassy-boot = { version = "0.4.0", path = "../../../../embassy-boot", features
|
||||
embassy-boot-nrf = { version = "0.4.0", path = "../../../../embassy-boot-nrf", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -12,9 +12,9 @@ embassy-rp = { version = "0.4.0", path = "../../../../embassy-rp", features = ["
|
||||
embassy-boot-rp = { version = "0.5.0", path = "../../../../embassy-boot-rp", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"], optional = true }
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"], optional = true }
|
||||
panic-reset = { version = "0.1.1", optional = true }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
embedded-storage = "0.3.1"
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
embedded-storage = "0.3.1"
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -14,8 +14,8 @@ embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded
|
||||
embassy-usb = { version = "0.4.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 }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -13,7 +13,7 @@ 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::{msos, Builder};
|
||||
use embassy_usb_dfu::consts::DfuAttributes;
|
||||
use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate};
|
||||
use panic_reset as _;
|
||||
@ -22,6 +22,11 @@ bind_interrupts!(struct Irqs {
|
||||
USB_LP => usb::InterruptHandler<peripherals::USB>;
|
||||
});
|
||||
|
||||
// This is a randomly generated GUID to allow clients on Windows to find your device.
|
||||
//
|
||||
// N.B. update to a custom GUID for your own device!
|
||||
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"];
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
@ -54,7 +59,28 @@ async fn main(_spawner: Spawner) {
|
||||
&mut control_buf,
|
||||
);
|
||||
|
||||
usb_dfu(&mut builder, &mut state, Duration::from_millis(2500));
|
||||
// We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.
|
||||
// Otherwise users need to do this manually using a tool like Zadig.
|
||||
//
|
||||
// It seems these always need to be at added at the device level for this to work and for
|
||||
// composite devices they also need to be added on the function level (as shown later).
|
||||
//
|
||||
builder.msos_descriptor(msos::windows_version::WIN8_1, 2);
|
||||
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
|
||||
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
|
||||
"DeviceInterfaceGUIDs",
|
||||
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
|
||||
));
|
||||
|
||||
usb_dfu(&mut builder, &mut state, Duration::from_millis(2500), |func| {
|
||||
// You likely don't have to add these function level headers if your USB device is not composite
|
||||
// (i.e. if your device does not expose another interface in addition to DFU)
|
||||
func.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
|
||||
func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
|
||||
"DeviceInterfaceGUIDs",
|
||||
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
|
||||
));
|
||||
});
|
||||
|
||||
let mut dev = builder.build();
|
||||
dev.run().await
|
||||
|
@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", feature
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
embassy-embedded-hal = { version = "0.3.0", path = "../../../../embassy-embedded-hal" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
panic-reset = { version = "0.1.1" }
|
||||
embedded-hal = { version = "0.2.6" }
|
||||
|
||||
|
@ -6,8 +6,8 @@ description = "Bootloader for nRF chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
|
||||
embassy-nrf = { path = "../../../../embassy-nrf", features = [] }
|
||||
embassy-boot-nrf = { path = "../../../../embassy-boot-nrf" }
|
||||
|
@ -6,8 +6,8 @@ description = "Example bootloader for RP2040 chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
|
||||
embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] }
|
||||
embassy-boot-rp = { path = "../../../../embassy-boot-rp" }
|
||||
|
@ -6,8 +6,8 @@ description = "Example bootloader for dual-bank flash STM32 chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
|
||||
embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
|
||||
embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
|
||||
|
@ -6,8 +6,8 @@ description = "Example bootloader for STM32 chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
|
||||
embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
|
||||
embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
|
||||
|
@ -6,8 +6,8 @@ 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 }
|
||||
defmt = { version = "1.0.1", optional = true }
|
||||
defmt-rtt = { version = "1.0.0", optional = true }
|
||||
|
||||
embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
|
||||
embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
|
||||
|
@ -20,7 +20,9 @@ bind_interrupts!(struct Irqs {
|
||||
USB_LP => usb::InterruptHandler<peripherals::USB>;
|
||||
});
|
||||
|
||||
// This is a randomly generated GUID to allow clients on Windows to find our device
|
||||
// This is a randomly generated GUID to allow clients on Windows to find your device.
|
||||
//
|
||||
// N.B. update to a custom GUID for your own device!
|
||||
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"];
|
||||
|
||||
#[entry]
|
||||
@ -68,7 +70,8 @@ fn main() -> ! {
|
||||
// We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.
|
||||
// Otherwise users need to do this manually using a tool like Zadig.
|
||||
//
|
||||
// It seems it is important for the DFU class that these headers be on the Device level.
|
||||
// It seems these always need to be at added at the device level for this to work and for
|
||||
// composite devices they also need to be added on the function level (as shown later).
|
||||
//
|
||||
builder.msos_descriptor(msos::windows_version::WIN8_1, 2);
|
||||
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
|
||||
@ -77,7 +80,15 @@ fn main() -> ! {
|
||||
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
|
||||
));
|
||||
|
||||
usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state);
|
||||
usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state, |func| {
|
||||
// You likely don't have to add these function level headers if your USB device is not composite
|
||||
// (i.e. if your device does not expose another interface in addition to DFU)
|
||||
func.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
|
||||
func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
|
||||
"DeviceInterfaceGUIDs",
|
||||
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
|
||||
));
|
||||
});
|
||||
|
||||
let mut dev = builder.build();
|
||||
embassy_futures::block_on(dev.run());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user