Rename embassy-hal-common to embassy-hal-internal, document it's for internal use only. (#1700)
This commit is contained in:
committed by
GitHub
parent
0ced8400d0
commit
036e6ae30c
29
embassy-hal-internal/Cargo.toml
Normal file
29
embassy-hal-internal/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "embassy-hal-internal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
|
||||
# Define the number of NVIC priority bits.
|
||||
prio-bits-0 = []
|
||||
prio-bits-1 = []
|
||||
prio-bits-2 = []
|
||||
prio-bits-3 = []
|
||||
prio-bits-4 = []
|
||||
prio-bits-5 = []
|
||||
prio-bits-6 = []
|
||||
prio-bits-7 = []
|
||||
prio-bits-8 = []
|
||||
|
||||
cortex-m = ["dep:cortex-m", "dep:critical-section"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
num-traits = { version = "0.2.14", default-features = false }
|
||||
|
||||
cortex-m = { version = "0.7.6", optional = true }
|
||||
critical-section = { version = "1", optional = true }
|
16
embassy-hal-internal/README.md
Normal file
16
embassy-hal-internal/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# embassy-macros
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport
|
||||
everything you need to use them effectively.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
29
embassy-hal-internal/build.rs
Normal file
29
embassy-hal-internal/build.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
|
||||
if target.starts_with("thumbv6m-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv6m");
|
||||
} else if target.starts_with("thumbv7m-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv7m");
|
||||
} else if target.starts_with("thumbv7em-") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv7m");
|
||||
println!("cargo:rustc-cfg=armv7em"); // (not currently used)
|
||||
} else if target.starts_with("thumbv8m.base") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv8m");
|
||||
println!("cargo:rustc-cfg=armv8m_base");
|
||||
} else if target.starts_with("thumbv8m.main") {
|
||||
println!("cargo:rustc-cfg=cortex_m");
|
||||
println!("cargo:rustc-cfg=armv8m");
|
||||
println!("cargo:rustc-cfg=armv8m_main");
|
||||
}
|
||||
|
||||
if target.ends_with("-eabihf") {
|
||||
println!("cargo:rustc-cfg=has_fpu");
|
||||
}
|
||||
}
|
556
embassy-hal-internal/src/atomic_ring_buffer.rs
Normal file
556
embassy-hal-internal/src/atomic_ring_buffer.rs
Normal file
@ -0,0 +1,556 @@
|
||||
use core::slice;
|
||||
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
|
||||
|
||||
/// Atomic reusable ringbuffer
|
||||
///
|
||||
/// This ringbuffer implementation is designed to be stored in a `static`,
|
||||
/// therefore all methods take `&self` and not `&mut self`.
|
||||
///
|
||||
/// It is "reusable": when created it has no backing buffer, you can give it
|
||||
/// one with `init` and take it back with `deinit`, and init it again in the
|
||||
/// future if needed. This is very non-idiomatic, but helps a lot when storing
|
||||
/// it in a `static`.
|
||||
///
|
||||
/// One concurrent writer and one concurrent reader are supported, even at
|
||||
/// different execution priorities (like main and irq).
|
||||
pub struct RingBuffer {
|
||||
pub buf: AtomicPtr<u8>,
|
||||
pub len: AtomicUsize,
|
||||
|
||||
// start and end wrap at len*2, not at len.
|
||||
// This allows distinguishing "full" and "empty".
|
||||
// full is when start+len == end (modulo len*2)
|
||||
// empty is when start == end
|
||||
//
|
||||
// This avoids having to consider the ringbuffer "full" at len-1 instead of len.
|
||||
// The usual solution is adding a "full" flag, but that can't be made atomic
|
||||
pub start: AtomicUsize,
|
||||
pub end: AtomicUsize,
|
||||
}
|
||||
|
||||
pub struct Reader<'a>(&'a RingBuffer);
|
||||
pub struct Writer<'a>(&'a RingBuffer);
|
||||
|
||||
impl RingBuffer {
|
||||
/// Create a new empty ringbuffer.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
buf: AtomicPtr::new(core::ptr::null_mut()),
|
||||
len: AtomicUsize::new(0),
|
||||
start: AtomicUsize::new(0),
|
||||
end: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the ring buffer with a buffer.
|
||||
///
|
||||
/// # Safety
|
||||
/// - The buffer (`buf .. buf+len`) must be valid memory until `deinit` is called.
|
||||
/// - Must not be called concurrently with any other methods.
|
||||
pub unsafe fn init(&self, buf: *mut u8, len: usize) {
|
||||
// Ordering: it's OK to use `Relaxed` because this is not called
|
||||
// concurrently with other methods.
|
||||
self.buf.store(buf, Ordering::Relaxed);
|
||||
self.len.store(len, Ordering::Relaxed);
|
||||
self.start.store(0, Ordering::Relaxed);
|
||||
self.end.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Deinitialize the ringbuffer.
|
||||
///
|
||||
/// After calling this, the ringbuffer becomes empty, as if it was
|
||||
/// just created with `new()`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - Must not be called concurrently with any other methods.
|
||||
pub unsafe fn deinit(&self) {
|
||||
// Ordering: it's OK to use `Relaxed` because this is not called
|
||||
// concurrently with other methods.
|
||||
self.len.store(0, Ordering::Relaxed);
|
||||
self.start.store(0, Ordering::Relaxed);
|
||||
self.end.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Create a reader.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only one reader can exist at a time.
|
||||
pub unsafe fn reader(&self) -> Reader<'_> {
|
||||
Reader(self)
|
||||
}
|
||||
|
||||
/// Create a writer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only one writer can exist at a time.
|
||||
pub unsafe fn writer(&self) -> Writer<'_> {
|
||||
Writer(self)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.len.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
let len = self.len.load(Ordering::Relaxed);
|
||||
let start = self.start.load(Ordering::Relaxed);
|
||||
let end = self.end.load(Ordering::Relaxed);
|
||||
|
||||
self.wrap(start + len) == end
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let start = self.start.load(Ordering::Relaxed);
|
||||
let end = self.end.load(Ordering::Relaxed);
|
||||
|
||||
start == end
|
||||
}
|
||||
|
||||
fn wrap(&self, mut n: usize) -> usize {
|
||||
let len = self.len.load(Ordering::Relaxed);
|
||||
|
||||
if n >= len * 2 {
|
||||
n -= len * 2
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Writer<'a> {
|
||||
/// Push data into the buffer in-place.
|
||||
///
|
||||
/// The closure `f` is called with a free part of the buffer, it must write
|
||||
/// some data to it and return the amount of bytes written.
|
||||
pub fn push(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize {
|
||||
let (p, n) = self.push_buf();
|
||||
let buf = unsafe { slice::from_raw_parts_mut(p, n) };
|
||||
let n = f(buf);
|
||||
self.push_done(n);
|
||||
n
|
||||
}
|
||||
|
||||
/// Push one data byte.
|
||||
///
|
||||
/// Returns true if pushed successfully.
|
||||
pub fn push_one(&mut self, val: u8) -> bool {
|
||||
let n = self.push(|f| match f {
|
||||
[] => 0,
|
||||
[x, ..] => {
|
||||
*x = val;
|
||||
1
|
||||
}
|
||||
});
|
||||
n != 0
|
||||
}
|
||||
|
||||
/// Get a buffer where data can be pushed to.
|
||||
///
|
||||
/// Equivalent to [`Self::push_buf`] but returns a slice.
|
||||
pub fn push_slice(&mut self) -> &mut [u8] {
|
||||
let (data, len) = self.push_buf();
|
||||
unsafe { slice::from_raw_parts_mut(data, len) }
|
||||
}
|
||||
|
||||
/// Get up to two buffers where data can be pushed to.
|
||||
///
|
||||
/// Equivalent to [`Self::push_bufs`] but returns slices.
|
||||
pub fn push_slices(&mut self) -> [&mut [u8]; 2] {
|
||||
let [(d0, l0), (d1, l1)] = self.push_bufs();
|
||||
unsafe { [slice::from_raw_parts_mut(d0, l0), slice::from_raw_parts_mut(d1, l1)] }
|
||||
}
|
||||
|
||||
/// Get a buffer where data can be pushed to.
|
||||
///
|
||||
/// Write data to the start of the buffer, then call `push_done` with
|
||||
/// however many bytes you've pushed.
|
||||
///
|
||||
/// The buffer is suitable to DMA to.
|
||||
///
|
||||
/// If the ringbuf is full, size=0 will be returned.
|
||||
///
|
||||
/// The buffer stays valid as long as no other `Writer` method is called
|
||||
/// and `init`/`deinit` aren't called on the ringbuf.
|
||||
pub fn push_buf(&mut self) -> (*mut u8, usize) {
|
||||
// Ordering: popping writes `start` last, so we read `start` first.
|
||||
// Read it with Acquire ordering, so that the next accesses can't be reordered up past it.
|
||||
let mut start = self.0.start.load(Ordering::Acquire);
|
||||
let buf = self.0.buf.load(Ordering::Relaxed);
|
||||
let len = self.0.len.load(Ordering::Relaxed);
|
||||
let mut end = self.0.end.load(Ordering::Relaxed);
|
||||
|
||||
let empty = start == end;
|
||||
|
||||
if start >= len {
|
||||
start -= len
|
||||
}
|
||||
if end >= len {
|
||||
end -= len
|
||||
}
|
||||
|
||||
if start == end && !empty {
|
||||
// full
|
||||
return (buf, 0);
|
||||
}
|
||||
let n = if start > end { start - end } else { len - end };
|
||||
|
||||
trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n);
|
||||
(unsafe { buf.add(end) }, n)
|
||||
}
|
||||
|
||||
/// Get up to two buffers where data can be pushed to.
|
||||
///
|
||||
/// Write data starting at the beginning of the first buffer, then call
|
||||
/// `push_done` with however many bytes you've pushed.
|
||||
///
|
||||
/// The buffers are suitable to DMA to.
|
||||
///
|
||||
/// If the ringbuf is full, both buffers will be zero length.
|
||||
/// If there is only area available, the second buffer will be zero length.
|
||||
///
|
||||
/// The buffer stays valid as long as no other `Writer` method is called
|
||||
/// and `init`/`deinit` aren't called on the ringbuf.
|
||||
pub fn push_bufs(&mut self) -> [(*mut u8, usize); 2] {
|
||||
// Ordering: as per push_buf()
|
||||
let mut start = self.0.start.load(Ordering::Acquire);
|
||||
let buf = self.0.buf.load(Ordering::Relaxed);
|
||||
let len = self.0.len.load(Ordering::Relaxed);
|
||||
let mut end = self.0.end.load(Ordering::Relaxed);
|
||||
|
||||
let empty = start == end;
|
||||
|
||||
if start >= len {
|
||||
start -= len
|
||||
}
|
||||
if end >= len {
|
||||
end -= len
|
||||
}
|
||||
|
||||
if start == end && !empty {
|
||||
// full
|
||||
return [(buf, 0), (buf, 0)];
|
||||
}
|
||||
let n0 = if start > end { start - end } else { len - end };
|
||||
let n1 = if start <= end { start } else { 0 };
|
||||
|
||||
trace!(" ringbuf: push_bufs [{:?}..{:?}, {:?}..{:?}]", end, end + n0, 0, n1);
|
||||
[(unsafe { buf.add(end) }, n0), (buf, n1)]
|
||||
}
|
||||
|
||||
pub fn push_done(&mut self, n: usize) {
|
||||
trace!(" ringbuf: push {:?}", n);
|
||||
let end = self.0.end.load(Ordering::Relaxed);
|
||||
|
||||
// Ordering: write `end` last, with Release ordering.
|
||||
// The ordering ensures no preceding memory accesses (such as writing
|
||||
// the actual data in the buffer) can be reordered down past it, which
|
||||
// will guarantee the reader sees them after reading from `end`.
|
||||
self.0.end.store(self.0.wrap(end + n), Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
/// Pop data from the buffer in-place.
|
||||
///
|
||||
/// The closure `f` is called with the next data, it must process
|
||||
/// some data from it and return the amount of bytes processed.
|
||||
pub fn pop(&mut self, f: impl FnOnce(&[u8]) -> usize) -> usize {
|
||||
let (p, n) = self.pop_buf();
|
||||
let buf = unsafe { slice::from_raw_parts(p, n) };
|
||||
let n = f(buf);
|
||||
self.pop_done(n);
|
||||
n
|
||||
}
|
||||
|
||||
/// Pop one data byte.
|
||||
///
|
||||
/// Returns true if popped successfully.
|
||||
pub fn pop_one(&mut self) -> Option<u8> {
|
||||
let mut res = None;
|
||||
self.pop(|f| match f {
|
||||
&[] => 0,
|
||||
&[x, ..] => {
|
||||
res = Some(x);
|
||||
1
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
/// Get a buffer where data can be popped from.
|
||||
///
|
||||
/// Equivalent to [`Self::pop_buf`] but returns a slice.
|
||||
pub fn pop_slice(&mut self) -> &mut [u8] {
|
||||
let (data, len) = self.pop_buf();
|
||||
unsafe { slice::from_raw_parts_mut(data, len) }
|
||||
}
|
||||
|
||||
/// Get a buffer where data can be popped from.
|
||||
///
|
||||
/// Read data from the start of the buffer, then call `pop_done` with
|
||||
/// however many bytes you've processed.
|
||||
///
|
||||
/// The buffer is suitable to DMA from.
|
||||
///
|
||||
/// If the ringbuf is empty, size=0 will be returned.
|
||||
///
|
||||
/// The buffer stays valid as long as no other `Reader` method is called
|
||||
/// and `init`/`deinit` aren't called on the ringbuf.
|
||||
pub fn pop_buf(&mut self) -> (*mut u8, usize) {
|
||||
// Ordering: pushing writes `end` last, so we read `end` first.
|
||||
// Read it with Acquire ordering, so that the next accesses can't be reordered up past it.
|
||||
// This is needed to guarantee we "see" the data written by the writer.
|
||||
let mut end = self.0.end.load(Ordering::Acquire);
|
||||
let buf = self.0.buf.load(Ordering::Relaxed);
|
||||
let len = self.0.len.load(Ordering::Relaxed);
|
||||
let mut start = self.0.start.load(Ordering::Relaxed);
|
||||
|
||||
if start == end {
|
||||
return (buf, 0);
|
||||
}
|
||||
|
||||
if start >= len {
|
||||
start -= len
|
||||
}
|
||||
if end >= len {
|
||||
end -= len
|
||||
}
|
||||
|
||||
let n = if end > start { end - start } else { len - start };
|
||||
|
||||
trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n);
|
||||
(unsafe { buf.add(start) }, n)
|
||||
}
|
||||
|
||||
pub fn pop_done(&mut self, n: usize) {
|
||||
trace!(" ringbuf: pop {:?}", n);
|
||||
|
||||
let start = self.0.start.load(Ordering::Relaxed);
|
||||
|
||||
// Ordering: write `start` last, with Release ordering.
|
||||
// The ordering ensures no preceding memory accesses (such as reading
|
||||
// the actual data) can be reordered down past it. This is necessary
|
||||
// because writing to `start` is effectively freeing the read part of the
|
||||
// buffer, which "gives permission" to the writer to write to it again.
|
||||
// Therefore, all buffer accesses must be completed before this.
|
||||
self.0.start.store(self.0.wrap(start + n), Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn push_pop() {
|
||||
let mut b = [0; 4];
|
||||
let rb = RingBuffer::new();
|
||||
unsafe {
|
||||
rb.init(b.as_mut_ptr(), 4);
|
||||
|
||||
assert_eq!(rb.is_empty(), true);
|
||||
assert_eq!(rb.is_full(), false);
|
||||
|
||||
rb.writer().push(|buf| {
|
||||
assert_eq!(4, buf.len());
|
||||
buf[0] = 1;
|
||||
buf[1] = 2;
|
||||
buf[2] = 3;
|
||||
buf[3] = 4;
|
||||
4
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), false);
|
||||
assert_eq!(rb.is_full(), true);
|
||||
|
||||
rb.writer().push(|buf| {
|
||||
// If it's full, we can push 0 bytes.
|
||||
assert_eq!(0, buf.len());
|
||||
0
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), false);
|
||||
assert_eq!(rb.is_full(), true);
|
||||
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(4, buf.len());
|
||||
assert_eq!(1, buf[0]);
|
||||
1
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), false);
|
||||
assert_eq!(rb.is_full(), false);
|
||||
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(3, buf.len());
|
||||
0
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), false);
|
||||
assert_eq!(rb.is_full(), false);
|
||||
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(3, buf.len());
|
||||
assert_eq!(2, buf[0]);
|
||||
assert_eq!(3, buf[1]);
|
||||
2
|
||||
});
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(1, buf.len());
|
||||
assert_eq!(4, buf[0]);
|
||||
1
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), true);
|
||||
assert_eq!(rb.is_full(), false);
|
||||
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(0, buf.len());
|
||||
0
|
||||
});
|
||||
|
||||
rb.writer().push(|buf| {
|
||||
assert_eq!(4, buf.len());
|
||||
buf[0] = 10;
|
||||
1
|
||||
});
|
||||
|
||||
rb.writer().push(|buf| {
|
||||
assert_eq!(3, buf.len());
|
||||
buf[0] = 11;
|
||||
buf[1] = 12;
|
||||
2
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), false);
|
||||
assert_eq!(rb.is_full(), false);
|
||||
|
||||
rb.writer().push(|buf| {
|
||||
assert_eq!(1, buf.len());
|
||||
buf[0] = 13;
|
||||
1
|
||||
});
|
||||
|
||||
assert_eq!(rb.is_empty(), false);
|
||||
assert_eq!(rb.is_full(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_len() {
|
||||
let rb = RingBuffer::new();
|
||||
unsafe {
|
||||
assert_eq!(rb.is_empty(), true);
|
||||
assert_eq!(rb.is_full(), true);
|
||||
|
||||
rb.writer().push(|buf| {
|
||||
assert_eq!(0, buf.len());
|
||||
0
|
||||
});
|
||||
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(0, buf.len());
|
||||
0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_slices() {
|
||||
let mut b = [0; 4];
|
||||
let rb = RingBuffer::new();
|
||||
unsafe {
|
||||
rb.init(b.as_mut_ptr(), 4);
|
||||
|
||||
/* push 3 -> [1 2 3 x] */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(4, ps[0].len());
|
||||
assert_eq!(0, ps[1].len());
|
||||
ps[0][0] = 1;
|
||||
ps[0][1] = 2;
|
||||
ps[0][2] = 3;
|
||||
w.push_done(3);
|
||||
drop(w);
|
||||
|
||||
/* pop 2 -> [x x 3 x] */
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(3, buf.len());
|
||||
assert_eq!(1, buf[0]);
|
||||
assert_eq!(2, buf[1]);
|
||||
assert_eq!(3, buf[2]);
|
||||
2
|
||||
});
|
||||
|
||||
/* push 3 -> [5 6 3 4] */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(1, ps[0].len());
|
||||
assert_eq!(2, ps[1].len());
|
||||
ps[0][0] = 4;
|
||||
ps[1][0] = 5;
|
||||
ps[1][1] = 6;
|
||||
w.push_done(3);
|
||||
drop(w);
|
||||
|
||||
/* buf is now full */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(0, ps[0].len());
|
||||
assert_eq!(0, ps[1].len());
|
||||
|
||||
/* pop 2 -> [5 6 x x] */
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(2, buf.len());
|
||||
assert_eq!(3, buf[0]);
|
||||
assert_eq!(4, buf[1]);
|
||||
2
|
||||
});
|
||||
|
||||
/* should now have one push slice again */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(2, ps[0].len());
|
||||
assert_eq!(0, ps[1].len());
|
||||
drop(w);
|
||||
|
||||
/* pop 2 -> [x x x x] */
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(2, buf.len());
|
||||
assert_eq!(5, buf[0]);
|
||||
assert_eq!(6, buf[1]);
|
||||
2
|
||||
});
|
||||
|
||||
/* should now have two push slices */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(2, ps[0].len());
|
||||
assert_eq!(2, ps[1].len());
|
||||
drop(w);
|
||||
|
||||
/* make sure we exercise all wrap around cases properly */
|
||||
for _ in 0..10 {
|
||||
/* should be empty, push 1 */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(4, ps[0].len() + ps[1].len());
|
||||
w.push_done(1);
|
||||
drop(w);
|
||||
|
||||
/* should have 1 element */
|
||||
let mut w = rb.writer();
|
||||
let ps = w.push_slices();
|
||||
assert_eq!(3, ps[0].len() + ps[1].len());
|
||||
drop(w);
|
||||
|
||||
/* pop 1 */
|
||||
rb.reader().pop(|buf| {
|
||||
assert_eq!(1, buf.len());
|
||||
1
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
embassy-hal-internal/src/drop.rs
Normal file
51
embassy-hal-internal/src/drop.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use core::mem;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
#[must_use = "to delay the drop handler invokation to the end of the scope"]
|
||||
pub struct OnDrop<F: FnOnce()> {
|
||||
f: MaybeUninit<F>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> OnDrop<F> {
|
||||
pub fn new(f: F) -> Self {
|
||||
Self { f: MaybeUninit::new(f) }
|
||||
}
|
||||
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.f.as_ptr().read()() }
|
||||
}
|
||||
}
|
||||
|
||||
/// An explosive ordinance that panics if it is improperly disposed of.
|
||||
///
|
||||
/// This is to forbid dropping futures, when there is absolutely no other choice.
|
||||
///
|
||||
/// To correctly dispose of this device, call the [defuse](struct.DropBomb.html#method.defuse)
|
||||
/// method before this object is dropped.
|
||||
#[must_use = "to delay the drop bomb invokation to the end of the scope"]
|
||||
pub struct DropBomb {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl DropBomb {
|
||||
pub fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
/// Defuses the bomb, rendering it safe to drop.
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropBomb {
|
||||
fn drop(&mut self) {
|
||||
panic!("boom")
|
||||
}
|
||||
}
|
225
embassy-hal-internal/src/fmt.rs
Normal file
225
embassy-hal-internal/src/fmt.rs
Normal file
@ -0,0 +1,225 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
846
embassy-hal-internal/src/interrupt.rs
Normal file
846
embassy-hal-internal/src/interrupt.rs
Normal file
@ -0,0 +1,846 @@
|
||||
//! Interrupt handling for cortex-m devices.
|
||||
use core::mem;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
use cortex_m::interrupt::InterruptNumber;
|
||||
use cortex_m::peripheral::NVIC;
|
||||
|
||||
/// Generate a standard `mod interrupt` for a HAL.
|
||||
#[macro_export]
|
||||
macro_rules! interrupt_mod {
|
||||
($($irqs:ident),* $(,)?) => {
|
||||
#[cfg(feature = "rt")]
|
||||
pub use cortex_m_rt::interrupt;
|
||||
|
||||
/// Interrupt definitions.
|
||||
pub mod interrupt {
|
||||
pub use $crate::interrupt::{InterruptExt, Priority};
|
||||
pub use crate::pac::Interrupt::*;
|
||||
pub use crate::pac::Interrupt;
|
||||
|
||||
/// Type-level interrupt infrastructure.
|
||||
///
|
||||
/// This module contains one *type* per interrupt. This is used for checking at compile time that
|
||||
/// the interrupts are correctly bound to HAL drivers.
|
||||
///
|
||||
/// As an end user, you shouldn't need to use this module directly. Use the [`crate::bind_interrupts!`] macro
|
||||
/// to bind interrupts, and the [`crate::interrupt`] module to manually register interrupt handlers and manipulate
|
||||
/// interrupts directly (pending/unpending, enabling/disabling, setting the priority, etc...)
|
||||
pub mod typelevel {
|
||||
use super::InterruptExt;
|
||||
|
||||
mod sealed {
|
||||
pub trait Interrupt {}
|
||||
}
|
||||
|
||||
/// Type-level interrupt.
|
||||
///
|
||||
/// This trait is implemented for all typelevel interrupt types in this module.
|
||||
pub trait Interrupt: sealed::Interrupt {
|
||||
|
||||
/// Interrupt enum variant.
|
||||
///
|
||||
/// This allows going from typelevel interrupts (one type per interrupt) to
|
||||
/// non-typelevel interrupts (a single `Interrupt` enum type, with one variant per interrupt).
|
||||
const IRQ: super::Interrupt;
|
||||
|
||||
/// Enable the interrupt.
|
||||
#[inline]
|
||||
unsafe fn enable() {
|
||||
Self::IRQ.enable()
|
||||
}
|
||||
|
||||
/// Disable the interrupt.
|
||||
#[inline]
|
||||
fn disable() {
|
||||
Self::IRQ.disable()
|
||||
}
|
||||
|
||||
/// Check if interrupt is enabled.
|
||||
#[inline]
|
||||
fn is_enabled() -> bool {
|
||||
Self::IRQ.is_enabled()
|
||||
}
|
||||
|
||||
/// Check if interrupt is pending.
|
||||
#[inline]
|
||||
fn is_pending() -> bool {
|
||||
Self::IRQ.is_pending()
|
||||
}
|
||||
|
||||
/// Set interrupt pending.
|
||||
#[inline]
|
||||
fn pend() {
|
||||
Self::IRQ.pend()
|
||||
}
|
||||
|
||||
/// Unset interrupt pending.
|
||||
#[inline]
|
||||
fn unpend() {
|
||||
Self::IRQ.unpend()
|
||||
}
|
||||
|
||||
/// Get the priority of the interrupt.
|
||||
#[inline]
|
||||
fn get_priority() -> crate::interrupt::Priority {
|
||||
Self::IRQ.get_priority()
|
||||
}
|
||||
|
||||
/// Set the interrupt priority.
|
||||
#[inline]
|
||||
fn set_priority(prio: crate::interrupt::Priority) {
|
||||
Self::IRQ.set_priority(prio)
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc=stringify!($irqs)]
|
||||
#[doc=" typelevel interrupt."]
|
||||
pub enum $irqs {}
|
||||
impl sealed::Interrupt for $irqs{}
|
||||
impl Interrupt for $irqs {
|
||||
const IRQ: super::Interrupt = super::Interrupt::$irqs;
|
||||
}
|
||||
)*
|
||||
|
||||
/// Interrupt handler trait.
|
||||
///
|
||||
/// Drivers that need to handle interrupts implement this trait.
|
||||
/// The user must ensure `on_interrupt()` is called every time the interrupt fires.
|
||||
/// Drivers must use use [`Binding`] to assert at compile time that the user has done so.
|
||||
pub trait Handler<I: Interrupt> {
|
||||
/// Interrupt handler function.
|
||||
///
|
||||
/// Must be called every time the `I` interrupt fires, synchronously from
|
||||
/// the interrupt handler context.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must ONLY be called from the interrupt handler for `I`.
|
||||
unsafe fn on_interrupt();
|
||||
}
|
||||
|
||||
/// Compile-time assertion that an interrupt has been bound to a handler.
|
||||
///
|
||||
/// For the vast majority of cases, you should use the `bind_interrupts!`
|
||||
/// macro instead of writing `unsafe impl`s of this trait.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()`
|
||||
/// to be called every time the `I` interrupt fires.
|
||||
///
|
||||
/// This allows drivers to check bindings at compile-time.
|
||||
pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents an interrupt type that can be configured by embassy to handle
|
||||
/// interrupts.
|
||||
pub unsafe trait InterruptExt: InterruptNumber + Copy {
|
||||
/// Enable the interrupt.
|
||||
#[inline]
|
||||
unsafe fn enable(self) {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
NVIC::unmask(self)
|
||||
}
|
||||
|
||||
/// Disable the interrupt.
|
||||
#[inline]
|
||||
fn disable(self) {
|
||||
NVIC::mask(self);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Check if interrupt is being handled.
|
||||
#[inline]
|
||||
#[cfg(not(armv6m))]
|
||||
fn is_active(self) -> bool {
|
||||
NVIC::is_active(self)
|
||||
}
|
||||
|
||||
/// Check if interrupt is enabled.
|
||||
#[inline]
|
||||
fn is_enabled(self) -> bool {
|
||||
NVIC::is_enabled(self)
|
||||
}
|
||||
|
||||
/// Check if interrupt is pending.
|
||||
#[inline]
|
||||
fn is_pending(self) -> bool {
|
||||
NVIC::is_pending(self)
|
||||
}
|
||||
|
||||
/// Set interrupt pending.
|
||||
#[inline]
|
||||
fn pend(self) {
|
||||
NVIC::pend(self)
|
||||
}
|
||||
|
||||
/// Unset interrupt pending.
|
||||
#[inline]
|
||||
fn unpend(self) {
|
||||
NVIC::unpend(self)
|
||||
}
|
||||
|
||||
/// Get the priority of the interrupt.
|
||||
#[inline]
|
||||
fn get_priority(self) -> Priority {
|
||||
Priority::from(NVIC::get_priority(self))
|
||||
}
|
||||
|
||||
/// Set the interrupt priority.
|
||||
#[inline]
|
||||
fn set_priority(self, prio: Priority) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
||||
nvic.set_priority(self, prio.into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: InterruptNumber + Copy> InterruptExt for T {}
|
||||
|
||||
impl From<u8> for Priority {
|
||||
fn from(priority: u8) -> Self {
|
||||
unsafe { mem::transmute(priority & PRIO_MASK) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Priority> for u8 {
|
||||
fn from(p: Priority) -> Self {
|
||||
p as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "prio-bits-0")]
|
||||
const PRIO_MASK: u8 = 0x00;
|
||||
#[cfg(feature = "prio-bits-1")]
|
||||
const PRIO_MASK: u8 = 0x80;
|
||||
#[cfg(feature = "prio-bits-2")]
|
||||
const PRIO_MASK: u8 = 0xc0;
|
||||
#[cfg(feature = "prio-bits-3")]
|
||||
const PRIO_MASK: u8 = 0xe0;
|
||||
#[cfg(feature = "prio-bits-4")]
|
||||
const PRIO_MASK: u8 = 0xf0;
|
||||
#[cfg(feature = "prio-bits-5")]
|
||||
const PRIO_MASK: u8 = 0xf8;
|
||||
#[cfg(feature = "prio-bits-6")]
|
||||
const PRIO_MASK: u8 = 0xfc;
|
||||
#[cfg(feature = "prio-bits-7")]
|
||||
const PRIO_MASK: u8 = 0xfe;
|
||||
#[cfg(feature = "prio-bits-8")]
|
||||
const PRIO_MASK: u8 = 0xff;
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-0")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-1")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x80,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-2")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x40,
|
||||
P2 = 0x80,
|
||||
P3 = 0xc0,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-3")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x20,
|
||||
P2 = 0x40,
|
||||
P3 = 0x60,
|
||||
P4 = 0x80,
|
||||
P5 = 0xa0,
|
||||
P6 = 0xc0,
|
||||
P7 = 0xe0,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-4")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x10,
|
||||
P2 = 0x20,
|
||||
P3 = 0x30,
|
||||
P4 = 0x40,
|
||||
P5 = 0x50,
|
||||
P6 = 0x60,
|
||||
P7 = 0x70,
|
||||
P8 = 0x80,
|
||||
P9 = 0x90,
|
||||
P10 = 0xa0,
|
||||
P11 = 0xb0,
|
||||
P12 = 0xc0,
|
||||
P13 = 0xd0,
|
||||
P14 = 0xe0,
|
||||
P15 = 0xf0,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-5")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x8,
|
||||
P2 = 0x10,
|
||||
P3 = 0x18,
|
||||
P4 = 0x20,
|
||||
P5 = 0x28,
|
||||
P6 = 0x30,
|
||||
P7 = 0x38,
|
||||
P8 = 0x40,
|
||||
P9 = 0x48,
|
||||
P10 = 0x50,
|
||||
P11 = 0x58,
|
||||
P12 = 0x60,
|
||||
P13 = 0x68,
|
||||
P14 = 0x70,
|
||||
P15 = 0x78,
|
||||
P16 = 0x80,
|
||||
P17 = 0x88,
|
||||
P18 = 0x90,
|
||||
P19 = 0x98,
|
||||
P20 = 0xa0,
|
||||
P21 = 0xa8,
|
||||
P22 = 0xb0,
|
||||
P23 = 0xb8,
|
||||
P24 = 0xc0,
|
||||
P25 = 0xc8,
|
||||
P26 = 0xd0,
|
||||
P27 = 0xd8,
|
||||
P28 = 0xe0,
|
||||
P29 = 0xe8,
|
||||
P30 = 0xf0,
|
||||
P31 = 0xf8,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-6")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x4,
|
||||
P2 = 0x8,
|
||||
P3 = 0xc,
|
||||
P4 = 0x10,
|
||||
P5 = 0x14,
|
||||
P6 = 0x18,
|
||||
P7 = 0x1c,
|
||||
P8 = 0x20,
|
||||
P9 = 0x24,
|
||||
P10 = 0x28,
|
||||
P11 = 0x2c,
|
||||
P12 = 0x30,
|
||||
P13 = 0x34,
|
||||
P14 = 0x38,
|
||||
P15 = 0x3c,
|
||||
P16 = 0x40,
|
||||
P17 = 0x44,
|
||||
P18 = 0x48,
|
||||
P19 = 0x4c,
|
||||
P20 = 0x50,
|
||||
P21 = 0x54,
|
||||
P22 = 0x58,
|
||||
P23 = 0x5c,
|
||||
P24 = 0x60,
|
||||
P25 = 0x64,
|
||||
P26 = 0x68,
|
||||
P27 = 0x6c,
|
||||
P28 = 0x70,
|
||||
P29 = 0x74,
|
||||
P30 = 0x78,
|
||||
P31 = 0x7c,
|
||||
P32 = 0x80,
|
||||
P33 = 0x84,
|
||||
P34 = 0x88,
|
||||
P35 = 0x8c,
|
||||
P36 = 0x90,
|
||||
P37 = 0x94,
|
||||
P38 = 0x98,
|
||||
P39 = 0x9c,
|
||||
P40 = 0xa0,
|
||||
P41 = 0xa4,
|
||||
P42 = 0xa8,
|
||||
P43 = 0xac,
|
||||
P44 = 0xb0,
|
||||
P45 = 0xb4,
|
||||
P46 = 0xb8,
|
||||
P47 = 0xbc,
|
||||
P48 = 0xc0,
|
||||
P49 = 0xc4,
|
||||
P50 = 0xc8,
|
||||
P51 = 0xcc,
|
||||
P52 = 0xd0,
|
||||
P53 = 0xd4,
|
||||
P54 = 0xd8,
|
||||
P55 = 0xdc,
|
||||
P56 = 0xe0,
|
||||
P57 = 0xe4,
|
||||
P58 = 0xe8,
|
||||
P59 = 0xec,
|
||||
P60 = 0xf0,
|
||||
P61 = 0xf4,
|
||||
P62 = 0xf8,
|
||||
P63 = 0xfc,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-7")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x2,
|
||||
P2 = 0x4,
|
||||
P3 = 0x6,
|
||||
P4 = 0x8,
|
||||
P5 = 0xa,
|
||||
P6 = 0xc,
|
||||
P7 = 0xe,
|
||||
P8 = 0x10,
|
||||
P9 = 0x12,
|
||||
P10 = 0x14,
|
||||
P11 = 0x16,
|
||||
P12 = 0x18,
|
||||
P13 = 0x1a,
|
||||
P14 = 0x1c,
|
||||
P15 = 0x1e,
|
||||
P16 = 0x20,
|
||||
P17 = 0x22,
|
||||
P18 = 0x24,
|
||||
P19 = 0x26,
|
||||
P20 = 0x28,
|
||||
P21 = 0x2a,
|
||||
P22 = 0x2c,
|
||||
P23 = 0x2e,
|
||||
P24 = 0x30,
|
||||
P25 = 0x32,
|
||||
P26 = 0x34,
|
||||
P27 = 0x36,
|
||||
P28 = 0x38,
|
||||
P29 = 0x3a,
|
||||
P30 = 0x3c,
|
||||
P31 = 0x3e,
|
||||
P32 = 0x40,
|
||||
P33 = 0x42,
|
||||
P34 = 0x44,
|
||||
P35 = 0x46,
|
||||
P36 = 0x48,
|
||||
P37 = 0x4a,
|
||||
P38 = 0x4c,
|
||||
P39 = 0x4e,
|
||||
P40 = 0x50,
|
||||
P41 = 0x52,
|
||||
P42 = 0x54,
|
||||
P43 = 0x56,
|
||||
P44 = 0x58,
|
||||
P45 = 0x5a,
|
||||
P46 = 0x5c,
|
||||
P47 = 0x5e,
|
||||
P48 = 0x60,
|
||||
P49 = 0x62,
|
||||
P50 = 0x64,
|
||||
P51 = 0x66,
|
||||
P52 = 0x68,
|
||||
P53 = 0x6a,
|
||||
P54 = 0x6c,
|
||||
P55 = 0x6e,
|
||||
P56 = 0x70,
|
||||
P57 = 0x72,
|
||||
P58 = 0x74,
|
||||
P59 = 0x76,
|
||||
P60 = 0x78,
|
||||
P61 = 0x7a,
|
||||
P62 = 0x7c,
|
||||
P63 = 0x7e,
|
||||
P64 = 0x80,
|
||||
P65 = 0x82,
|
||||
P66 = 0x84,
|
||||
P67 = 0x86,
|
||||
P68 = 0x88,
|
||||
P69 = 0x8a,
|
||||
P70 = 0x8c,
|
||||
P71 = 0x8e,
|
||||
P72 = 0x90,
|
||||
P73 = 0x92,
|
||||
P74 = 0x94,
|
||||
P75 = 0x96,
|
||||
P76 = 0x98,
|
||||
P77 = 0x9a,
|
||||
P78 = 0x9c,
|
||||
P79 = 0x9e,
|
||||
P80 = 0xa0,
|
||||
P81 = 0xa2,
|
||||
P82 = 0xa4,
|
||||
P83 = 0xa6,
|
||||
P84 = 0xa8,
|
||||
P85 = 0xaa,
|
||||
P86 = 0xac,
|
||||
P87 = 0xae,
|
||||
P88 = 0xb0,
|
||||
P89 = 0xb2,
|
||||
P90 = 0xb4,
|
||||
P91 = 0xb6,
|
||||
P92 = 0xb8,
|
||||
P93 = 0xba,
|
||||
P94 = 0xbc,
|
||||
P95 = 0xbe,
|
||||
P96 = 0xc0,
|
||||
P97 = 0xc2,
|
||||
P98 = 0xc4,
|
||||
P99 = 0xc6,
|
||||
P100 = 0xc8,
|
||||
P101 = 0xca,
|
||||
P102 = 0xcc,
|
||||
P103 = 0xce,
|
||||
P104 = 0xd0,
|
||||
P105 = 0xd2,
|
||||
P106 = 0xd4,
|
||||
P107 = 0xd6,
|
||||
P108 = 0xd8,
|
||||
P109 = 0xda,
|
||||
P110 = 0xdc,
|
||||
P111 = 0xde,
|
||||
P112 = 0xe0,
|
||||
P113 = 0xe2,
|
||||
P114 = 0xe4,
|
||||
P115 = 0xe6,
|
||||
P116 = 0xe8,
|
||||
P117 = 0xea,
|
||||
P118 = 0xec,
|
||||
P119 = 0xee,
|
||||
P120 = 0xf0,
|
||||
P121 = 0xf2,
|
||||
P122 = 0xf4,
|
||||
P123 = 0xf6,
|
||||
P124 = 0xf8,
|
||||
P125 = 0xfa,
|
||||
P126 = 0xfc,
|
||||
P127 = 0xfe,
|
||||
}
|
||||
|
||||
/// The interrupt priority level.
|
||||
///
|
||||
/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.
|
||||
#[cfg(feature = "prio-bits-8")]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Priority {
|
||||
P0 = 0x0,
|
||||
P1 = 0x1,
|
||||
P2 = 0x2,
|
||||
P3 = 0x3,
|
||||
P4 = 0x4,
|
||||
P5 = 0x5,
|
||||
P6 = 0x6,
|
||||
P7 = 0x7,
|
||||
P8 = 0x8,
|
||||
P9 = 0x9,
|
||||
P10 = 0xa,
|
||||
P11 = 0xb,
|
||||
P12 = 0xc,
|
||||
P13 = 0xd,
|
||||
P14 = 0xe,
|
||||
P15 = 0xf,
|
||||
P16 = 0x10,
|
||||
P17 = 0x11,
|
||||
P18 = 0x12,
|
||||
P19 = 0x13,
|
||||
P20 = 0x14,
|
||||
P21 = 0x15,
|
||||
P22 = 0x16,
|
||||
P23 = 0x17,
|
||||
P24 = 0x18,
|
||||
P25 = 0x19,
|
||||
P26 = 0x1a,
|
||||
P27 = 0x1b,
|
||||
P28 = 0x1c,
|
||||
P29 = 0x1d,
|
||||
P30 = 0x1e,
|
||||
P31 = 0x1f,
|
||||
P32 = 0x20,
|
||||
P33 = 0x21,
|
||||
P34 = 0x22,
|
||||
P35 = 0x23,
|
||||
P36 = 0x24,
|
||||
P37 = 0x25,
|
||||
P38 = 0x26,
|
||||
P39 = 0x27,
|
||||
P40 = 0x28,
|
||||
P41 = 0x29,
|
||||
P42 = 0x2a,
|
||||
P43 = 0x2b,
|
||||
P44 = 0x2c,
|
||||
P45 = 0x2d,
|
||||
P46 = 0x2e,
|
||||
P47 = 0x2f,
|
||||
P48 = 0x30,
|
||||
P49 = 0x31,
|
||||
P50 = 0x32,
|
||||
P51 = 0x33,
|
||||
P52 = 0x34,
|
||||
P53 = 0x35,
|
||||
P54 = 0x36,
|
||||
P55 = 0x37,
|
||||
P56 = 0x38,
|
||||
P57 = 0x39,
|
||||
P58 = 0x3a,
|
||||
P59 = 0x3b,
|
||||
P60 = 0x3c,
|
||||
P61 = 0x3d,
|
||||
P62 = 0x3e,
|
||||
P63 = 0x3f,
|
||||
P64 = 0x40,
|
||||
P65 = 0x41,
|
||||
P66 = 0x42,
|
||||
P67 = 0x43,
|
||||
P68 = 0x44,
|
||||
P69 = 0x45,
|
||||
P70 = 0x46,
|
||||
P71 = 0x47,
|
||||
P72 = 0x48,
|
||||
P73 = 0x49,
|
||||
P74 = 0x4a,
|
||||
P75 = 0x4b,
|
||||
P76 = 0x4c,
|
||||
P77 = 0x4d,
|
||||
P78 = 0x4e,
|
||||
P79 = 0x4f,
|
||||
P80 = 0x50,
|
||||
P81 = 0x51,
|
||||
P82 = 0x52,
|
||||
P83 = 0x53,
|
||||
P84 = 0x54,
|
||||
P85 = 0x55,
|
||||
P86 = 0x56,
|
||||
P87 = 0x57,
|
||||
P88 = 0x58,
|
||||
P89 = 0x59,
|
||||
P90 = 0x5a,
|
||||
P91 = 0x5b,
|
||||
P92 = 0x5c,
|
||||
P93 = 0x5d,
|
||||
P94 = 0x5e,
|
||||
P95 = 0x5f,
|
||||
P96 = 0x60,
|
||||
P97 = 0x61,
|
||||
P98 = 0x62,
|
||||
P99 = 0x63,
|
||||
P100 = 0x64,
|
||||
P101 = 0x65,
|
||||
P102 = 0x66,
|
||||
P103 = 0x67,
|
||||
P104 = 0x68,
|
||||
P105 = 0x69,
|
||||
P106 = 0x6a,
|
||||
P107 = 0x6b,
|
||||
P108 = 0x6c,
|
||||
P109 = 0x6d,
|
||||
P110 = 0x6e,
|
||||
P111 = 0x6f,
|
||||
P112 = 0x70,
|
||||
P113 = 0x71,
|
||||
P114 = 0x72,
|
||||
P115 = 0x73,
|
||||
P116 = 0x74,
|
||||
P117 = 0x75,
|
||||
P118 = 0x76,
|
||||
P119 = 0x77,
|
||||
P120 = 0x78,
|
||||
P121 = 0x79,
|
||||
P122 = 0x7a,
|
||||
P123 = 0x7b,
|
||||
P124 = 0x7c,
|
||||
P125 = 0x7d,
|
||||
P126 = 0x7e,
|
||||
P127 = 0x7f,
|
||||
P128 = 0x80,
|
||||
P129 = 0x81,
|
||||
P130 = 0x82,
|
||||
P131 = 0x83,
|
||||
P132 = 0x84,
|
||||
P133 = 0x85,
|
||||
P134 = 0x86,
|
||||
P135 = 0x87,
|
||||
P136 = 0x88,
|
||||
P137 = 0x89,
|
||||
P138 = 0x8a,
|
||||
P139 = 0x8b,
|
||||
P140 = 0x8c,
|
||||
P141 = 0x8d,
|
||||
P142 = 0x8e,
|
||||
P143 = 0x8f,
|
||||
P144 = 0x90,
|
||||
P145 = 0x91,
|
||||
P146 = 0x92,
|
||||
P147 = 0x93,
|
||||
P148 = 0x94,
|
||||
P149 = 0x95,
|
||||
P150 = 0x96,
|
||||
P151 = 0x97,
|
||||
P152 = 0x98,
|
||||
P153 = 0x99,
|
||||
P154 = 0x9a,
|
||||
P155 = 0x9b,
|
||||
P156 = 0x9c,
|
||||
P157 = 0x9d,
|
||||
P158 = 0x9e,
|
||||
P159 = 0x9f,
|
||||
P160 = 0xa0,
|
||||
P161 = 0xa1,
|
||||
P162 = 0xa2,
|
||||
P163 = 0xa3,
|
||||
P164 = 0xa4,
|
||||
P165 = 0xa5,
|
||||
P166 = 0xa6,
|
||||
P167 = 0xa7,
|
||||
P168 = 0xa8,
|
||||
P169 = 0xa9,
|
||||
P170 = 0xaa,
|
||||
P171 = 0xab,
|
||||
P172 = 0xac,
|
||||
P173 = 0xad,
|
||||
P174 = 0xae,
|
||||
P175 = 0xaf,
|
||||
P176 = 0xb0,
|
||||
P177 = 0xb1,
|
||||
P178 = 0xb2,
|
||||
P179 = 0xb3,
|
||||
P180 = 0xb4,
|
||||
P181 = 0xb5,
|
||||
P182 = 0xb6,
|
||||
P183 = 0xb7,
|
||||
P184 = 0xb8,
|
||||
P185 = 0xb9,
|
||||
P186 = 0xba,
|
||||
P187 = 0xbb,
|
||||
P188 = 0xbc,
|
||||
P189 = 0xbd,
|
||||
P190 = 0xbe,
|
||||
P191 = 0xbf,
|
||||
P192 = 0xc0,
|
||||
P193 = 0xc1,
|
||||
P194 = 0xc2,
|
||||
P195 = 0xc3,
|
||||
P196 = 0xc4,
|
||||
P197 = 0xc5,
|
||||
P198 = 0xc6,
|
||||
P199 = 0xc7,
|
||||
P200 = 0xc8,
|
||||
P201 = 0xc9,
|
||||
P202 = 0xca,
|
||||
P203 = 0xcb,
|
||||
P204 = 0xcc,
|
||||
P205 = 0xcd,
|
||||
P206 = 0xce,
|
||||
P207 = 0xcf,
|
||||
P208 = 0xd0,
|
||||
P209 = 0xd1,
|
||||
P210 = 0xd2,
|
||||
P211 = 0xd3,
|
||||
P212 = 0xd4,
|
||||
P213 = 0xd5,
|
||||
P214 = 0xd6,
|
||||
P215 = 0xd7,
|
||||
P216 = 0xd8,
|
||||
P217 = 0xd9,
|
||||
P218 = 0xda,
|
||||
P219 = 0xdb,
|
||||
P220 = 0xdc,
|
||||
P221 = 0xdd,
|
||||
P222 = 0xde,
|
||||
P223 = 0xdf,
|
||||
P224 = 0xe0,
|
||||
P225 = 0xe1,
|
||||
P226 = 0xe2,
|
||||
P227 = 0xe3,
|
||||
P228 = 0xe4,
|
||||
P229 = 0xe5,
|
||||
P230 = 0xe6,
|
||||
P231 = 0xe7,
|
||||
P232 = 0xe8,
|
||||
P233 = 0xe9,
|
||||
P234 = 0xea,
|
||||
P235 = 0xeb,
|
||||
P236 = 0xec,
|
||||
P237 = 0xed,
|
||||
P238 = 0xee,
|
||||
P239 = 0xef,
|
||||
P240 = 0xf0,
|
||||
P241 = 0xf1,
|
||||
P242 = 0xf2,
|
||||
P243 = 0xf3,
|
||||
P244 = 0xf4,
|
||||
P245 = 0xf5,
|
||||
P246 = 0xf6,
|
||||
P247 = 0xf7,
|
||||
P248 = 0xf8,
|
||||
P249 = 0xf9,
|
||||
P250 = 0xfa,
|
||||
P251 = 0xfb,
|
||||
P252 = 0xfc,
|
||||
P253 = 0xfd,
|
||||
P254 = 0xfe,
|
||||
P255 = 0xff,
|
||||
}
|
17
embassy-hal-internal/src/lib.rs
Normal file
17
embassy-hal-internal/src/lib.rs
Normal file
@ -0,0 +1,17 @@
|
||||
#![no_std]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
pub mod atomic_ring_buffer;
|
||||
pub mod drop;
|
||||
mod macros;
|
||||
mod peripheral;
|
||||
pub mod ratio;
|
||||
pub mod ring_buffer;
|
||||
pub use peripheral::{Peripheral, PeripheralRef};
|
||||
|
||||
#[cfg(feature = "cortex-m")]
|
||||
pub mod interrupt;
|
123
embassy-hal-internal/src/macros.rs
Normal file
123
embassy-hal-internal/src/macros.rs
Normal file
@ -0,0 +1,123 @@
|
||||
#[macro_export]
|
||||
macro_rules! peripherals_definition {
|
||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||
/// Types for the peripheral singletons.
|
||||
pub mod peripherals {
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc = concat!(stringify!($name), " peripheral")]
|
||||
pub struct $name { _private: () }
|
||||
|
||||
$(#[$cfg])?
|
||||
impl $name {
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a time.
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self{ _private: ()}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$cfg])?
|
||||
$crate::impl_peripheral!($name);
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! peripherals_struct {
|
||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||
/// Struct containing all the peripheral singletons.
|
||||
///
|
||||
/// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`].
|
||||
#[allow(non_snake_case)]
|
||||
pub struct Peripherals {
|
||||
$(
|
||||
#[doc = concat!(stringify!($name), " peripheral")]
|
||||
$(#[$cfg])?
|
||||
pub $name: peripherals::$name,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Peripherals {
|
||||
///Returns all the peripherals *once*
|
||||
#[inline]
|
||||
pub(crate) fn take() -> Self {
|
||||
|
||||
#[no_mangle]
|
||||
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
if _EMBASSY_DEVICE_PERIPHERALS {
|
||||
panic!("init called more than once!")
|
||||
}
|
||||
_EMBASSY_DEVICE_PERIPHERALS = true;
|
||||
Self::steal()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Peripherals {
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a time.
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name: peripherals::$name::steal(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! peripherals {
|
||||
($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
|
||||
$crate::peripherals_definition!(
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name,
|
||||
)*
|
||||
);
|
||||
$crate::peripherals_struct!(
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name,
|
||||
)*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! into_ref {
|
||||
($($name:ident),*) => {
|
||||
$(
|
||||
let mut $name = $name.into_ref();
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_peripheral {
|
||||
($type:ident) => {
|
||||
impl $crate::Peripheral for $type {
|
||||
type P = $type;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
$type { ..*self }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
174
embassy-hal-internal/src/peripheral.rs
Normal file
174
embassy-hal-internal/src/peripheral.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
/// An exclusive reference to a peripheral.
|
||||
///
|
||||
/// This is functionally the same as a `&'a mut T`. There's a few advantages in having
|
||||
/// a dedicated struct instead:
|
||||
///
|
||||
/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete
|
||||
/// peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte).
|
||||
/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
|
||||
/// PeripheralRef stores a copy of `T` instead, so it's the same size.
|
||||
/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`,
|
||||
/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic
|
||||
/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes
|
||||
/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization.
|
||||
pub struct PeripheralRef<'a, T> {
|
||||
inner: T,
|
||||
_lifetime: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<'a, T> PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely clone (duplicate) a peripheral singleton.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This returns an owned clone of the peripheral. You must manually ensure
|
||||
/// only one copy of the peripheral is in use at a time. For example, don't
|
||||
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
|
||||
///
|
||||
/// You should strongly prefer using `reborrow()` instead. It returns a
|
||||
/// `PeripheralRef` that borrows `self`, which allows the borrow checker
|
||||
/// to enforce this at compile time.
|
||||
pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T>
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
PeripheralRef::new(self.inner.clone_unchecked())
|
||||
}
|
||||
|
||||
/// Reborrow into a "child" PeripheralRef.
|
||||
///
|
||||
/// `self` will stay borrowed until the child PeripheralRef is dropped.
|
||||
pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
// safety: we're returning the clone inside a new PeripheralRef that borrows
|
||||
// self, so user code can't use both at the same time.
|
||||
PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
|
||||
}
|
||||
|
||||
/// Map the inner peripheral using `Into`.
|
||||
///
|
||||
/// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`, using an
|
||||
/// `Into` impl to convert from `T` to `U`.
|
||||
///
|
||||
/// For example, this can be useful to degrade GPIO pins: converting from PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`.
|
||||
#[inline]
|
||||
pub fn map_into<U>(self) -> PeripheralRef<'a, U>
|
||||
where
|
||||
T: Into<U>,
|
||||
{
|
||||
PeripheralRef {
|
||||
inner: self.inner.into(),
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for PeripheralRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for any type that can be used as a peripheral of type `P`.
|
||||
///
|
||||
/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
|
||||
/// or borrowed peripherals (e.g. `&mut TWISPI0`).
|
||||
///
|
||||
/// For example, if you have a driver with a constructor like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// pub fn new(
|
||||
/// twim: impl Peripheral<P = T> + 'd,
|
||||
/// irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
/// sda: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
/// scl: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
/// config: Config,
|
||||
/// ) -> Self { .. }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You may call it with owned peripherals, which yields an instance that can live forever (`'static`):
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
|
||||
/// ```
|
||||
///
|
||||
/// Or you may call it with borrowed peripherals, which yields an instance that can only live for as long
|
||||
/// as the borrows last:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
|
||||
/// ```
|
||||
///
|
||||
/// # Implementation details, for HAL authors
|
||||
///
|
||||
/// When writing a HAL, the intended way to use this trait is to take `impl Peripheral<P = ..>` in
|
||||
/// the HAL's public API (such as driver constructors), calling `.into_ref()` to obtain a `PeripheralRef`,
|
||||
/// and storing that in the driver struct.
|
||||
///
|
||||
/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
|
||||
/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
|
||||
pub trait Peripheral: Sized {
|
||||
/// Peripheral singleton type
|
||||
type P;
|
||||
|
||||
/// Unsafely clone (duplicate) a peripheral singleton.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This returns an owned clone of the peripheral. You must manually ensure
|
||||
/// only one copy of the peripheral is in use at a time. For example, don't
|
||||
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
|
||||
///
|
||||
/// You should strongly prefer using `into_ref()` instead. It returns a
|
||||
/// `PeripheralRef`, which allows the borrow checker to enforce this at compile time.
|
||||
unsafe fn clone_unchecked(&self) -> Self::P;
|
||||
|
||||
/// Convert a value into a `PeripheralRef`.
|
||||
///
|
||||
/// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
|
||||
/// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
|
||||
#[inline]
|
||||
fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
PeripheralRef::new(unsafe { self.clone_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, T: DerefMut> Peripheral for T
|
||||
where
|
||||
T::Target: Peripheral,
|
||||
{
|
||||
type P = <T::Target as Peripheral>::P;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
self.deref().clone_unchecked()
|
||||
}
|
||||
}
|
129
embassy-hal-internal/src/ratio.rs
Normal file
129
embassy-hal-internal/src/ratio.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use core::ops::{Add, Div, Mul};
|
||||
|
||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
|
||||
|
||||
/// Represents the ratio between two numbers.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Ratio<T> {
|
||||
/// Numerator.
|
||||
numer: T,
|
||||
/// Denominator.
|
||||
denom: T,
|
||||
}
|
||||
|
||||
impl<T> Ratio<T> {
|
||||
/// Creates a new `Ratio`.
|
||||
#[inline(always)]
|
||||
pub const fn new_raw(numer: T, denom: T) -> Ratio<T> {
|
||||
Ratio { numer, denom }
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the numerator.
|
||||
#[inline(always)]
|
||||
pub const fn numer(&self) -> &T {
|
||||
&self.numer
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the denominator.
|
||||
#[inline(always)]
|
||||
pub const fn denom(&self) -> &T {
|
||||
&self.denom
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedDiv> Ratio<T> {
|
||||
/// Converts to an integer, rounding towards zero.
|
||||
#[inline(always)]
|
||||
pub fn to_integer(&self) -> T {
|
||||
unwrap!(self.numer().checked_div(self.denom()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedMul> Div<T> for Ratio<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn div(mut self, rhs: T) -> Self::Output {
|
||||
self.denom = unwrap!(self.denom().checked_mul(&rhs));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedMul> Mul<T> for Ratio<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn mul(mut self, rhs: T) -> Self::Output {
|
||||
self.numer = unwrap!(self.numer().checked_mul(&rhs));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedMul + CheckedAdd> Add<T> for Ratio<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(mut self, rhs: T) -> Self::Output {
|
||||
self.numer = unwrap!(unwrap!(self.denom().checked_mul(&rhs)).checked_add(self.numer()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_for_float {
|
||||
($from:ident) => {
|
||||
impl From<Ratio<$from>> for f32 {
|
||||
#[inline(always)]
|
||||
fn from(r: Ratio<$from>) -> Self {
|
||||
(r.numer as f32) / (r.denom as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ratio<$from>> for f64 {
|
||||
#[inline(always)]
|
||||
fn from(r: Ratio<$from>) -> Self {
|
||||
(r.numer as f64) / (r.denom as f64)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_for_float!(u8);
|
||||
impl_from_for_float!(u16);
|
||||
impl_from_for_float!(u32);
|
||||
impl_from_for_float!(u64);
|
||||
impl_from_for_float!(u128);
|
||||
impl_from_for_float!(i8);
|
||||
impl_from_for_float!(i16);
|
||||
impl_from_for_float!(i32);
|
||||
impl_from_for_float!(i64);
|
||||
impl_from_for_float!(i128);
|
||||
|
||||
impl<T: core::fmt::Display> core::fmt::Display for Ratio<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
core::write!(f, "{} / {}", self.numer(), self.denom())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ratio;
|
||||
|
||||
#[test]
|
||||
fn basics() {
|
||||
let mut r = Ratio::new_raw(1, 2) + 2;
|
||||
assert_eq!(*r.numer(), 5);
|
||||
assert_eq!(*r.denom(), 2);
|
||||
assert_eq!(r.to_integer(), 2);
|
||||
|
||||
r = r * 2;
|
||||
assert_eq!(*r.numer(), 10);
|
||||
assert_eq!(*r.denom(), 2);
|
||||
assert_eq!(r.to_integer(), 5);
|
||||
|
||||
r = r / 2;
|
||||
assert_eq!(*r.numer(), 10);
|
||||
assert_eq!(*r.denom(), 4);
|
||||
assert_eq!(r.to_integer(), 2);
|
||||
}
|
||||
}
|
136
embassy-hal-internal/src/ring_buffer.rs
Normal file
136
embassy-hal-internal/src/ring_buffer.rs
Normal file
@ -0,0 +1,136 @@
|
||||
pub struct RingBuffer<'a> {
|
||||
buf: &'a mut [u8],
|
||||
start: usize,
|
||||
end: usize,
|
||||
empty: bool,
|
||||
}
|
||||
|
||||
impl<'a> RingBuffer<'a> {
|
||||
pub fn new(buf: &'a mut [u8]) -> Self {
|
||||
Self {
|
||||
buf,
|
||||
start: 0,
|
||||
end: 0,
|
||||
empty: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_buf(&mut self) -> &mut [u8] {
|
||||
if self.start == self.end && !self.empty {
|
||||
trace!(" ringbuf: push_buf empty");
|
||||
return &mut self.buf[..0];
|
||||
}
|
||||
|
||||
let n = if self.start <= self.end {
|
||||
self.buf.len() - self.end
|
||||
} else {
|
||||
self.start - self.end
|
||||
};
|
||||
|
||||
trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
|
||||
&mut self.buf[self.end..self.end + n]
|
||||
}
|
||||
|
||||
pub fn push(&mut self, n: usize) {
|
||||
trace!(" ringbuf: push {:?}", n);
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.end = self.wrap(self.end + n);
|
||||
self.empty = false;
|
||||
}
|
||||
|
||||
pub fn pop_buf(&mut self) -> &mut [u8] {
|
||||
if self.empty {
|
||||
trace!(" ringbuf: pop_buf empty");
|
||||
return &mut self.buf[..0];
|
||||
}
|
||||
|
||||
let n = if self.end <= self.start {
|
||||
self.buf.len() - self.start
|
||||
} else {
|
||||
self.end - self.start
|
||||
};
|
||||
|
||||
trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
|
||||
&mut self.buf[self.start..self.start + n]
|
||||
}
|
||||
|
||||
pub fn pop(&mut self, n: usize) {
|
||||
trace!(" ringbuf: pop {:?}", n);
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.start = self.wrap(self.start + n);
|
||||
self.empty = self.start == self.end;
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.start == self.end && !self.empty
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.empty
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.start = 0;
|
||||
self.end = 0;
|
||||
self.empty = true;
|
||||
}
|
||||
|
||||
fn wrap(&self, n: usize) -> usize {
|
||||
assert!(n <= self.buf.len());
|
||||
if n == self.buf.len() {
|
||||
0
|
||||
} else {
|
||||
n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn push_pop() {
|
||||
let mut b = [0; 4];
|
||||
let mut rb = RingBuffer::new(&mut b);
|
||||
let buf = rb.push_buf();
|
||||
assert_eq!(4, buf.len());
|
||||
buf[0] = 1;
|
||||
buf[1] = 2;
|
||||
buf[2] = 3;
|
||||
buf[3] = 4;
|
||||
rb.push(4);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(4, buf.len());
|
||||
assert_eq!(1, buf[0]);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(3, buf.len());
|
||||
assert_eq!(2, buf[0]);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(2, buf.len());
|
||||
assert_eq!(3, buf[0]);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(1, buf.len());
|
||||
assert_eq!(4, buf[0]);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(0, buf.len());
|
||||
|
||||
let buf = rb.push_buf();
|
||||
assert_eq!(4, buf.len());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user