nrf: docs.
This commit is contained in:
@ -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>;
|
||||
@ -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,
|
||||
}
|
||||
|
Reference in New Issue
Block a user