Merge #1186
1186: Add some docs r=Dirbaio a=Dirbaio This also does some renames of things to more intuitive/consistent names. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
commit
594969f281
@ -1,10 +1,12 @@
|
||||
#[macro_export]
|
||||
macro_rules! peripherals {
|
||||
($($(#[$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])?
|
||||
@ -25,9 +27,13 @@ macro_rules! peripherals {
|
||||
)*
|
||||
}
|
||||
|
||||
/// 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,
|
||||
)*
|
||||
|
@ -6,7 +6,10 @@ pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> {
|
||||
let name_interrupt = format_ident!("{}", name);
|
||||
let name_handler = format!("__EMBASSY_{}_HANDLER", name);
|
||||
|
||||
let doc = format!("{} interrupt singleton.", name);
|
||||
|
||||
let result = quote! {
|
||||
#[doc = #doc]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct #name_interrupt(());
|
||||
unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name_interrupt {
|
||||
|
58
embassy-nrf/README.md
Normal file
58
embassy-nrf/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Embassy nRF HAL
|
||||
|
||||
HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed.
|
||||
|
||||
The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs
|
||||
for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to
|
||||
complete operations in low power mod and handling interrupts, so that applications can focus on more important matters.
|
||||
|
||||
## EasyDMA considerations
|
||||
|
||||
On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting
|
||||
with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI).
|
||||
However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust
|
||||
slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen:
|
||||
|
||||
```no_run
|
||||
// As we pass a slice to the function whose contents will not ever change,
|
||||
// the compiler writes it into the flash and thus the pointer to it will
|
||||
// reference static memory. Since EasyDMA requires slices to reside in RAM,
|
||||
// this function call will fail.
|
||||
let result = spim.write_from_ram(&[1, 2, 3]);
|
||||
assert_eq!(result, Err(Error::BufferNotInRAM));
|
||||
|
||||
// The data is still static and located in flash. However, since we are assigning
|
||||
// it to a variable, the compiler will load it into memory. Passing a reference to the
|
||||
// variable will yield a pointer that references dynamic memory, thus making EasyDMA happy.
|
||||
// This function call succeeds.
|
||||
let data = [1, 2, 3];
|
||||
let result = spim.write_from_ram(&data);
|
||||
assert!(result.is_ok());
|
||||
```
|
||||
|
||||
Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions:
|
||||
- Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM.
|
||||
- Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission.
|
||||
|
||||
Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will
|
||||
fail and notify you, or the more convenient versions without the suffix which are potentially a little bit
|
||||
more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage
|
||||
as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840).
|
||||
|
||||
Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as
|
||||
mutable slices always reside in RAM.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## 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.
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Async buffered UART
|
||||
//! Async buffered UART driver.
|
||||
//!
|
||||
//! WARNING!!! The functionality provided here is intended to be used only
|
||||
//! in situations where hardware flow control are available i.e. CTS and RTS.
|
||||
@ -69,7 +69,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
|
||||
tx_waker: WakerRegistration,
|
||||
}
|
||||
|
||||
/// Interface to a UARTE instance
|
||||
/// Buffered UARTE driver.
|
||||
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
|
||||
inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>,
|
||||
}
|
||||
@ -199,6 +199,9 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Split the UART in reader and writer parts.
|
||||
///
|
||||
/// This allows reading and writing concurrently from independent tasks.
|
||||
pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) {
|
||||
(BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self })
|
||||
}
|
||||
@ -320,10 +323,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader part of the buffered UARTE driver.
|
||||
pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> {
|
||||
inner: &'u BufferedUarte<'d, U, T>,
|
||||
}
|
||||
|
||||
/// Writer part of the buffered UARTE driver.
|
||||
pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
|
||||
inner: &'u BufferedUarte<'d, U, T>,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// Peripheral Access Crate
|
||||
#[allow(unused_imports)]
|
||||
#[rustfmt::skip]
|
||||
pub mod pac {
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// Peripheral Access Crate
|
||||
#[allow(unused_imports)]
|
||||
#[rustfmt::skip]
|
||||
pub mod pac {
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// Peripheral Access Crate
|
||||
#[allow(unused_imports)]
|
||||
#[rustfmt::skip]
|
||||
pub mod pac {
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! General purpose input/output for nRF.
|
||||
//! General purpose input/output (GPIO) driver.
|
||||
#![macro_use]
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! GPIO task/event (GPIOTE) driver.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Context, Poll};
|
||||
@ -11,29 +13,38 @@ use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::ppi::{Event, Task};
|
||||
use crate::{interrupt, pac, peripherals};
|
||||
|
||||
pub const CHANNEL_COUNT: usize = 8;
|
||||
/// Amount of GPIOTE channels in the chip.
|
||||
const CHANNEL_COUNT: usize = 8;
|
||||
|
||||
#[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
|
||||
pub const PIN_COUNT: usize = 48;
|
||||
const PIN_COUNT: usize = 48;
|
||||
#[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))]
|
||||
pub const PIN_COUNT: usize = 32;
|
||||
const PIN_COUNT: usize = 32;
|
||||
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];
|
||||
static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT];
|
||||
|
||||
/// Polarity for listening to events for GPIOTE input channels.
|
||||
pub enum InputChannelPolarity {
|
||||
/// Don't listen for any pin changes.
|
||||
None,
|
||||
/// Listen for high to low changes.
|
||||
HiToLo,
|
||||
/// Listen for low to high changes.
|
||||
LoToHi,
|
||||
/// Listen for any change, either low to high or high to low.
|
||||
Toggle,
|
||||
}
|
||||
|
||||
/// Polarity of the `task out` operation.
|
||||
/// Polarity of the OUT task operation for GPIOTE output channels.
|
||||
pub enum OutputChannelPolarity {
|
||||
/// Set the pin high.
|
||||
Set,
|
||||
/// Set the pin low.
|
||||
Clear,
|
||||
/// Toggle the pin.
|
||||
Toggle,
|
||||
}
|
||||
|
||||
@ -162,6 +173,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> {
|
||||
}
|
||||
|
||||
impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
||||
/// Create a new GPIOTE input channel driver.
|
||||
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
|
||||
into_ref!(ch);
|
||||
|
||||
@ -188,6 +200,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
||||
InputChannel { ch, pin }
|
||||
}
|
||||
|
||||
/// Asynchronously wait for an event in this channel.
|
||||
pub async fn wait(&self) {
|
||||
let g = regs();
|
||||
let num = self.ch.number();
|
||||
@ -231,6 +244,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
|
||||
}
|
||||
|
||||
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
||||
/// Create a new GPIOTE output channel driver.
|
||||
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
|
||||
into_ref!(ch);
|
||||
let g = regs();
|
||||
@ -258,20 +272,20 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
||||
OutputChannel { ch, _pin: pin }
|
||||
}
|
||||
|
||||
/// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle).
|
||||
/// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle).
|
||||
pub fn out(&self) {
|
||||
let g = regs();
|
||||
g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
/// Triggers `task set` (set associated pin high).
|
||||
/// Triggers the SET task (set associated pin high).
|
||||
#[cfg(not(feature = "nrf51"))]
|
||||
pub fn set(&self) {
|
||||
let g = regs();
|
||||
g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
/// Triggers `task clear` (set associated pin low).
|
||||
/// Triggers the CLEAR task (set associated pin low).
|
||||
#[cfg(not(feature = "nrf51"))]
|
||||
pub fn clear(&self) {
|
||||
let g = regs();
|
||||
@ -336,48 +350,58 @@ impl<'a> Future for PortInputFuture<'a> {
|
||||
}
|
||||
|
||||
impl<'d, T: GpioPin> Input<'d, T> {
|
||||
/// Wait until the pin is high. If it is already high, return immediately.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
self.pin.wait_for_high().await
|
||||
}
|
||||
|
||||
/// Wait until the pin is low. If it is already low, return immediately.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
self.pin.wait_for_low().await
|
||||
}
|
||||
|
||||
/// Wait for the pin to undergo a transition from low to high.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
self.pin.wait_for_rising_edge().await
|
||||
}
|
||||
|
||||
/// Wait for the pin to undergo a transition from high to low.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
self.pin.wait_for_falling_edge().await
|
||||
}
|
||||
|
||||
/// Wait for the pin to undergo any transition, i.e low to high OR high to low.
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
self.pin.wait_for_any_edge().await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: GpioPin> Flex<'d, T> {
|
||||
/// Wait until the pin is high. If it is already high, return immediately.
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
self.pin.conf().modify(|_, w| w.sense().high());
|
||||
PortInputFuture::new(&mut self.pin).await
|
||||
}
|
||||
|
||||
/// Wait until the pin is low. If it is already low, return immediately.
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
self.pin.conf().modify(|_, w| w.sense().low());
|
||||
PortInputFuture::new(&mut self.pin).await
|
||||
}
|
||||
|
||||
/// Wait for the pin to undergo a transition from low to high.
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
self.wait_for_low().await;
|
||||
self.wait_for_high().await;
|
||||
}
|
||||
|
||||
/// Wait for the pin to undergo a transition from high to low.
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
self.wait_for_high().await;
|
||||
self.wait_for_low().await;
|
||||
}
|
||||
|
||||
/// Wait for the pin to undergo any transition, i.e low to high OR high to low.
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
if self.is_high() {
|
||||
self.pin.conf().modify(|_, w| w.sense().low());
|
||||
@ -394,8 +418,17 @@ mod sealed {
|
||||
pub trait Channel {}
|
||||
}
|
||||
|
||||
/// GPIOTE channel trait.
|
||||
///
|
||||
/// Implemented by all GPIOTE channels.
|
||||
pub trait Channel: sealed::Channel + Sized {
|
||||
/// Get the channel number.
|
||||
fn number(&self) -> usize;
|
||||
|
||||
/// Convert this channel to a type-erased `AnyChannel`.
|
||||
///
|
||||
/// This allows using several channels in situations that might require
|
||||
/// them to be the same type, like putting them in an array.
|
||||
fn degrade(self) -> AnyChannel {
|
||||
AnyChannel {
|
||||
number: self.number() as u8,
|
||||
@ -403,6 +436,12 @@ pub trait Channel: sealed::Channel + Sized {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type-erased channel.
|
||||
///
|
||||
/// Obtained by calling `Channel::degrade`.
|
||||
///
|
||||
/// This allows using several channels in situations that might require
|
||||
/// them to be the same type, like putting them in an array.
|
||||
pub struct AnyChannel {
|
||||
number: u8,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![macro_use]
|
||||
//! Inter-IC Sound (I2S) driver.
|
||||
|
||||
//! Support for I2S audio
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
@ -19,16 +19,23 @@ use crate::pac::i2s::RegisterBlock;
|
||||
use crate::util::{slice_in_ram_or, slice_ptr_parts};
|
||||
use crate::{Peripheral, EASY_DMA_SIZE};
|
||||
|
||||
/// Type alias for `MultiBuffering` with 2 buffers.
|
||||
pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>;
|
||||
|
||||
/// I2S transfer error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// The buffer is too long.
|
||||
BufferTooLong,
|
||||
/// The buffer is empty.
|
||||
BufferZeroLength,
|
||||
BufferNotInDataMemory,
|
||||
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||
BufferNotInRAM,
|
||||
/// The buffer address is not aligned.
|
||||
BufferMisaligned,
|
||||
/// The buffer length is not a multiple of the alignment.
|
||||
BufferLengthMisaligned,
|
||||
}
|
||||
|
||||
@ -36,34 +43,16 @@ pub enum Error {
|
||||
#[derive(Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// Sample width
|
||||
pub sample_width: SampleWidth,
|
||||
/// Alignment
|
||||
pub align: Align,
|
||||
/// Sample format
|
||||
pub format: Format,
|
||||
/// Channel configuration.
|
||||
pub channels: Channels,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn sample_width(mut self, sample_width: SampleWidth) -> Self {
|
||||
self.sample_width = sample_width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn align(mut self, align: Align) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn format(mut self, format: Format) -> Self {
|
||||
self.format = format;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn channels(mut self, channels: Channels) -> Self {
|
||||
self.channels = channels;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@ -75,7 +64,7 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// I2S Mode
|
||||
/// I2S clock configuration.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub struct MasterClock {
|
||||
freq: MckFreq,
|
||||
@ -83,12 +72,14 @@ pub struct MasterClock {
|
||||
}
|
||||
|
||||
impl MasterClock {
|
||||
/// Create a new `MasterClock`.
|
||||
pub fn new(freq: MckFreq, ratio: Ratio) -> Self {
|
||||
Self { freq, ratio }
|
||||
}
|
||||
}
|
||||
|
||||
impl MasterClock {
|
||||
/// Get the sample rate for this clock configuration.
|
||||
pub fn sample_rate(&self) -> u32 {
|
||||
self.freq.to_frequency() / self.ratio.to_divisor()
|
||||
}
|
||||
@ -97,18 +88,31 @@ impl MasterClock {
|
||||
/// Master clock generator frequency.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum MckFreq {
|
||||
/// 32 Mhz / 8 = 4000.00 kHz
|
||||
_32MDiv8,
|
||||
/// 32 Mhz / 10 = 3200.00 kHz
|
||||
_32MDiv10,
|
||||
/// 32 Mhz / 11 = 2909.09 kHz
|
||||
_32MDiv11,
|
||||
/// 32 Mhz / 15 = 2133.33 kHz
|
||||
_32MDiv15,
|
||||
/// 32 Mhz / 16 = 2000.00 kHz
|
||||
_32MDiv16,
|
||||
/// 32 Mhz / 21 = 1523.81 kHz
|
||||
_32MDiv21,
|
||||
/// 32 Mhz / 23 = 1391.30 kHz
|
||||
_32MDiv23,
|
||||
/// 32 Mhz / 30 = 1066.67 kHz
|
||||
_32MDiv30,
|
||||
/// 32 Mhz / 31 = 1032.26 kHz
|
||||
_32MDiv31,
|
||||
/// 32 Mhz / 32 = 1000.00 kHz
|
||||
_32MDiv32,
|
||||
/// 32 Mhz / 42 = 761.90 kHz
|
||||
_32MDiv42,
|
||||
/// 32 Mhz / 63 = 507.94 kHz
|
||||
_32MDiv63,
|
||||
/// 32 Mhz / 125 = 256.00 kHz
|
||||
_32MDiv125,
|
||||
}
|
||||
|
||||
@ -146,14 +150,23 @@ impl From<MckFreq> for usize {
|
||||
///
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Ratio {
|
||||
/// Divide by 32
|
||||
_32x,
|
||||
/// Divide by 48
|
||||
_48x,
|
||||
/// Divide by 64
|
||||
_64x,
|
||||
/// Divide by 96
|
||||
_96x,
|
||||
/// Divide by 128
|
||||
_128x,
|
||||
/// Divide by 192
|
||||
_192x,
|
||||
/// Divide by 256
|
||||
_256x,
|
||||
/// Divide by 384
|
||||
_384x,
|
||||
/// Divide by 512
|
||||
_512x,
|
||||
}
|
||||
|
||||
@ -165,6 +178,7 @@ impl Ratio {
|
||||
usize::from(*self) as u8
|
||||
}
|
||||
|
||||
/// Return the divisor for this ratio
|
||||
pub fn to_divisor(&self) -> u32 {
|
||||
Self::RATIOS[usize::from(*self)]
|
||||
}
|
||||
@ -183,11 +197,17 @@ impl From<Ratio> for usize {
|
||||
/// For custom master clock configuration, please refer to [MasterClock].
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ApproxSampleRate {
|
||||
/// 11025 Hz
|
||||
_11025,
|
||||
/// 16000 Hz
|
||||
_16000,
|
||||
/// 22050 Hz
|
||||
_22050,
|
||||
/// 32000 Hz
|
||||
_32000,
|
||||
/// 44100 Hz
|
||||
_44100,
|
||||
/// 48000 Hz
|
||||
_48000,
|
||||
}
|
||||
|
||||
@ -211,6 +231,7 @@ impl From<ApproxSampleRate> for MasterClock {
|
||||
}
|
||||
|
||||
impl ApproxSampleRate {
|
||||
/// Get the sample rate as an integer.
|
||||
pub fn sample_rate(&self) -> u32 {
|
||||
MasterClock::from(*self).sample_rate()
|
||||
}
|
||||
@ -223,20 +244,32 @@ impl ApproxSampleRate {
|
||||
/// For custom master clock configuration, please refer to [Mode].
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ExactSampleRate {
|
||||
/// 8000 Hz
|
||||
_8000,
|
||||
/// 10582 Hz
|
||||
_10582,
|
||||
/// 12500 Hz
|
||||
_12500,
|
||||
/// 15625 Hz
|
||||
_15625,
|
||||
/// 15873 Hz
|
||||
_15873,
|
||||
/// 25000 Hz
|
||||
_25000,
|
||||
/// 31250 Hz
|
||||
_31250,
|
||||
/// 50000 Hz
|
||||
_50000,
|
||||
/// 62500 Hz
|
||||
_62500,
|
||||
/// 100000 Hz
|
||||
_100000,
|
||||
/// 125000 Hz
|
||||
_125000,
|
||||
}
|
||||
|
||||
impl ExactSampleRate {
|
||||
/// Get the sample rate as an integer.
|
||||
pub fn sample_rate(&self) -> u32 {
|
||||
MasterClock::from(*self).sample_rate()
|
||||
}
|
||||
@ -263,8 +296,11 @@ impl From<ExactSampleRate> for MasterClock {
|
||||
/// Sample width.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum SampleWidth {
|
||||
/// 8 bit samples.
|
||||
_8bit,
|
||||
/// 16 bit samples.
|
||||
_16bit,
|
||||
/// 24 bit samples.
|
||||
_24bit,
|
||||
}
|
||||
|
||||
@ -277,7 +313,9 @@ impl From<SampleWidth> for u8 {
|
||||
/// Channel used for the most significant sample value in a frame.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Align {
|
||||
/// Left-align samples.
|
||||
Left,
|
||||
/// Right-align samples.
|
||||
Right,
|
||||
}
|
||||
|
||||
@ -293,7 +331,9 @@ impl From<Align> for bool {
|
||||
/// Frame format.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Format {
|
||||
/// I2S frame format
|
||||
I2S,
|
||||
/// Aligned frame format
|
||||
Aligned,
|
||||
}
|
||||
|
||||
@ -309,8 +349,11 @@ impl From<Format> for bool {
|
||||
/// Channels
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Channels {
|
||||
/// Stereo (2 channels).
|
||||
Stereo,
|
||||
/// Mono, left channel only.
|
||||
MonoLeft,
|
||||
/// Mono, right channel only.
|
||||
MonoRight,
|
||||
}
|
||||
|
||||
@ -320,7 +363,7 @@ impl From<Channels> for u8 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload.
|
||||
/// I2S driver.
|
||||
pub struct I2S<'d, T: Instance> {
|
||||
i2s: PeripheralRef<'d, T>,
|
||||
irq: PeripheralRef<'d, T::Interrupt>,
|
||||
@ -566,7 +609,7 @@ impl<'d, T: Instance> I2S<'d, T> {
|
||||
{
|
||||
trace!("SEND: {}", buffer_ptr as *const S as u32);
|
||||
|
||||
slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?;
|
||||
slice_in_ram_or(buffer_ptr, Error::BufferNotInRAM)?;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
@ -1003,7 +1046,10 @@ impl<T: Instance> Device<T> {
|
||||
|
||||
/// Sample details
|
||||
pub trait Sample: Sized + Copy + Default {
|
||||
/// Width of this sample type.
|
||||
const WIDTH: usize;
|
||||
|
||||
/// Scale of this sample.
|
||||
const SCALE: Self;
|
||||
}
|
||||
|
||||
@ -1022,12 +1068,13 @@ impl Sample for i32 {
|
||||
const SCALE: Self = 1 << (Self::WIDTH - 1);
|
||||
}
|
||||
|
||||
/// A 4-bytes aligned buffer.
|
||||
/// A 4-bytes aligned buffer. Needed for DMA access.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(align(4))]
|
||||
pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]);
|
||||
|
||||
impl<T: Sample, const N: usize> AlignedBuffer<T, N> {
|
||||
/// Create a new `AlignedBuffer`.
|
||||
pub fn new(array: [T; N]) -> Self {
|
||||
Self(array)
|
||||
}
|
||||
@ -1052,12 +1099,14 @@ impl<T: Sample, const N: usize> DerefMut for AlignedBuffer<T, N> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of multiple buffers, for multi-buffering transfers.
|
||||
pub struct MultiBuffering<S: Sample, const NB: usize, const NS: usize> {
|
||||
buffers: [AlignedBuffer<S, NS>; NB],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<S: Sample, const NB: usize, const NS: usize> MultiBuffering<S, NB, NS> {
|
||||
/// Create a new `MultiBuffering`.
|
||||
pub fn new() -> Self {
|
||||
assert!(NB > 1);
|
||||
Self {
|
||||
@ -1119,7 +1168,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// I2S peripehral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
|
@ -1,53 +1,11 @@
|
||||
//! # Embassy nRF HAL
|
||||
//!
|
||||
//! HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed.
|
||||
//!
|
||||
//! The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs
|
||||
//! for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to
|
||||
//! complete operations in low power mod and handling interrupts, so that applications can focus on more important matters.
|
||||
//!
|
||||
//! ## EasyDMA considerations
|
||||
//!
|
||||
//! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting
|
||||
//! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI).
|
||||
//! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust
|
||||
//! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // As we pass a slice to the function whose contents will not ever change,
|
||||
//! // the compiler writes it into the flash and thus the pointer to it will
|
||||
//! // reference static memory. Since EasyDMA requires slices to reside in RAM,
|
||||
//! // this function call will fail.
|
||||
//! let result = spim.write_from_ram(&[1, 2, 3]);
|
||||
//! assert_eq!(result, Err(Error::DMABufferNotInDataMemory));
|
||||
//!
|
||||
//! // The data is still static and located in flash. However, since we are assigning
|
||||
//! // it to a variable, the compiler will load it into memory. Passing a reference to the
|
||||
//! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy.
|
||||
//! // This function call succeeds.
|
||||
//! let data = [1, 2, 3];
|
||||
//! let result = spim.write_from_ram(&data);
|
||||
//! assert!(result.is_ok());
|
||||
//! ```
|
||||
//!
|
||||
//! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions:
|
||||
//! - Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM.
|
||||
//! - Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission.
|
||||
//!
|
||||
//! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will
|
||||
//! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit
|
||||
//! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage
|
||||
//! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840).
|
||||
//!
|
||||
//! Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as
|
||||
//! mutable slices always reside in RAM.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(
|
||||
feature = "nightly",
|
||||
feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections)
|
||||
)]
|
||||
#![cfg_attr(feature = "nightly", allow(incomplete_features))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "nrf51",
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Non-Volatile Memory Controller (NVMC) module.
|
||||
//! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver.
|
||||
|
||||
use core::{ptr, slice};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! PDM mirophone interface
|
||||
//! Pulse Density Modulation (PDM) mirophone driver.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
@ -22,12 +22,16 @@ pub struct Pdm<'d> {
|
||||
phantom: PhantomData<&'d PDM>,
|
||||
}
|
||||
|
||||
/// PDM error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Buffer is too long.
|
||||
BufferTooLong,
|
||||
/// Buffer is empty
|
||||
BufferZeroLength,
|
||||
/// PDM is not running
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
@ -119,6 +123,7 @@ impl<'d> Pdm<'d> {
|
||||
r.events_started.reset();
|
||||
}
|
||||
|
||||
/// Sample data into the given buffer.
|
||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
@ -215,14 +220,21 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM operation mode.
|
||||
#[derive(PartialEq)]
|
||||
pub enum OperationMode {
|
||||
/// Mono (1 channel)
|
||||
Mono,
|
||||
/// Stereo (2 channels)
|
||||
Stereo,
|
||||
}
|
||||
|
||||
/// PDM edge polarity
|
||||
#[derive(PartialEq)]
|
||||
pub enum Edge {
|
||||
/// Left edge is rising
|
||||
LeftRising,
|
||||
/// Left edge is falling
|
||||
LeftFalling,
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,14 @@ fn regs() -> &'static pac::dppic::RegisterBlock {
|
||||
}
|
||||
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||
/// Configure PPI channel to trigger `task` on `event`.
|
||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
|
||||
Ppi::new_many_to_many(ch, [event], [task])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
|
||||
Ppi::new_many_to_many(ch, [event], [task1, task2])
|
||||
}
|
||||
@ -25,6 +27,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||
impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
|
||||
Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
|
||||
{
|
||||
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
|
||||
pub fn new_many_to_many(
|
||||
ch: impl Peripheral<P = C> + 'd,
|
||||
events: [Event; EVENT_COUNT],
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
//! HAL interface for the PPI and DPPI peripheral.
|
||||
//! Programmable Peripheral Interconnect (PPI/DPPI) driver.
|
||||
//!
|
||||
//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability
|
||||
//! between peripherals through their events and tasks. There are fixed PPI channels and fully
|
||||
|
@ -48,7 +48,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||
|
||||
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||
/// Configure PPI channel to trigger `task1` and `task2` on `event`.
|
||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
|
||||
into_ref!(ch);
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Pulse Width Modulation (PWM) driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
@ -32,6 +34,7 @@ pub struct SequencePwm<'d, T: Instance> {
|
||||
ch3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
}
|
||||
|
||||
/// PWM error
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
@ -41,7 +44,7 @@ pub enum Error {
|
||||
/// Min Sequence count is 1
|
||||
SequenceTimesAtLeastOne,
|
||||
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||
DMABufferNotInDataMemory,
|
||||
BufferNotInRAM,
|
||||
}
|
||||
|
||||
const MAX_SEQUENCE_LEN: usize = 32767;
|
||||
@ -358,6 +361,7 @@ pub struct Sequence<'s> {
|
||||
}
|
||||
|
||||
impl<'s> Sequence<'s> {
|
||||
/// Create a new `Sequence`
|
||||
pub fn new(words: &'s [u16], config: SequenceConfig) -> Self {
|
||||
Self { words, config }
|
||||
}
|
||||
@ -367,7 +371,7 @@ impl<'s> Sequence<'s> {
|
||||
/// Takes at one sequence along with its configuration.
|
||||
#[non_exhaustive]
|
||||
pub struct SingleSequencer<'d, 's, T: Instance> {
|
||||
pub sequencer: Sequencer<'d, 's, T>,
|
||||
sequencer: Sequencer<'d, 's, T>,
|
||||
}
|
||||
|
||||
impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> {
|
||||
@ -428,8 +432,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> {
|
||||
let sequence0 = &self.sequence0;
|
||||
let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0);
|
||||
|
||||
slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?;
|
||||
slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?;
|
||||
|
||||
if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN {
|
||||
return Err(Error::SequenceTooLong);
|
||||
@ -536,13 +540,21 @@ pub enum SequenceMode {
|
||||
/// PWM Base clock is system clock (16MHz) divided by prescaler
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum Prescaler {
|
||||
/// Divide by 1
|
||||
Div1,
|
||||
/// Divide by 2
|
||||
Div2,
|
||||
/// Divide by 4
|
||||
Div4,
|
||||
/// Divide by 8
|
||||
Div8,
|
||||
/// Divide by 16
|
||||
Div16,
|
||||
/// Divide by 32
|
||||
Div32,
|
||||
/// Divide by 64
|
||||
Div64,
|
||||
/// Divide by 128
|
||||
Div128,
|
||||
}
|
||||
|
||||
@ -828,7 +840,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// PWM peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Quadrature decoder interface
|
||||
//! Quadrature decoder (QDEC) driver.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
@ -12,17 +12,23 @@ use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::QDEC;
|
||||
use crate::{interrupt, pac, Peripheral};
|
||||
|
||||
/// Quadrature decoder
|
||||
/// Quadrature decoder driver.
|
||||
pub struct Qdec<'d> {
|
||||
_p: PeripheralRef<'d, QDEC>,
|
||||
}
|
||||
|
||||
/// QDEC config
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// Number of samples
|
||||
pub num_samples: NumSamples,
|
||||
/// Sample period
|
||||
pub period: SamplePeriod,
|
||||
/// Set LED output pin polarity
|
||||
pub led_polarity: LedPolarity,
|
||||
/// Enable/disable input debounce filters
|
||||
pub debounce: bool,
|
||||
/// Time period the LED is switched ON prior to sampling (0..511 us).
|
||||
pub led_pre_usecs: u16,
|
||||
}
|
||||
|
||||
@ -41,6 +47,7 @@ impl Default for Config {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
impl<'d> Qdec<'d> {
|
||||
/// Create a new QDEC.
|
||||
pub fn new(
|
||||
qdec: impl Peripheral<P = QDEC> + 'd,
|
||||
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
|
||||
@ -52,6 +59,7 @@ impl<'d> Qdec<'d> {
|
||||
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config)
|
||||
}
|
||||
|
||||
/// Create a new QDEC, with a pin for LED output.
|
||||
pub fn new_with_led(
|
||||
qdec: impl Peripheral<P = QDEC> + 'd,
|
||||
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
|
||||
@ -170,36 +178,61 @@ impl<'d> Qdec<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample period
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum SamplePeriod {
|
||||
/// 128 us
|
||||
_128us,
|
||||
/// 256 us
|
||||
_256us,
|
||||
/// 512 us
|
||||
_512us,
|
||||
/// 1024 us
|
||||
_1024us,
|
||||
/// 2048 us
|
||||
_2048us,
|
||||
/// 4096 us
|
||||
_4096us,
|
||||
/// 8192 us
|
||||
_8192us,
|
||||
/// 16384 us
|
||||
_16384us,
|
||||
/// 32 ms
|
||||
_32ms,
|
||||
/// 65 ms
|
||||
_65ms,
|
||||
/// 131 ms
|
||||
_131ms,
|
||||
}
|
||||
|
||||
/// Number of samples taken.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum NumSamples {
|
||||
/// 10 samples
|
||||
_10smpl,
|
||||
/// 40 samples
|
||||
_40smpl,
|
||||
/// 80 samples
|
||||
_80smpl,
|
||||
/// 120 samples
|
||||
_120smpl,
|
||||
/// 160 samples
|
||||
_160smpl,
|
||||
/// 200 samples
|
||||
_200smpl,
|
||||
/// 240 samples
|
||||
_240smpl,
|
||||
/// 280 samples
|
||||
_280smpl,
|
||||
/// 1 sample
|
||||
_1smpl,
|
||||
}
|
||||
|
||||
/// LED polarity
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum LedPolarity {
|
||||
/// Active high (a high output turns on the LED).
|
||||
ActiveHigh,
|
||||
/// Active low (a low output turns on the LED).
|
||||
ActiveLow,
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Quad Serial Peripheral Interface (QSPI) flash driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
@ -15,6 +17,7 @@ pub use crate::pac::qspi::ifconfig0::{
|
||||
pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode;
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// Deep power-down config.
|
||||
pub struct DeepPowerDownConfig {
|
||||
/// Time required for entering DPM, in units of 16us
|
||||
pub enter_time: u16,
|
||||
@ -22,37 +25,62 @@ pub struct DeepPowerDownConfig {
|
||||
pub exit_time: u16,
|
||||
}
|
||||
|
||||
/// QSPI bus frequency.
|
||||
pub enum Frequency {
|
||||
/// 32 Mhz
|
||||
M32 = 0,
|
||||
/// 16 Mhz
|
||||
M16 = 1,
|
||||
/// 10.7 Mhz
|
||||
M10_7 = 2,
|
||||
/// 8 Mhz
|
||||
M8 = 3,
|
||||
/// 6.4 Mhz
|
||||
M6_4 = 4,
|
||||
/// 5.3 Mhz
|
||||
M5_3 = 5,
|
||||
/// 4.6 Mhz
|
||||
M4_6 = 6,
|
||||
/// 4 Mhz
|
||||
M4 = 7,
|
||||
/// 3.6 Mhz
|
||||
M3_6 = 8,
|
||||
/// 3.2 Mhz
|
||||
M3_2 = 9,
|
||||
/// 2.9 Mhz
|
||||
M2_9 = 10,
|
||||
/// 2.7 Mhz
|
||||
M2_7 = 11,
|
||||
/// 2.5 Mhz
|
||||
M2_5 = 12,
|
||||
/// 2.3 Mhz
|
||||
M2_3 = 13,
|
||||
/// 2.1 Mhz
|
||||
M2_1 = 14,
|
||||
/// 2 Mhz
|
||||
M2 = 15,
|
||||
}
|
||||
|
||||
/// QSPI config.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// XIP offset.
|
||||
pub xip_offset: u32,
|
||||
/// Opcode used for read operations.
|
||||
pub read_opcode: ReadOpcode,
|
||||
/// Opcode used for write operations.
|
||||
pub write_opcode: WriteOpcode,
|
||||
/// Page size for write operations.
|
||||
pub write_page_size: WritePageSize,
|
||||
/// Configuration for deep power down. If None, deep power down is disabled.
|
||||
pub deep_power_down: Option<DeepPowerDownConfig>,
|
||||
/// QSPI bus frequency.
|
||||
pub frequency: Frequency,
|
||||
/// Value is specified in number of 16 MHz periods (62.5 ns)
|
||||
pub sck_delay: u8,
|
||||
/// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3)
|
||||
pub spi_mode: SpiMode,
|
||||
/// Addressing mode (24-bit or 32-bit)
|
||||
pub address_mode: AddressMode,
|
||||
}
|
||||
|
||||
@ -72,20 +100,24 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Operation address was out of bounds.
|
||||
OutOfBounds,
|
||||
// TODO add "not in data memory" error and check for it
|
||||
}
|
||||
|
||||
/// QSPI flash driver.
|
||||
pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> {
|
||||
irq: PeripheralRef<'d, T::Interrupt>,
|
||||
dpm_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
/// Create a new QSPI driver.
|
||||
pub fn new(
|
||||
_qspi: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -183,6 +215,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Do a custom QSPI instruction.
|
||||
pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> {
|
||||
let bomb = DropBomb::new();
|
||||
|
||||
@ -198,6 +231,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Do a custom QSPI instruction, blocking version.
|
||||
pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> {
|
||||
let len = core::cmp::max(req.len(), resp.len()) as u8;
|
||||
self.custom_instruction_start(opcode, req, len)?;
|
||||
@ -346,6 +380,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read data from the flash memory.
|
||||
pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
|
||||
let bomb = DropBomb::new();
|
||||
|
||||
@ -357,6 +392,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write data to the flash memory.
|
||||
pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
|
||||
let bomb = DropBomb::new();
|
||||
|
||||
@ -368,6 +404,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Erase a sector on the flash memory.
|
||||
pub async fn erase(&mut self, address: usize) -> Result<(), Error> {
|
||||
let bomb = DropBomb::new();
|
||||
|
||||
@ -379,18 +416,21 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read data from the flash memory, blocking version.
|
||||
pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
|
||||
self.start_read(address, data)?;
|
||||
self.blocking_wait_ready();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write data to the flash memory, blocking version.
|
||||
pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
|
||||
self.start_write(address, data)?;
|
||||
self.blocking_wait_ready();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Erase a sector on the flash memory, blocking version.
|
||||
pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> {
|
||||
self.start_erase(address)?;
|
||||
self.blocking_wait_ready();
|
||||
@ -547,7 +587,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// QSPI peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Random Number Generator (RNG) driver.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
@ -128,10 +130,11 @@ impl<'d> Rng<'d> {
|
||||
/// However, this makes the generation of numbers slower.
|
||||
///
|
||||
/// Defaults to disabled.
|
||||
pub fn bias_correction(&self, enable: bool) {
|
||||
pub fn set_bias_correction(&self, enable: bool) {
|
||||
RNG::regs().config.write(|w| w.dercen().bit(enable))
|
||||
}
|
||||
|
||||
/// Fill the buffer with random bytes.
|
||||
pub async fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
if dest.len() == 0 {
|
||||
return; // Nothing to fill
|
||||
@ -175,6 +178,7 @@ impl<'d> Rng<'d> {
|
||||
drop(on_drop);
|
||||
}
|
||||
|
||||
/// Fill the buffer with random bytes, blocking version.
|
||||
pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.start();
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Successive Approximation Analog-to-Digital Converter (SAADC) driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
@ -20,6 +22,7 @@ use crate::ppi::{ConfigurableChannel, Event, Ppi, Task};
|
||||
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
|
||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
|
||||
/// SAADC error
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
@ -102,17 +105,17 @@ impl<'d> ChannelConfig<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of a continuously running sampler. While it reflects
|
||||
/// the progress of a sampler, it also signals what should be done
|
||||
/// next. For example, if the sampler has stopped then the Saadc implementation
|
||||
/// can then tear down its infrastructure.
|
||||
/// Value returned by the SAADC callback, deciding what happens next.
|
||||
#[derive(PartialEq)]
|
||||
pub enum SamplerState {
|
||||
Sampled,
|
||||
Stopped,
|
||||
pub enum CallbackResult {
|
||||
/// The SAADC should keep sampling and calling the callback.
|
||||
Continue,
|
||||
/// The SAADC should stop sampling, and return.
|
||||
Stop,
|
||||
}
|
||||
|
||||
impl<'d, const N: usize> Saadc<'d, N> {
|
||||
/// Create a new SAADC driver.
|
||||
pub fn new(
|
||||
saadc: impl Peripheral<P = peripherals::SAADC> + 'd,
|
||||
irq: impl Peripheral<P = interrupt::SAADC> + 'd,
|
||||
@ -285,7 +288,7 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
/// free the buffers from being used by the peripheral. Cancellation will
|
||||
/// also cause the sampling to be stopped.
|
||||
|
||||
pub async fn run_task_sampler<S, T: TimerInstance, const N0: usize>(
|
||||
pub async fn run_task_sampler<F, T: TimerInstance, const N0: usize>(
|
||||
&mut self,
|
||||
timer: &mut T,
|
||||
ppi_ch1: &mut impl ConfigurableChannel,
|
||||
@ -293,9 +296,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
frequency: Frequency,
|
||||
sample_counter: u32,
|
||||
bufs: &mut [[[i16; N]; N0]; 2],
|
||||
sampler: S,
|
||||
callback: F,
|
||||
) where
|
||||
S: FnMut(&[[i16; N]]) -> SamplerState,
|
||||
F: FnMut(&[[i16; N]]) -> CallbackResult,
|
||||
{
|
||||
let r = Self::regs();
|
||||
|
||||
@ -321,20 +324,20 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
|| {
|
||||
sample_ppi.enable();
|
||||
},
|
||||
sampler,
|
||||
callback,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn run_sampler<I, S, const N0: usize>(
|
||||
async fn run_sampler<I, F, const N0: usize>(
|
||||
&mut self,
|
||||
bufs: &mut [[[i16; N]; N0]; 2],
|
||||
sample_rate_divisor: Option<u16>,
|
||||
mut init: I,
|
||||
mut sampler: S,
|
||||
mut callback: F,
|
||||
) where
|
||||
I: FnMut(),
|
||||
S: FnMut(&[[i16; N]]) -> SamplerState,
|
||||
F: FnMut(&[[i16; N]]) -> CallbackResult,
|
||||
{
|
||||
// In case the future is dropped, stop the task and wait for it to end.
|
||||
let on_drop = OnDrop::new(Self::stop_sampling_immediately);
|
||||
@ -395,12 +398,15 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
r.events_end.reset();
|
||||
r.intenset.write(|w| w.end().set());
|
||||
|
||||
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
|
||||
let next_buffer = 1 - current_buffer;
|
||||
current_buffer = next_buffer;
|
||||
} else {
|
||||
return Poll::Ready(());
|
||||
};
|
||||
match callback(&bufs[current_buffer]) {
|
||||
CallbackResult::Continue => {
|
||||
let next_buffer = 1 - current_buffer;
|
||||
current_buffer = next_buffer;
|
||||
}
|
||||
CallbackResult::Stop => {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.events_started.read().bits() != 0 {
|
||||
@ -458,7 +464,7 @@ impl<'d> Saadc<'d, 1> {
|
||||
sample_rate_divisor: u16,
|
||||
sampler: S,
|
||||
) where
|
||||
S: FnMut(&[[i16; 1]]) -> SamplerState,
|
||||
S: FnMut(&[[i16; 1]]) -> CallbackResult,
|
||||
{
|
||||
self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await;
|
||||
}
|
||||
@ -658,6 +664,10 @@ pub(crate) mod sealed {
|
||||
|
||||
/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.
|
||||
pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized + 'static {
|
||||
/// Convert this SAADC input to a type-erased `AnyInput`.
|
||||
///
|
||||
/// This allows using several inputs in situations that might require
|
||||
/// them to be the same type, like putting them in an array.
|
||||
fn degrade_saadc(self) -> AnyInput {
|
||||
AnyInput {
|
||||
channel: self.channel(),
|
||||
@ -665,6 +675,10 @@ pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized +
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-erased SAADC input.
|
||||
///
|
||||
/// This allows using several inputs in situations that might require
|
||||
/// them to be the same type, like putting them in an array.
|
||||
pub struct AnyInput {
|
||||
channel: InputChannel,
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Serial Peripheral Instance in master mode (SPIM) driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
@ -16,27 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// SPIM error
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// TX buffer was too long.
|
||||
TxBufferTooLong,
|
||||
/// RX buffer was too long.
|
||||
RxBufferTooLong,
|
||||
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||
DMABufferNotInDataMemory,
|
||||
BufferNotInRAM,
|
||||
}
|
||||
|
||||
/// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload.
|
||||
///
|
||||
/// For more details about EasyDMA, consult the module documentation.
|
||||
/// SPIM driver.
|
||||
pub struct Spim<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// SPIM configuration.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// Frequency
|
||||
pub frequency: Frequency,
|
||||
|
||||
/// SPI mode
|
||||
pub mode: Mode,
|
||||
|
||||
/// Overread character.
|
||||
///
|
||||
/// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer,
|
||||
/// this byte will be transmitted in the MOSI line for the left-over bytes.
|
||||
pub orc: u8,
|
||||
}
|
||||
|
||||
@ -51,6 +63,7 @@ impl Default for Config {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Spim<'d, T> {
|
||||
/// Create a new SPIM driver.
|
||||
pub fn new(
|
||||
spim: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -70,6 +83,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new SPIM driver, capable of TX only (MOSI only).
|
||||
pub fn new_txonly(
|
||||
spim: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -81,6 +95,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config)
|
||||
}
|
||||
|
||||
/// Create a new SPIM driver, capable of RX only (MISO only).
|
||||
pub fn new_rxonly(
|
||||
spim: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -194,7 +209,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
}
|
||||
|
||||
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
|
||||
// NOTE: RAM slice check for rx is not necessary, as a mutable
|
||||
// slice can only be built from data located in RAM.
|
||||
|
||||
@ -236,7 +251,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
|
||||
match self.blocking_inner_from_ram(rx, tx) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying SPIM tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
|
||||
tx_ram_buf.copy_from_slice(tx);
|
||||
@ -268,7 +283,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
|
||||
match self.async_inner_from_ram(rx, tx).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying SPIM tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
|
||||
tx_ram_buf.copy_from_slice(tx);
|
||||
@ -385,7 +400,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// SPIM peripheral instance
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
@ -437,7 +454,7 @@ mod eh1 {
|
||||
match *self {
|
||||
Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
|
||||
Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
|
||||
Self::DMABufferNotInDataMemory => embedded_hal_1::spi::ErrorKind::Other,
|
||||
Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Serial Peripheral Instance in slave mode (SPIS) driver.
|
||||
|
||||
#![macro_use]
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
@ -14,28 +16,43 @@ use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// SPIS error
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// TX buffer was too long.
|
||||
TxBufferTooLong,
|
||||
/// RX buffer was too long.
|
||||
RxBufferTooLong,
|
||||
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||
DMABufferNotInDataMemory,
|
||||
BufferNotInRAM,
|
||||
}
|
||||
|
||||
/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload.
|
||||
///
|
||||
/// For more details about EasyDMA, consult the module documentation.
|
||||
/// SPIS driver.
|
||||
pub struct Spis<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// SPIS configuration.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// SPI mode
|
||||
pub mode: Mode,
|
||||
|
||||
/// Overread character.
|
||||
///
|
||||
/// If the master keeps clocking the bus after all the bytes in the TX buffer have
|
||||
/// already been transmitted, this byte will be constantly transmitted in the MISO line.
|
||||
pub orc: u8,
|
||||
|
||||
/// Default byte.
|
||||
///
|
||||
/// This is the byte clocked out in the MISO line for ignored transactions (if the master
|
||||
/// sets CSN low while the semaphore is owned by the firmware)
|
||||
pub def: u8,
|
||||
|
||||
/// Automatically make the firmware side acquire the semaphore on transfer end.
|
||||
pub auto_acquire: bool,
|
||||
}
|
||||
|
||||
@ -51,6 +68,7 @@ impl Default for Config {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Spis<'d, T> {
|
||||
/// Create a new SPIS driver.
|
||||
pub fn new(
|
||||
spis: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -72,6 +90,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new SPIS driver, capable of TX only (MISO only).
|
||||
pub fn new_txonly(
|
||||
spis: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -92,6 +111,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new SPIS driver, capable of RX only (MOSI only).
|
||||
pub fn new_rxonly(
|
||||
spis: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -212,7 +232,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
}
|
||||
|
||||
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
|
||||
// NOTE: RAM slice check for rx is not necessary, as a mutable
|
||||
// slice can only be built from data located in RAM.
|
||||
|
||||
@ -267,7 +287,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
|
||||
match self.blocking_inner_from_ram(rx, tx) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying SPIS tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
|
||||
tx_ram_buf.copy_from_slice(tx);
|
||||
@ -330,7 +350,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
|
||||
match self.async_inner_from_ram(rx, tx).await {
|
||||
Ok(n) => Ok(n),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying SPIS tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
|
||||
tx_ram_buf.copy_from_slice(tx);
|
||||
@ -468,7 +488,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// SPIS peripheral instance
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Temperature sensor interface.
|
||||
//! Builtin temperature sensor driver.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
@ -12,7 +12,7 @@ use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::TEMP;
|
||||
use crate::{interrupt, pac, Peripheral};
|
||||
|
||||
/// Integrated temperature sensor.
|
||||
/// Builtin temperature sensor driver.
|
||||
pub struct Temp<'d> {
|
||||
_irq: PeripheralRef<'d, interrupt::TEMP>,
|
||||
}
|
||||
@ -20,6 +20,7 @@ pub struct Temp<'d> {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
impl<'d> Temp<'d> {
|
||||
/// Create a new temperature sensor driver.
|
||||
pub fn new(_t: impl Peripheral<P = TEMP> + 'd, irq: impl Peripheral<P = interrupt::TEMP> + 'd) -> Self {
|
||||
into_ref!(_t, irq);
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
//! Timer driver.
|
||||
//!
|
||||
//! Important note! This driver is very low level. For most time-related use cases, like
|
||||
//! "sleep for X seconds", "do something every X seconds", or measuring time, you should
|
||||
//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead!
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
@ -28,9 +34,13 @@ pub(crate) mod sealed {
|
||||
pub trait TimerType {}
|
||||
}
|
||||
|
||||
/// Basic Timer instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
/// Extended timer instance.
|
||||
pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {}
|
||||
|
||||
macro_rules! impl_timer {
|
||||
@ -61,18 +71,28 @@ macro_rules! impl_timer {
|
||||
};
|
||||
}
|
||||
|
||||
/// Timer frequency
|
||||
#[repr(u8)]
|
||||
pub enum Frequency {
|
||||
// I'd prefer not to prefix these with `F`, but Rust identifiers can't start with digits.
|
||||
/// 16MHz
|
||||
F16MHz = 0,
|
||||
/// 8MHz
|
||||
F8MHz = 1,
|
||||
/// 4MHz
|
||||
F4MHz = 2,
|
||||
/// 2MHz
|
||||
F2MHz = 3,
|
||||
/// 1MHz
|
||||
F1MHz = 4,
|
||||
/// 500kHz
|
||||
F500kHz = 5,
|
||||
/// 250kHz
|
||||
F250kHz = 6,
|
||||
/// 125kHz
|
||||
F125kHz = 7,
|
||||
/// 62500Hz
|
||||
F62500Hz = 8,
|
||||
/// 31250Hz
|
||||
F31250Hz = 9,
|
||||
}
|
||||
|
||||
@ -86,7 +106,10 @@ pub enum Frequency {
|
||||
|
||||
pub trait TimerType: sealed::TimerType {}
|
||||
|
||||
/// Marker type indicating the timer driver can await expiration (it owns the timer interrupt).
|
||||
pub enum Awaitable {}
|
||||
|
||||
/// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt).
|
||||
pub enum NotAwaitable {}
|
||||
|
||||
impl sealed::TimerType for Awaitable {}
|
||||
@ -94,12 +117,14 @@ impl sealed::TimerType for NotAwaitable {}
|
||||
impl TimerType for Awaitable {}
|
||||
impl TimerType for NotAwaitable {}
|
||||
|
||||
/// Timer driver.
|
||||
pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
_i: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Timer<'d, T, Awaitable> {
|
||||
/// Create a new async-capable timer driver.
|
||||
pub fn new_awaitable(timer: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd) -> Self {
|
||||
into_ref!(irq);
|
||||
|
||||
@ -107,16 +132,17 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> {
|
||||
irq.unpend();
|
||||
irq.enable();
|
||||
|
||||
Self::new_irqless(timer)
|
||||
Self::new_inner(timer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Timer<'d, T, NotAwaitable> {
|
||||
/// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
|
||||
/// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work.
|
||||
///
|
||||
/// This can be useful for triggering tasks via PPI
|
||||
/// `Uarte` uses this internally.
|
||||
pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self {
|
||||
Self::new_irqless(timer)
|
||||
Self::new_inner(timer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +150,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
|
||||
/// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
|
||||
///
|
||||
/// This is used by the public constructors.
|
||||
fn new_irqless(timer: impl Peripheral<P = T> + 'd) -> Self {
|
||||
fn new_inner(timer: impl Peripheral<P = T> + 'd) -> Self {
|
||||
into_ref!(timer);
|
||||
|
||||
let regs = T::regs();
|
||||
|
@ -1,11 +1,7 @@
|
||||
//! I2C-compatible Two Wire Interface in master mode (TWIM) driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
//! HAL interface to the TWIM peripheral.
|
||||
//!
|
||||
//! See product specification:
|
||||
//!
|
||||
//! - nRF52832: Section 33
|
||||
//! - nRF52840: Section 6.31
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::sync::atomic::compiler_fence;
|
||||
use core::sync::atomic::Ordering::SeqCst;
|
||||
@ -23,22 +19,39 @@ use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::util::{slice_in_ram, slice_in_ram_or};
|
||||
use crate::{gpio, pac, Peripheral};
|
||||
|
||||
/// TWI frequency
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Frequency {
|
||||
#[doc = "26738688: 100 kbps"]
|
||||
/// 100 kbps
|
||||
K100 = 26738688,
|
||||
#[doc = "67108864: 250 kbps"]
|
||||
/// 250 kbps
|
||||
K250 = 67108864,
|
||||
#[doc = "104857600: 400 kbps"]
|
||||
/// 400 kbps
|
||||
K400 = 104857600,
|
||||
}
|
||||
|
||||
/// TWIM config.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// Frequency
|
||||
pub frequency: Frequency,
|
||||
|
||||
/// Enable high drive for the SDA line.
|
||||
pub sda_high_drive: bool,
|
||||
|
||||
/// Enable internal pullup for the SDA line.
|
||||
///
|
||||
/// Note that using external pullups is recommended for I2C, and
|
||||
/// most boards already have them.
|
||||
pub sda_pullup: bool,
|
||||
|
||||
/// Enable high drive for the SCL line.
|
||||
pub scl_high_drive: bool,
|
||||
|
||||
/// Enable internal pullup for the SCL line.
|
||||
///
|
||||
/// Note that using external pullups is recommended for I2C, and
|
||||
/// most boards already have them.
|
||||
pub scl_pullup: bool,
|
||||
}
|
||||
|
||||
@ -54,29 +67,38 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// TWI error.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// TX buffer was too long.
|
||||
TxBufferTooLong,
|
||||
/// RX buffer was too long.
|
||||
RxBufferTooLong,
|
||||
/// Data transmit failed.
|
||||
Transmit,
|
||||
/// Data reception failed.
|
||||
Receive,
|
||||
DMABufferNotInDataMemory,
|
||||
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||
BufferNotInRAM,
|
||||
/// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly.
|
||||
AddressNack,
|
||||
/// Didn't receive an ACK bit after a data byte.
|
||||
DataNack,
|
||||
/// Overrun error.
|
||||
Overrun,
|
||||
/// Timeout error.
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload.
|
||||
///
|
||||
/// For more details about EasyDMA, consult the module documentation.
|
||||
/// TWI driver.
|
||||
pub struct Twim<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// Create a new TWI driver.
|
||||
pub fn new(
|
||||
twim: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -153,7 +175,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
|
||||
/// Set TX buffer, checking that it is in RAM and has suitable length.
|
||||
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
|
||||
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::TxBufferTooLong);
|
||||
@ -233,7 +255,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
return Err(Error::DataNack);
|
||||
}
|
||||
if err.overrun().is_received() {
|
||||
return Err(Error::DataNack);
|
||||
return Err(Error::Overrun);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -435,7 +457,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
) -> Result<(), Error> {
|
||||
match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying TWIM tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
|
||||
tx_ram_buf.copy_from_slice(wr_buffer);
|
||||
@ -448,7 +470,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
|
||||
match self.setup_write_from_ram(address, wr_buffer, inten) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying TWIM tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
|
||||
tx_ram_buf.copy_from_slice(wr_buffer);
|
||||
@ -612,6 +634,10 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
|
||||
// ===========================================
|
||||
|
||||
/// Read from an I2C slave.
|
||||
///
|
||||
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||
/// and at most 65535 bytes on the nRF52840.
|
||||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.setup_read(address, buffer, true)?;
|
||||
self.async_wait().await;
|
||||
@ -621,6 +647,10 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write to an I2C slave.
|
||||
///
|
||||
/// The buffer must have a length of at most 255 bytes on the nRF52832
|
||||
/// and at most 65535 bytes on the nRF52840.
|
||||
pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.setup_write(address, buffer, true)?;
|
||||
self.async_wait().await;
|
||||
@ -640,6 +670,11 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write data to an I2C slave, then read data from the slave without
|
||||
/// triggering a stop condition between the two.
|
||||
///
|
||||
/// The buffers must have a length of at most 255 bytes on the nRF52832
|
||||
/// and at most 65535 bytes on the nRF52840.
|
||||
pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.setup_write_read(address, wr_buffer, rd_buffer, true)?;
|
||||
self.async_wait().await;
|
||||
@ -705,7 +740,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// TWIM peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
@ -776,7 +813,7 @@ mod eh1 {
|
||||
Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Receive => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::DMABufferNotInDataMemory => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::AddressNack => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
//! I2C-compatible Two Wire Interface in slave mode (TWIM) driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
//! HAL interface to the TWIS peripheral.
|
||||
//!
|
||||
//! See product specification:
|
||||
//!
|
||||
//! - nRF52832: Section 33
|
||||
//! - nRF52840: Section 6.31
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::sync::atomic::compiler_fence;
|
||||
use core::sync::atomic::Ordering::SeqCst;
|
||||
@ -22,14 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::util::slice_in_ram_or;
|
||||
use crate::{gpio, pac, Peripheral};
|
||||
|
||||
/// TWIS config.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// First address
|
||||
pub address0: u8,
|
||||
|
||||
/// Second address, optional.
|
||||
pub address1: Option<u8>,
|
||||
|
||||
/// Overread character.
|
||||
///
|
||||
/// If the master keeps clocking the bus after all the bytes in the TX buffer have
|
||||
/// already been transmitted, this byte will be constantly transmitted.
|
||||
pub orc: u8,
|
||||
|
||||
/// Enable high drive for the SDA line.
|
||||
pub sda_high_drive: bool,
|
||||
|
||||
/// Enable internal pullup for the SDA line.
|
||||
///
|
||||
/// Note that using external pullups is recommended for I2C, and
|
||||
/// most boards already have them.
|
||||
pub sda_pullup: bool,
|
||||
|
||||
/// Enable high drive for the SCL line.
|
||||
pub scl_high_drive: bool,
|
||||
|
||||
/// Enable internal pullup for the SCL line.
|
||||
///
|
||||
/// Note that using external pullups is recommended for I2C, and
|
||||
/// most boards already have them.
|
||||
pub scl_pullup: bool,
|
||||
}
|
||||
|
||||
@ -54,36 +73,48 @@ enum Status {
|
||||
Write,
|
||||
}
|
||||
|
||||
/// TWIS error.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// TX buffer was too long.
|
||||
TxBufferTooLong,
|
||||
/// TX buffer was too long.
|
||||
RxBufferTooLong,
|
||||
/// Didn't receive an ACK bit after a data byte.
|
||||
DataNack,
|
||||
/// Bus error.
|
||||
Bus,
|
||||
DMABufferNotInDataMemory,
|
||||
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||
BufferNotInRAM,
|
||||
/// Overflow
|
||||
Overflow,
|
||||
/// Overread
|
||||
OverRead,
|
||||
/// Timeout
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// Received command
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Command {
|
||||
/// Read
|
||||
Read,
|
||||
/// Write+read
|
||||
WriteRead(usize),
|
||||
/// Write
|
||||
Write(usize),
|
||||
}
|
||||
|
||||
/// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload.
|
||||
///
|
||||
/// For more details about EasyDMA, consult the module documentation.
|
||||
/// TWIS driver.
|
||||
pub struct Twis<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Twis<'d, T> {
|
||||
/// Create a new TWIS driver.
|
||||
pub fn new(
|
||||
twis: impl Peripheral<P = T> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
@ -174,7 +205,7 @@ impl<'d, T: Instance> Twis<'d, T> {
|
||||
|
||||
/// Set TX buffer, checking that it is in RAM and has suitable length.
|
||||
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
|
||||
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::TxBufferTooLong);
|
||||
@ -535,7 +566,7 @@ impl<'d, T: Instance> Twis<'d, T> {
|
||||
fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
|
||||
match self.setup_respond_from_ram(wr_buffer, inten) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying TWIS tx buffer into RAM for DMA");
|
||||
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
|
||||
tx_ram_buf.copy_from_slice(wr_buffer);
|
||||
@ -737,7 +768,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// TWIS peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
//! Async UART
|
||||
//! Universal Asynchronous Receiver Transmitter (UART) driver.
|
||||
//!
|
||||
//! Async UART is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte].
|
||||
//! The UART driver is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte].
|
||||
//! The [Uarte] here is useful for those use-cases where reading the UARTE peripheral is
|
||||
//! exclusively awaited on. If the [Uarte] is required to be awaited on with some other future,
|
||||
//! for example when using `futures_util::future::select`, then you should consider
|
||||
@ -13,6 +11,8 @@
|
||||
//! memory may be used given that buffers are passed in directly to its read and write
|
||||
//! methods.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
@ -32,10 +32,13 @@ use crate::timer::{Frequency, Instance as TimerInstance, Timer};
|
||||
use crate::util::slice_in_ram_or;
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// UARTE config.
|
||||
#[derive(Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// Parity bit.
|
||||
pub parity: Parity,
|
||||
/// Baud rate.
|
||||
pub baudrate: Baudrate,
|
||||
}
|
||||
|
||||
@ -48,31 +51,33 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// UART error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Buffer was too long.
|
||||
BufferTooLong,
|
||||
DMABufferNotInDataMemory,
|
||||
// TODO: add other error variants.
|
||||
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
|
||||
BufferNotInRAM,
|
||||
}
|
||||
|
||||
/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload.
|
||||
///
|
||||
/// For more details about EasyDMA, consult the module documentation.
|
||||
/// UARTE driver.
|
||||
pub struct Uarte<'d, T: Instance> {
|
||||
tx: UarteTx<'d, T>,
|
||||
rx: UarteRx<'d, T>,
|
||||
}
|
||||
|
||||
/// Transmitter interface to the UARTE peripheral obtained
|
||||
/// via [Uarte]::split.
|
||||
/// Transmitter part of the UARTE driver.
|
||||
///
|
||||
/// This can be obtained via [`Uarte::split`], or created directly.
|
||||
pub struct UarteTx<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// Receiver interface to the UARTE peripheral obtained
|
||||
/// via [Uarte]::split.
|
||||
/// Receiver part of the UARTE driver.
|
||||
///
|
||||
/// This can be obtained via [`Uarte::split`], or created directly.
|
||||
pub struct UarteRx<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
}
|
||||
@ -165,16 +170,16 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the Uarte into a transmitter and receiver, which is
|
||||
/// particularly useful when having two tasks correlating to
|
||||
/// transmitting and receiving.
|
||||
/// Split the Uarte into the transmitter and receiver parts.
|
||||
///
|
||||
/// This is useful to concurrently transmit and receive from independent tasks.
|
||||
pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
/// Split the Uarte into a transmitter and receiver that will
|
||||
/// return on idle, which is determined as the time it takes
|
||||
/// for two bytes to be received.
|
||||
/// Split the Uarte into the transmitter and receiver with idle support parts.
|
||||
///
|
||||
/// This is useful to concurrently transmit and receive from independent tasks.
|
||||
pub fn split_with_idle<U: TimerInstance>(
|
||||
self,
|
||||
timer: impl Peripheral<P = U> + 'd,
|
||||
@ -247,10 +252,12 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled.
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.rx.read(buffer).await
|
||||
}
|
||||
|
||||
/// Write all bytes in the buffer.
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.tx.write(buffer).await
|
||||
}
|
||||
@ -260,10 +267,12 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
||||
self.tx.write_from_ram(buffer).await
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled.
|
||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.rx.blocking_read(buffer)
|
||||
}
|
||||
|
||||
/// Write all bytes in the buffer.
|
||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.tx.blocking_write(buffer)
|
||||
}
|
||||
@ -355,10 +364,11 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
||||
Self { _p: uarte }
|
||||
}
|
||||
|
||||
/// Write all bytes in the buffer.
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
match self.write_from_ram(buffer).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying UARTE tx buffer into RAM for DMA");
|
||||
let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()];
|
||||
ram_buf.copy_from_slice(buffer);
|
||||
@ -368,12 +378,13 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`write`](Self::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
@ -423,10 +434,11 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write all bytes in the buffer.
|
||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
match self.blocking_write_from_ram(buffer) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::DMABufferNotInDataMemory) => {
|
||||
Err(Error::BufferNotInRAM) => {
|
||||
trace!("Copying UARTE tx buffer into RAM for DMA");
|
||||
let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()];
|
||||
ram_buf.copy_from_slice(buffer);
|
||||
@ -436,12 +448,13 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`write_from_ram`](Self::write_from_ram) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
|
||||
pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
|
||||
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
@ -549,6 +562,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
||||
Self { _p: uarte }
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled.
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(());
|
||||
@ -602,6 +616,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled.
|
||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(());
|
||||
@ -653,6 +668,9 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Receiver part of the UARTE driver, with `read_until_idle` support.
|
||||
///
|
||||
/// This can be obtained via [`Uarte::split_with_idle`].
|
||||
pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
|
||||
rx: UarteRx<'d, T>,
|
||||
timer: Timer<'d, U>,
|
||||
@ -661,16 +679,21 @@ pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
|
||||
/// Read bytes until the buffer is filled.
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.ppi_ch1.disable();
|
||||
self.rx.read(buffer).await
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled.
|
||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.ppi_ch1.disable();
|
||||
self.rx.blocking_read(buffer)
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled, or the line becomes idle.
|
||||
///
|
||||
/// Returns the amount of bytes read.
|
||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(0);
|
||||
@ -727,6 +750,9 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
/// Read bytes until the buffer is filled, or the line becomes idle.
|
||||
///
|
||||
/// Returns the amount of bytes read.
|
||||
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(0);
|
||||
@ -860,7 +886,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// UARTE peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
@ -919,7 +947,7 @@ mod eh1 {
|
||||
fn kind(&self) -> embedded_hal_1::serial::ErrorKind {
|
||||
match *self {
|
||||
Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other,
|
||||
Self::DMABufferNotInDataMemory => embedded_hal_1::serial::ErrorKind::Other,
|
||||
Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Universal Serial Bus (USB) driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
@ -24,38 +26,38 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
|
||||
static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
|
||||
static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/// Trait for detecting USB VBUS power.
|
||||
///
|
||||
/// There are multiple ways to detect USB power. The behavior
|
||||
/// here provides a hook into determining whether it is.
|
||||
pub trait UsbSupply {
|
||||
pub trait VbusDetect {
|
||||
/// Report whether power is detected.
|
||||
///
|
||||
/// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the
|
||||
/// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral.
|
||||
fn is_usb_detected(&self) -> bool;
|
||||
|
||||
/// Wait until USB power is ready.
|
||||
///
|
||||
/// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the
|
||||
/// `USBPWRRDY` event from the `POWER` peripheral.
|
||||
async fn wait_power_ready(&mut self) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
pub struct Driver<'d, T: Instance, P: UsbSupply> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
alloc_in: Allocator,
|
||||
alloc_out: Allocator,
|
||||
usb_supply: P,
|
||||
}
|
||||
|
||||
/// Uses the POWER peripheral to detect when power is available
|
||||
/// for USB. Unsuitable for usage with the nRF softdevice.
|
||||
/// [`VbusDetect`] implementation using the native hardware POWER peripheral.
|
||||
///
|
||||
/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces
|
||||
/// to POWER. In that case, use [`VbusDetectSignal`].
|
||||
#[cfg(not(feature = "_nrf5340-app"))]
|
||||
pub struct PowerUsb {
|
||||
pub struct HardwareVbusDetect {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// Can be used to signal that power is available. Particularly suited for
|
||||
/// use with the nRF softdevice.
|
||||
pub struct SignalledSupply {
|
||||
usb_detected: AtomicBool,
|
||||
power_ready: AtomicBool,
|
||||
}
|
||||
|
||||
static POWER_WAKER: AtomicWaker = NEW_AW;
|
||||
|
||||
#[cfg(not(feature = "_nrf5340-app"))]
|
||||
impl PowerUsb {
|
||||
impl HardwareVbusDetect {
|
||||
/// Create a new `VbusDetectNative`.
|
||||
pub fn new(power_irq: impl Interrupt) -> Self {
|
||||
let regs = unsafe { &*pac::POWER::ptr() };
|
||||
|
||||
@ -92,7 +94,7 @@ impl PowerUsb {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "_nrf5340-app"))]
|
||||
impl UsbSupply for PowerUsb {
|
||||
impl VbusDetect for HardwareVbusDetect {
|
||||
fn is_usb_detected(&self) -> bool {
|
||||
let regs = unsafe { &*pac::POWER::ptr() };
|
||||
regs.usbregstatus.read().vbusdetect().is_vbus_present()
|
||||
@ -115,7 +117,20 @@ impl UsbSupply for PowerUsb {
|
||||
}
|
||||
}
|
||||
|
||||
impl SignalledSupply {
|
||||
/// Software-backed [`VbusDetect`] implementation.
|
||||
///
|
||||
/// This implementation does not interact with the hardware, it allows user code
|
||||
/// to notify the power events by calling functions instead.
|
||||
///
|
||||
/// This is suitable for use with the nRF softdevice, by calling the functions
|
||||
/// when the softdevice reports power-related events.
|
||||
pub struct SoftwareVbusDetect {
|
||||
usb_detected: AtomicBool,
|
||||
power_ready: AtomicBool,
|
||||
}
|
||||
|
||||
impl SoftwareVbusDetect {
|
||||
/// Create a new `SoftwareVbusDetect`.
|
||||
pub fn new(usb_detected: bool, power_ready: bool) -> Self {
|
||||
BUS_WAKER.wake();
|
||||
|
||||
@ -125,6 +140,9 @@ impl SignalledSupply {
|
||||
}
|
||||
}
|
||||
|
||||
/// Report whether power was detected.
|
||||
///
|
||||
/// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral.
|
||||
pub fn detected(&self, detected: bool) {
|
||||
self.usb_detected.store(detected, Ordering::Relaxed);
|
||||
self.power_ready.store(false, Ordering::Relaxed);
|
||||
@ -132,13 +150,16 @@ impl SignalledSupply {
|
||||
POWER_WAKER.wake();
|
||||
}
|
||||
|
||||
/// Report when USB power is ready.
|
||||
///
|
||||
/// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral.
|
||||
pub fn ready(&self) {
|
||||
self.power_ready.store(true, Ordering::Relaxed);
|
||||
POWER_WAKER.wake();
|
||||
}
|
||||
}
|
||||
|
||||
impl UsbSupply for &SignalledSupply {
|
||||
impl VbusDetect for &SoftwareVbusDetect {
|
||||
fn is_usb_detected(&self) -> bool {
|
||||
self.usb_detected.load(Ordering::Relaxed)
|
||||
}
|
||||
@ -159,7 +180,16 @@ impl UsbSupply for &SignalledSupply {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> {
|
||||
/// USB driver.
|
||||
pub struct Driver<'d, T: Instance, P: VbusDetect> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
alloc_in: Allocator,
|
||||
alloc_out: Allocator,
|
||||
usb_supply: P,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> {
|
||||
/// Create a new USB driver.
|
||||
pub fn new(usb: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd, usb_supply: P) -> Self {
|
||||
into_ref!(usb, irq);
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
@ -225,7 +255,7 @@ impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> {
|
||||
impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> {
|
||||
type EndpointOut = Endpoint<'d, T, Out>;
|
||||
type EndpointIn = Endpoint<'d, T, In>;
|
||||
type ControlPipe = ControlPipe<'d, T>;
|
||||
@ -235,7 +265,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||
let index = self.alloc_in.allocate(ep_type)?;
|
||||
let ep_addr = EndpointAddress::from_parts(index, Direction::In);
|
||||
@ -243,7 +273,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
addr: ep_addr,
|
||||
ep_type,
|
||||
max_packet_size: packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -251,7 +281,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||
let index = self.alloc_out.allocate(ep_type)?;
|
||||
let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
|
||||
@ -259,7 +289,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
addr: ep_addr,
|
||||
ep_type,
|
||||
max_packet_size: packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -278,13 +308,14 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bus<'d, T: Instance, P: UsbSupply> {
|
||||
/// USB bus.
|
||||
pub struct Bus<'d, T: Instance, P: VbusDetect> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
power_available: bool,
|
||||
usb_supply: P,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> {
|
||||
async fn enable(&mut self) {
|
||||
let regs = T::regs();
|
||||
|
||||
@ -513,7 +544,10 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type-level marker for OUT endpoints.
|
||||
pub enum Out {}
|
||||
|
||||
/// Type-level marker for IN endpoints.
|
||||
pub enum In {}
|
||||
|
||||
trait EndpointDir {
|
||||
@ -556,6 +590,7 @@ impl EndpointDir for Out {
|
||||
}
|
||||
}
|
||||
|
||||
/// USB endpoint.
|
||||
pub struct Endpoint<'d, T: Instance, Dir> {
|
||||
_phantom: PhantomData<(&'d mut T, Dir)>,
|
||||
info: EndpointInfo,
|
||||
@ -715,6 +750,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
||||
}
|
||||
}
|
||||
|
||||
/// USB control pipe.
|
||||
pub struct ControlPipe<'d, T: Instance> {
|
||||
_p: PeripheralRef<'d, T>,
|
||||
max_packet_size: u16,
|
||||
@ -905,7 +941,9 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
/// USB peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: Interrupt;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! HAL interface to the WDT peripheral.
|
||||
//! Watchdog Timer (WDT) driver.
|
||||
//!
|
||||
//! This HAL implements a basic watchdog timer with 1..=8 handles.
|
||||
//! Once the watchdog has been started, it cannot be stopped.
|
||||
@ -8,6 +8,7 @@ use crate::peripherals;
|
||||
|
||||
const MIN_TICKS: u32 = 15;
|
||||
|
||||
/// WDT configuration.
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
/// Number of 32768 Hz ticks in each watchdog period.
|
||||
@ -57,13 +58,13 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface to the Watchdog.
|
||||
/// Watchdog driver.
|
||||
pub struct Watchdog {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Watchdog {
|
||||
/// Try to create a new watchdog instance from the peripheral.
|
||||
/// Try to create a new watchdog driver.
|
||||
///
|
||||
/// This function will return an error if the watchdog is already active
|
||||
/// with a `config` different to the requested one, or a different number of
|
||||
@ -155,6 +156,7 @@ impl Watchdog {
|
||||
}
|
||||
}
|
||||
|
||||
/// Watchdog handle.
|
||||
pub struct WatchdogHandle {
|
||||
index: u8,
|
||||
}
|
||||
|
@ -194,13 +194,13 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
|
||||
trace!(
|
||||
"allocating type={:?} mps={:?} interval={}, dir={:?}",
|
||||
"allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
D::dir()
|
||||
);
|
||||
|
||||
@ -281,7 +281,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
addr: EndpointAddress::from_parts(index, D::dir()),
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
},
|
||||
buf,
|
||||
})
|
||||
@ -298,18 +298,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
|
||||
|
@ -268,13 +268,13 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
|
||||
trace!(
|
||||
"allocating type={:?} mps={:?} interval={}, dir={:?}",
|
||||
"allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
D::dir()
|
||||
);
|
||||
|
||||
@ -345,7 +345,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
addr: EndpointAddress::from_parts(index, D::dir()),
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
},
|
||||
buf,
|
||||
})
|
||||
@ -362,18 +362,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
|
||||
|
@ -217,13 +217,13 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Endpoint<'d, T, D>, EndpointAllocError> {
|
||||
trace!(
|
||||
"allocating type={:?} mps={:?} interval={}, dir={:?}",
|
||||
"allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
D::dir()
|
||||
);
|
||||
|
||||
@ -292,7 +292,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
addr: EndpointAddress::from_parts(index, D::dir()),
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
interval_ms,
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -308,18 +308,18 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointIn, EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointOut, EndpointAllocError> {
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||
self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
|
||||
|
32
embassy-usb-driver/README.md
Normal file
32
embassy-usb-driver/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# embassy-usb-driver
|
||||
|
||||
This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these
|
||||
traits to add support for using `embassy-usb` for a given chip/platform.
|
||||
|
||||
The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]
|
||||
APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used
|
||||
with the newer `embassy-usb` without needing updates.
|
||||
|
||||
If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate
|
||||
instead of this one.
|
||||
|
||||
[`embassy-usb`]: https://crates.io/crates/embassy-usb
|
||||
|
||||
## Interoperability
|
||||
|
||||
This crate can run on any executor.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
This crate requires nightly Rust, due to using "async fn in trait" support.
|
||||
|
||||
## 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.
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![no_std]
|
||||
#![feature(async_fn_in_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
|
||||
/// the perspective of the host, which is backward for devices, but the standard directions are used
|
||||
@ -95,46 +97,65 @@ impl EndpointAddress {
|
||||
}
|
||||
}
|
||||
|
||||
/// Infomation for an endpoint.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointInfo {
|
||||
/// Endpoint's address.
|
||||
pub addr: EndpointAddress,
|
||||
/// Endpoint's type.
|
||||
pub ep_type: EndpointType,
|
||||
/// Max packet size, in bytes.
|
||||
pub max_packet_size: u16,
|
||||
pub interval: u8,
|
||||
/// Polling interval, in milliseconds.
|
||||
pub interval_ms: u8,
|
||||
}
|
||||
|
||||
/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
|
||||
/// platform.
|
||||
/// Main USB driver trait.
|
||||
///
|
||||
/// Implement this to add support for a new hardware platform.
|
||||
pub trait Driver<'a> {
|
||||
/// Type of the OUT endpoints for this driver.
|
||||
type EndpointOut: EndpointOut + 'a;
|
||||
/// Type of the IN endpoints for this driver.
|
||||
type EndpointIn: EndpointIn + 'a;
|
||||
/// Type of the control pipe for this driver.
|
||||
type ControlPipe: ControlPipe + 'a;
|
||||
/// Type for bus control for this driver.
|
||||
type Bus: Bus + 'a;
|
||||
|
||||
/// Allocates an endpoint and specified endpoint parameters. This method is called by the device
|
||||
/// and class implementations to allocate endpoints, and can only be called before
|
||||
/// [`start`](Self::start) is called.
|
||||
/// Allocates an OUT endpoint.
|
||||
///
|
||||
/// This method is called by the USB stack to allocate endpoints.
|
||||
/// It can only be called before [`start`](Self::start) is called.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should
|
||||
/// attempt to return an endpoint with the specified address. If None, the implementation
|
||||
/// should return the next available one.
|
||||
/// * `ep_type` - the endpoint's type.
|
||||
/// * `max_packet_size` - Maximum packet size in bytes.
|
||||
/// * `interval` - Polling interval parameter for interrupt endpoints.
|
||||
/// * `interval_ms` - Polling interval parameter for interrupt endpoints.
|
||||
fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointOut, EndpointAllocError>;
|
||||
|
||||
/// Allocates an IN endpoint.
|
||||
///
|
||||
/// This method is called by the USB stack to allocate endpoints.
|
||||
/// It can only be called before [`start`](Self::start) is called.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ep_type` - the endpoint's type.
|
||||
/// * `max_packet_size` - Maximum packet size in bytes.
|
||||
/// * `interval_ms` - Polling interval parameter for interrupt endpoints.
|
||||
fn alloc_endpoint_in(
|
||||
&mut self,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
interval_ms: u8,
|
||||
) -> Result<Self::EndpointIn, EndpointAllocError>;
|
||||
|
||||
/// Start operation of the USB device.
|
||||
@ -146,35 +167,37 @@ pub trait Driver<'a> {
|
||||
/// This consumes the `Driver` instance, so it's no longer possible to allocate more
|
||||
/// endpoints.
|
||||
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe);
|
||||
|
||||
/// Indicates that `set_device_address` must be called before accepting the corresponding
|
||||
/// control transfer, not after.
|
||||
///
|
||||
/// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
|
||||
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
|
||||
}
|
||||
|
||||
/// USB bus trait.
|
||||
///
|
||||
/// This trait provides methods that act on the whole bus. It is kept owned by
|
||||
/// the main USB task, and used to manage the bus.
|
||||
pub trait Bus {
|
||||
/// Enables the USB peripheral. Soon after enabling the device will be reset, so
|
||||
/// there is no need to perform a USB reset in this method.
|
||||
/// Enable the USB peripheral.
|
||||
async fn enable(&mut self);
|
||||
|
||||
/// Disables and powers down the USB peripheral.
|
||||
/// Disable and powers down the USB peripheral.
|
||||
async fn disable(&mut self);
|
||||
|
||||
/// Wait for a bus-related event.
|
||||
///
|
||||
/// This method should asynchronously wait for an event to happen, then
|
||||
/// return it. See [`Event`] for the list of events this method should return.
|
||||
async fn poll(&mut self) -> Event;
|
||||
|
||||
/// Enables or disables an endpoint.
|
||||
/// Enable or disable an endpoint.
|
||||
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool);
|
||||
|
||||
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
|
||||
/// should be prepared to receive data again. Only used during control transfers.
|
||||
/// Set or clear the STALL condition for an endpoint.
|
||||
///
|
||||
/// If the endpoint is an OUT endpoint, it should be prepared to receive data again.
|
||||
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
|
||||
|
||||
/// Gets whether the STALL condition is set for an endpoint. Only used during control transfers.
|
||||
/// Get whether the STALL condition is set for an endpoint.
|
||||
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;
|
||||
|
||||
/// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
|
||||
/// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the
|
||||
/// device.
|
||||
///
|
||||
/// The default implementation just returns `Unsupported`.
|
||||
@ -187,7 +210,7 @@ pub trait Bus {
|
||||
Err(Unsupported)
|
||||
}
|
||||
|
||||
/// Initiates a remote wakeup of the host by the device.
|
||||
/// Initiate a remote wakeup of the host by the device.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@ -196,25 +219,27 @@ pub trait Bus {
|
||||
async fn remote_wakeup(&mut self) -> Result<(), Unsupported>;
|
||||
}
|
||||
|
||||
/// Endpoint trait, common for OUT and IN.
|
||||
pub trait Endpoint {
|
||||
/// Get the endpoint address
|
||||
fn info(&self) -> &EndpointInfo;
|
||||
|
||||
/// Waits for the endpoint to be enabled.
|
||||
/// Wait for the endpoint to be enabled.
|
||||
async fn wait_enabled(&mut self);
|
||||
}
|
||||
|
||||
/// OUT Endpoint trait.
|
||||
pub trait EndpointOut: Endpoint {
|
||||
/// Reads a single packet of data from the endpoint, and returns the actual length of
|
||||
/// Read a single packet of data from the endpoint, and return the actual length of
|
||||
/// the packet.
|
||||
///
|
||||
/// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
|
||||
}
|
||||
|
||||
/// Trait for USB control pipe.
|
||||
/// USB control pipe trait.
|
||||
///
|
||||
/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single
|
||||
/// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single
|
||||
/// unit, and manages them together to implement the control pipe state machine.
|
||||
///
|
||||
/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that
|
||||
@ -232,6 +257,14 @@ pub trait EndpointOut: Endpoint {
|
||||
/// accept() or reject()
|
||||
/// ```
|
||||
///
|
||||
/// - control out for setting the device address:
|
||||
///
|
||||
/// ```not_rust
|
||||
/// setup()
|
||||
/// (...processing...)
|
||||
/// accept_set_address(addr) or reject()
|
||||
/// ```
|
||||
///
|
||||
/// - control out with len != 0:
|
||||
///
|
||||
/// ```not_rust
|
||||
@ -280,26 +313,26 @@ pub trait ControlPipe {
|
||||
/// Maximum packet size for the control pipe
|
||||
fn max_packet_size(&self) -> usize;
|
||||
|
||||
/// Reads a single setup packet from the endpoint.
|
||||
/// Read a single setup packet from the endpoint.
|
||||
async fn setup(&mut self) -> [u8; 8];
|
||||
|
||||
/// Reads a DATA OUT packet into `buf` in response to a control write request.
|
||||
/// Read a DATA OUT packet into `buf` in response to a control write request.
|
||||
///
|
||||
/// Must be called after `setup()` for requests with `direction` of `Out`
|
||||
/// and `length` greater than zero.
|
||||
async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>;
|
||||
|
||||
/// Sends a DATA IN packet with `data` in response to a control read request.
|
||||
/// Send a DATA IN packet with `data` in response to a control read request.
|
||||
///
|
||||
/// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`.
|
||||
async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>;
|
||||
|
||||
/// Accepts a control request.
|
||||
/// Accept a control request.
|
||||
///
|
||||
/// Causes the STATUS packet for the current request to be ACKed.
|
||||
async fn accept(&mut self);
|
||||
|
||||
/// Rejects a control request.
|
||||
/// Reject a control request.
|
||||
///
|
||||
/// Sets a STALL condition on the pipe to indicate an error.
|
||||
async fn reject(&mut self);
|
||||
@ -311,8 +344,9 @@ pub trait ControlPipe {
|
||||
async fn accept_set_address(&mut self, addr: u8);
|
||||
}
|
||||
|
||||
/// IN Endpoint trait.
|
||||
pub trait EndpointIn: Endpoint {
|
||||
/// Writes a single packet of data to the endpoint.
|
||||
/// Write a single packet of data to the endpoint.
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;
|
||||
}
|
||||
|
||||
@ -338,18 +372,22 @@ pub enum Event {
|
||||
PowerRemoved,
|
||||
}
|
||||
|
||||
/// Allocating an endpoint failed.
|
||||
///
|
||||
/// This can be due to running out of endpoints, or out of endpoint memory,
|
||||
/// or because the hardware doesn't support the requested combination of features.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointAllocError;
|
||||
|
||||
/// Operation is unsupported by the driver.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Operation is unsupported by the driver.
|
||||
pub struct Unsupported;
|
||||
|
||||
/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]
|
||||
pub enum EndpointError {
|
||||
/// Either the packet to be written is too long to fit in the transmission
|
||||
/// buffer or the received packet is too long to fit in `buf`.
|
||||
|
22
embassy-usb/README.md
Normal file
22
embassy-usb/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# embassy-usb
|
||||
|
||||
TODO crate description/
|
||||
|
||||
## Interoperability
|
||||
|
||||
This crate can run on any executor.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
This crate requires nightly Rust, due to using "async fn in trait" support.
|
||||
|
||||
## 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.
|
||||
|
@ -349,11 +349,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
self.builder.config_descriptor.write(descriptor_type, descriptor)
|
||||
}
|
||||
|
||||
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn {
|
||||
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||
let ep = self
|
||||
.builder
|
||||
.driver
|
||||
.alloc_endpoint_in(ep_type, max_packet_size, interval)
|
||||
.alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
|
||||
.expect("alloc_endpoint_in failed");
|
||||
|
||||
self.builder.config_descriptor.endpoint(ep.info());
|
||||
@ -361,11 +361,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
ep
|
||||
}
|
||||
|
||||
fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut {
|
||||
fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
||||
let ep = self
|
||||
.builder
|
||||
.driver
|
||||
.alloc_endpoint_out(ep_type, max_packet_size, interval)
|
||||
.alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
|
||||
.expect("alloc_endpoint_out failed");
|
||||
|
||||
self.builder.config_descriptor.endpoint(ep.info());
|
||||
@ -393,25 +393,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
|
||||
///
|
||||
/// Descriptors are written in the order builder functions are called. Note that some
|
||||
/// classes care about the order.
|
||||
pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn {
|
||||
self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval)
|
||||
pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||
self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
|
||||
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut {
|
||||
self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval)
|
||||
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
||||
self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
/// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
|
||||
///
|
||||
/// Descriptors are written in the order builder functions are called. Note that some
|
||||
/// classes care about the order.
|
||||
pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn {
|
||||
self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval)
|
||||
pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
|
||||
self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms)
|
||||
}
|
||||
|
||||
/// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
|
||||
pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut {
|
||||
self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval)
|
||||
pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
|
||||
self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! CDC-ACM class implementation, aka Serial over USB.
|
||||
|
||||
use core::cell::Cell;
|
||||
use core::mem::{self, MaybeUninit};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20;
|
||||
const REQ_GET_LINE_CODING: u8 = 0x21;
|
||||
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
|
||||
|
||||
/// Internal state for CDC-ACM
|
||||
pub struct State<'a> {
|
||||
control: MaybeUninit<Control<'a>>,
|
||||
shared: ControlShared,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
/// Create a new `State`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
control: MaybeUninit::uninit(),
|
||||
@ -284,10 +288,15 @@ impl From<u8> for StopBits {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ParityType {
|
||||
/// No parity bit.
|
||||
None = 0,
|
||||
/// Parity bit is 1 if the amount of `1` bits in the data byte is odd.
|
||||
Odd = 1,
|
||||
/// Parity bit is 1 if the amount of `1` bits in the data byte is even.
|
||||
Even = 2,
|
||||
/// Parity bit is always 1
|
||||
Mark = 3,
|
||||
/// Parity bit is always 0
|
||||
Space = 4,
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class.
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_net_driver_channel as ch;
|
||||
use embassy_net_driver_channel::driver::LinkState;
|
||||
@ -5,11 +6,13 @@ use embassy_usb_driver::Driver;
|
||||
|
||||
use super::{CdcNcmClass, Receiver, Sender};
|
||||
|
||||
/// Internal state for the embassy-net integration.
|
||||
pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
|
||||
ch_state: ch::State<MTU, N_RX, N_TX>,
|
||||
}
|
||||
|
||||
impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {
|
||||
/// Create a new `State`.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ch_state: ch::State::new(),
|
||||
@ -17,6 +20,9 @@ impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_
|
||||
}
|
||||
}
|
||||
|
||||
/// Background runner for the CDC-NCM class.
|
||||
///
|
||||
/// You must call `.run()` in a background task for the class to operate.
|
||||
pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
|
||||
tx_usb: Sender<'d, D>,
|
||||
rx_usb: Receiver<'d, D>,
|
||||
@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
|
||||
/// Run the CDC-NCM class.
|
||||
///
|
||||
/// You must call this in a background task for the class to operate.
|
||||
pub async fn run(mut self) -> ! {
|
||||
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
|
||||
let rx_fut = async move {
|
||||
@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
|
||||
|
||||
// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug?
|
||||
//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd;
|
||||
/// Type alias for the embassy-net driver for CDC-NCM.
|
||||
pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
|
||||
|
||||
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
|
||||
/// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net).
|
||||
pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
||||
self,
|
||||
state: &'d mut State<MTU, N_RX, N_TX>,
|
||||
|
@ -1,18 +1,19 @@
|
||||
/// CDC-NCM, aka Ethernet over USB.
|
||||
///
|
||||
/// # Compatibility
|
||||
///
|
||||
/// Windows: NOT supported in Windows 10. Supported in Windows 11.
|
||||
///
|
||||
/// Linux: Well-supported since forever.
|
||||
///
|
||||
/// Android: Support for CDC-NCM is spotty and varies across manufacturers.
|
||||
///
|
||||
/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
|
||||
/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
|
||||
/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
|
||||
/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
|
||||
/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
|
||||
//! CDC-NCM class implementation, aka Ethernet over USB.
|
||||
//!
|
||||
//! # Compatibility
|
||||
//!
|
||||
//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11.
|
||||
//!
|
||||
//! Linux: Well-supported since forever.
|
||||
//!
|
||||
//! Android: Support for CDC-NCM is spotty and varies across manufacturers.
|
||||
//!
|
||||
//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
|
||||
//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
|
||||
//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
|
||||
//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
|
||||
//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
|
||||
|
||||
use core::intrinsics::copy_nonoverlapping;
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
|
||||
@ -114,6 +115,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
|
||||
&buf[..len]
|
||||
}
|
||||
|
||||
/// Internal state for the CDC-NCM class.
|
||||
pub struct State<'a> {
|
||||
comm_control: MaybeUninit<CommControl<'a>>,
|
||||
data_control: MaybeUninit<DataControl>,
|
||||
@ -121,6 +123,7 @@ pub struct State<'a> {
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
/// Create a new `State`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
comm_control: MaybeUninit::uninit(),
|
||||
@ -223,6 +226,7 @@ impl ControlHandler for DataControl {
|
||||
}
|
||||
}
|
||||
|
||||
/// CDC-NCM class
|
||||
pub struct CdcNcmClass<'d, D: Driver<'d>> {
|
||||
_comm_if: InterfaceNumber,
|
||||
comm_ep: D::EndpointIn,
|
||||
@ -235,6 +239,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> {
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
|
||||
/// Create a new CDC NCM class.
|
||||
pub fn new(
|
||||
builder: &mut Builder<'d, D>,
|
||||
state: &'d mut State<'d>,
|
||||
@ -319,6 +324,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the class into a sender and receiver.
|
||||
///
|
||||
/// This allows concurrently sending and receiving packets from separate tasks.
|
||||
pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
|
||||
(
|
||||
Sender {
|
||||
@ -334,12 +342,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
|
||||
}
|
||||
}
|
||||
|
||||
/// CDC NCM class packet sender.
|
||||
///
|
||||
/// You can obtain a `Sender` with [`CdcNcmClass::split`]
|
||||
pub struct Sender<'d, D: Driver<'d>> {
|
||||
write_ep: D::EndpointIn,
|
||||
seq: u16,
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> Sender<'d, D> {
|
||||
/// Write a packet.
|
||||
///
|
||||
/// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers.
|
||||
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
|
||||
let seq = self.seq;
|
||||
self.seq = self.seq.wrapping_add(1);
|
||||
@ -393,6 +407,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
|
||||
}
|
||||
}
|
||||
|
||||
/// CDC NCM class packet receiver.
|
||||
///
|
||||
/// You can obtain a `Receiver` with [`CdcNcmClass::split`]
|
||||
pub struct Receiver<'d, D: Driver<'d>> {
|
||||
data_if: InterfaceNumber,
|
||||
comm_ep: D::EndpointIn,
|
||||
@ -400,7 +417,9 @@ pub struct Receiver<'d, D: Driver<'d>> {
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> Receiver<'d, D> {
|
||||
/// Reads a single packet from the OUT endpoint.
|
||||
/// Write a network packet.
|
||||
///
|
||||
/// This waits until a packet is succesfully received from the endpoint buffers.
|
||||
pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
|
||||
// Retry loop
|
||||
loop {
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! USB HID (Human Interface Device) class implementation.
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::Range;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09;
|
||||
const HID_REQ_GET_PROTOCOL: u8 = 0x03;
|
||||
const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
|
||||
|
||||
/// Configuration for the HID class.
|
||||
pub struct Config<'d> {
|
||||
/// HID report descriptor.
|
||||
pub report_descriptor: &'d [u8],
|
||||
@ -46,11 +49,15 @@ pub struct Config<'d> {
|
||||
pub max_packet_size: u16,
|
||||
}
|
||||
|
||||
/// Report ID
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ReportId {
|
||||
/// IN report
|
||||
In(u8),
|
||||
/// OUT report
|
||||
Out(u8),
|
||||
/// Feature report
|
||||
Feature(u8),
|
||||
}
|
||||
|
||||
@ -65,12 +72,14 @@ impl ReportId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal state for USB HID.
|
||||
pub struct State<'d> {
|
||||
control: MaybeUninit<Control<'d>>,
|
||||
out_report_offset: AtomicUsize,
|
||||
}
|
||||
|
||||
impl<'d> State<'d> {
|
||||
/// Create a new `State`.
|
||||
pub fn new() -> Self {
|
||||
State {
|
||||
control: MaybeUninit::uninit(),
|
||||
@ -79,6 +88,7 @@ impl<'d> State<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
/// USB HID reader/writer.
|
||||
pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
|
||||
reader: HidReader<'d, D, READ_N>,
|
||||
writer: HidWriter<'d, D, WRITE_N>,
|
||||
@ -180,20 +190,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit
|
||||
}
|
||||
}
|
||||
|
||||
/// USB HID writer.
|
||||
///
|
||||
/// You can obtain a `HidWriter` using [`HidReaderWriter::split`].
|
||||
pub struct HidWriter<'d, D: Driver<'d>, const N: usize> {
|
||||
ep_in: D::EndpointIn,
|
||||
}
|
||||
|
||||
/// USB HID reader.
|
||||
///
|
||||
/// You can obtain a `HidReader` using [`HidReaderWriter::split`].
|
||||
pub struct HidReader<'d, D: Driver<'d>, const N: usize> {
|
||||
ep_out: D::EndpointOut,
|
||||
offset: &'d AtomicUsize,
|
||||
}
|
||||
|
||||
/// Error when reading a HID report.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ReadError {
|
||||
/// The given buffer was too small to read the received report.
|
||||
BufferOverflow,
|
||||
/// The endpoint is disabled.
|
||||
Disabled,
|
||||
/// The report was only partially read. See [`HidReader::read`] for details.
|
||||
Sync(Range<usize>),
|
||||
}
|
||||
|
||||
@ -344,6 +364,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for HID-related control requests.
|
||||
pub trait RequestHandler {
|
||||
/// Reads the value of report `id` into `buf` returning the size.
|
||||
///
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Implementations of well-known USB classes.
|
||||
pub mod cdc_acm;
|
||||
pub mod cdc_ncm;
|
||||
pub mod hid;
|
||||
|
@ -126,17 +126,23 @@ impl Request {
|
||||
}
|
||||
}
|
||||
|
||||
/// Response for a CONTROL OUT request.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum OutResponse {
|
||||
/// The request was accepted.
|
||||
Accepted,
|
||||
/// The request was rejected.
|
||||
Rejected,
|
||||
}
|
||||
|
||||
/// Response for a CONTROL IN request.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InResponse<'a> {
|
||||
/// The request was accepted. The buffer contains the response data.
|
||||
Accepted(&'a [u8]),
|
||||
/// The request was rejected.
|
||||
Rejected,
|
||||
}
|
||||
|
||||
@ -148,6 +154,7 @@ pub trait ControlHandler {
|
||||
/// Called after a USB reset after the bus reset sequence is complete.
|
||||
fn reset(&mut self) {}
|
||||
|
||||
/// Called when a "set alternate setting" control request is done on the interface.
|
||||
fn set_alternate_setting(&mut self, alternate_setting: u8) {
|
||||
let _ = alternate_setting;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Utilities for writing USB descriptors.
|
||||
|
||||
use crate::builder::Config;
|
||||
use crate::driver::EndpointInfo;
|
||||
use crate::types::*;
|
||||
@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> {
|
||||
endpoint.ep_type as u8, // bmAttributes
|
||||
endpoint.max_packet_size as u8,
|
||||
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
|
||||
endpoint.interval, // bInterval
|
||||
endpoint.interval_ms, // bInterval
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
@ -46,10 +48,13 @@ pub enum UsbDeviceState {
|
||||
Configured,
|
||||
}
|
||||
|
||||
/// Error returned by [`UsbDevice::remote_wakeup`].
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum RemoteWakeupError {
|
||||
/// The USB device is not suspended, or remote wakeup was not enabled.
|
||||
InvalidState,
|
||||
/// The underlying driver doesn't support remote wakeup.
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
@ -65,6 +70,7 @@ pub const CONFIGURATION_NONE: u8 = 0;
|
||||
/// The bConfiguration value for the single configuration supported by this device.
|
||||
pub const CONFIGURATION_VALUE: u8 = 1;
|
||||
|
||||
/// Maximum interface count, configured at compile time.
|
||||
pub const MAX_INTERFACE_COUNT: usize = 4;
|
||||
|
||||
const STRING_INDEX_MANUFACTURER: u8 = 1;
|
||||
@ -100,6 +106,7 @@ struct Interface<'d> {
|
||||
num_strings: u8,
|
||||
}
|
||||
|
||||
/// Main struct for the USB device stack.
|
||||
pub struct UsbDevice<'d, D: Driver<'d>> {
|
||||
control_buf: &'d mut [u8],
|
||||
control: D::ControlPipe,
|
||||
@ -489,7 +496,6 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
|
||||
.unwrap();
|
||||
|
||||
// TODO check it is valid (not out of range)
|
||||
// TODO actually enable/disable endpoints.
|
||||
|
||||
if let Some(handler) = &mut iface.handler {
|
||||
handler.set_alternate_setting(new_altsetting);
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! USB types.
|
||||
|
||||
/// A handle for a USB interface that contains its number.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
|
||||
embedded-io = "0.4.0"
|
||||
|
@ -24,9 +24,9 @@ async fn main(_spawner: Spawner) {
|
||||
let sample_rate = master_clock.sample_rate();
|
||||
info!("Sample rate: {}", sample_rate);
|
||||
|
||||
let config = Config::default()
|
||||
.sample_width(SampleWidth::_16bit)
|
||||
.channels(Channels::MonoLeft);
|
||||
let mut config = Config::default();
|
||||
config.sample_width = SampleWidth::_16bit;
|
||||
config.channels = Channels::MonoLeft;
|
||||
|
||||
let irq = interrupt::take!(I2S);
|
||||
let buffers_out = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new();
|
||||
|
@ -22,9 +22,9 @@ async fn main(_spawner: Spawner) {
|
||||
let sample_rate = master_clock.sample_rate();
|
||||
info!("Sample rate: {}", sample_rate);
|
||||
|
||||
let config = Config::default()
|
||||
.sample_width(SampleWidth::_16bit)
|
||||
.channels(Channels::MonoLeft);
|
||||
let mut config = Config::default();
|
||||
config.sample_width = SampleWidth::_16bit;
|
||||
config.channels = Channels::MonoLeft;
|
||||
|
||||
let irq = interrupt::take!(I2S);
|
||||
let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new();
|
||||
|
@ -23,9 +23,9 @@ async fn main(_spawner: Spawner) {
|
||||
let sample_rate = master_clock.sample_rate();
|
||||
info!("Sample rate: {}", sample_rate);
|
||||
|
||||
let config = Config::default()
|
||||
.sample_width(SampleWidth::_16bit)
|
||||
.channels(Channels::MonoLeft);
|
||||
let mut config = Config::default();
|
||||
config.sample_width = SampleWidth::_16bit;
|
||||
config.channels = Channels::MonoLeft;
|
||||
|
||||
let irq = interrupt::take!(I2S);
|
||||
let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new();
|
||||
|
@ -5,7 +5,7 @@
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nrf::interrupt;
|
||||
use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState};
|
||||
use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc};
|
||||
use embassy_nrf::timer::Frequency;
|
||||
use embassy_time::Duration;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -61,7 +61,7 @@ async fn main(_p: Spawner) {
|
||||
c = 0;
|
||||
a = 0;
|
||||
}
|
||||
SamplerState::Sampled
|
||||
CallbackResult::Continue
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
@ -9,7 +9,7 @@ use embassy_executor::Spawner;
|
||||
use embassy_net::tcp::TcpSocket;
|
||||
use embassy_net::{Stack, StackResources};
|
||||
use embassy_nrf::rng::Rng;
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::usb::{Driver, HardwareVbusDetect};
|
||||
use embassy_nrf::{interrupt, pac, peripherals};
|
||||
use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};
|
||||
use embassy_usb::class::cdc_ncm::{CdcNcmClass, State};
|
||||
@ -18,7 +18,7 @@ use embedded_io::asynch::Write;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>;
|
||||
type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
|
||||
|
||||
macro_rules! singleton {
|
||||
($val:expr) => {{
|
||||
@ -58,7 +58,7 @@ async fn main(spawner: Spawner) {
|
||||
// Create the driver, from the HAL.
|
||||
let irq = interrupt::take!(USBD);
|
||||
let power_irq = interrupt::take!(POWER_CLOCK);
|
||||
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
|
||||
let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq));
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
|
@ -10,7 +10,7 @@ use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_nrf::gpio::{Input, Pin, Pull};
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::usb::{Driver, HardwareVbusDetect};
|
||||
use embassy_nrf::{interrupt, pac};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) {
|
||||
// Create the driver, from the HAL.
|
||||
let irq = interrupt::take!(USBD);
|
||||
let power_irq = interrupt::take!(POWER_CLOCK);
|
||||
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
|
||||
let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq));
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
|
@ -7,7 +7,7 @@ use core::mem;
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::usb::{Driver, HardwareVbusDetect};
|
||||
use embassy_nrf::{interrupt, pac};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State};
|
||||
@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
|
||||
// Create the driver, from the HAL.
|
||||
let irq = interrupt::take!(USBD);
|
||||
let power_irq = interrupt::take!(POWER_CLOCK);
|
||||
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
|
||||
let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq));
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
|
@ -7,7 +7,7 @@ use core::mem;
|
||||
use defmt::{info, panic};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply};
|
||||
use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect};
|
||||
use embassy_nrf::{interrupt, pac};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) {
|
||||
// Create the driver, from the HAL.
|
||||
let irq = interrupt::take!(USBD);
|
||||
let power_irq = interrupt::take!(POWER_CLOCK);
|
||||
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
|
||||
let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq));
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
@ -97,7 +97,7 @@ impl From<EndpointError> for Disconnected {
|
||||
}
|
||||
}
|
||||
|
||||
async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>(
|
||||
async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>(
|
||||
class: &mut CdcAcmClass<'d, Driver<'d, T, P>>,
|
||||
) -> Result<(), Disconnected> {
|
||||
let mut buf = [0; 64];
|
||||
|
@ -6,7 +6,7 @@ use core::mem;
|
||||
|
||||
use defmt::{info, panic, unwrap};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::usb::{Driver, HardwareVbusDetect};
|
||||
use embassy_nrf::{interrupt, pac, peripherals};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
@ -14,7 +14,7 @@ use embassy_usb::{Builder, Config, UsbDevice};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>;
|
||||
type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) {
|
||||
@ -42,7 +42,7 @@ async fn main(spawner: Spawner) {
|
||||
// Create the driver, from the HAL.
|
||||
let irq = interrupt::take!(USBD);
|
||||
let power_irq = interrupt::take!(POWER_CLOCK);
|
||||
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
|
||||
let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq));
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
|
Loading…
Reference in New Issue
Block a user