nrf: docs.

This commit is contained in:
Dario Nieuwenhuis
2023-02-01 00:48:33 +01:00
parent ca10fe7135
commit b5cf332cc0
40 changed files with 694 additions and 245 deletions

View File

@ -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>,
}

View File

@ -1,3 +1,4 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {

View File

@ -1,3 +1,4 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {

View File

@ -1,3 +1,4 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {

View File

@ -1,4 +1,4 @@
//! General purpose input/output for nRF.
//! General purpose input/output (GPIO) driver.
#![macro_use]
use core::convert::Infallible;

View File

@ -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,
}

View File

@ -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;
}

View File

@ -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",

View File

@ -1,4 +1,4 @@
//! Non-Volatile Memory Controller (NVMC) module.
//! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver.
use core::{ptr, slice};

View File

@ -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,
}

View File

@ -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],

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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,
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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,
}

View File

@ -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,
}
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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();

View File

@ -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)
}

View File

@ -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;
}

View File

@ -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,
}
}
}

View File

@ -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;
}

View File

@ -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,
}