diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-02-01 00:48:33 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-02-01 01:17:41 +0100 |
| commit | b5cf332cc076a0de11ce6a0563a2235c9e57eb5c (patch) | |
| tree | ce14e014dfbe8c3764040d7f9f1ffee84ab5747b /embassy-nrf/src | |
| parent | ca10fe7135d10084e38038f3cd433da39e505bea (diff) | |
nrf: docs.
Diffstat (limited to 'embassy-nrf/src')
27 files changed, 602 insertions, 220 deletions
diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index ea25236f0..112f084c1 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! Async buffered UART | 1 | //! Async buffered UART driver. |
| 2 | //! | 2 | //! |
| 3 | //! WARNING!!! The functionality provided here is intended to be used only | 3 | //! WARNING!!! The functionality provided here is intended to be used only |
| 4 | //! in situations where hardware flow control are available i.e. CTS and RTS. | 4 | //! in situations where hardware flow control are available i.e. CTS and RTS. |
| @@ -69,7 +69,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> { | |||
| 69 | tx_waker: WakerRegistration, | 69 | tx_waker: WakerRegistration, |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | /// Interface to a UARTE instance | 72 | /// Buffered UARTE driver. |
| 73 | pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { | 73 | pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { |
| 74 | inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>, | 74 | inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>, |
| 75 | } | 75 | } |
| @@ -199,6 +199,9 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { | |||
| 199 | }); | 199 | }); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | /// Split the UART in reader and writer parts. | ||
| 203 | /// | ||
| 204 | /// This allows reading and writing concurrently from independent tasks. | ||
| 202 | pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) { | 205 | pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) { |
| 203 | (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) | 206 | (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) |
| 204 | } | 207 | } |
| @@ -320,10 +323,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { | |||
| 320 | } | 323 | } |
| 321 | } | 324 | } |
| 322 | 325 | ||
| 326 | /// Reader part of the buffered UARTE driver. | ||
| 323 | pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { | 327 | pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { |
| 324 | inner: &'u BufferedUarte<'d, U, T>, | 328 | inner: &'u BufferedUarte<'d, U, T>, |
| 325 | } | 329 | } |
| 326 | 330 | ||
| 331 | /// Writer part of the buffered UARTE driver. | ||
| 327 | pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { | 332 | pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { |
| 328 | inner: &'u BufferedUarte<'d, U, T>, | 333 | inner: &'u BufferedUarte<'d, U, T>, |
| 329 | } | 334 | } |
diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 4575f09ff..c600fcbf6 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | /// Peripheral Access Crate | ||
| 1 | #[allow(unused_imports)] | 2 | #[allow(unused_imports)] |
| 2 | #[rustfmt::skip] | 3 | #[rustfmt::skip] |
| 3 | pub mod pac { | 4 | pub mod pac { |
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 54827238a..a46583eca 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | /// Peripheral Access Crate | ||
| 1 | #[allow(unused_imports)] | 2 | #[allow(unused_imports)] |
| 2 | #[rustfmt::skip] | 3 | #[rustfmt::skip] |
| 3 | pub mod pac { | 4 | pub mod pac { |
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 472ee6772..e1509ddde 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | /// Peripheral Access Crate | ||
| 1 | #[allow(unused_imports)] | 2 | #[allow(unused_imports)] |
| 2 | #[rustfmt::skip] | 3 | #[rustfmt::skip] |
| 3 | pub mod pac { | 4 | pub mod pac { |
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 6ef5033eb..895ab9340 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! General purpose input/output for nRF. | 1 | //! General purpose input/output (GPIO) driver. |
| 2 | #![macro_use] | 2 | #![macro_use] |
| 3 | 3 | ||
| 4 | use core::convert::Infallible; | 4 | use core::convert::Infallible; |
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 7467dbc60..e1816eb9b 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! GPIO task/event (GPIOTE) driver. | ||
| 2 | |||
| 1 | use core::convert::Infallible; | 3 | use core::convert::Infallible; |
| 2 | use core::future::{poll_fn, Future}; | 4 | use core::future::{poll_fn, Future}; |
| 3 | use core::task::{Context, Poll}; | 5 | use core::task::{Context, Poll}; |
| @@ -11,29 +13,38 @@ use crate::interrupt::{Interrupt, InterruptExt}; | |||
| 11 | use crate::ppi::{Event, Task}; | 13 | use crate::ppi::{Event, Task}; |
| 12 | use crate::{interrupt, pac, peripherals}; | 14 | use crate::{interrupt, pac, peripherals}; |
| 13 | 15 | ||
| 14 | pub const CHANNEL_COUNT: usize = 8; | 16 | /// Amount of GPIOTE channels in the chip. |
| 17 | const CHANNEL_COUNT: usize = 8; | ||
| 15 | 18 | ||
| 16 | #[cfg(any(feature = "nrf52833", feature = "nrf52840"))] | 19 | #[cfg(any(feature = "nrf52833", feature = "nrf52840"))] |
| 17 | pub const PIN_COUNT: usize = 48; | 20 | const PIN_COUNT: usize = 48; |
| 18 | #[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))] | 21 | #[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))] |
| 19 | pub const PIN_COUNT: usize = 32; | 22 | const PIN_COUNT: usize = 32; |
| 20 | 23 | ||
| 21 | #[allow(clippy::declare_interior_mutable_const)] | 24 | #[allow(clippy::declare_interior_mutable_const)] |
| 22 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 25 | const NEW_AW: AtomicWaker = AtomicWaker::new(); |
| 23 | static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; | 26 | static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; |
| 24 | static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; | 27 | static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; |
| 25 | 28 | ||
| 29 | /// Polarity for listening to events for GPIOTE input channels. | ||
| 26 | pub enum InputChannelPolarity { | 30 | pub enum InputChannelPolarity { |
| 31 | /// Don't listen for any pin changes. | ||
| 27 | None, | 32 | None, |
| 33 | /// Listen for high to low changes. | ||
| 28 | HiToLo, | 34 | HiToLo, |
| 35 | /// Listen for low to high changes. | ||
| 29 | LoToHi, | 36 | LoToHi, |
| 37 | /// Listen for any change, either low to high or high to low. | ||
| 30 | Toggle, | 38 | Toggle, |
| 31 | } | 39 | } |
| 32 | 40 | ||
| 33 | /// Polarity of the `task out` operation. | 41 | /// Polarity of the OUT task operation for GPIOTE output channels. |
| 34 | pub enum OutputChannelPolarity { | 42 | pub enum OutputChannelPolarity { |
| 43 | /// Set the pin high. | ||
| 35 | Set, | 44 | Set, |
| 45 | /// Set the pin low. | ||
| 36 | Clear, | 46 | Clear, |
| 47 | /// Toggle the pin. | ||
| 37 | Toggle, | 48 | Toggle, |
| 38 | } | 49 | } |
| 39 | 50 | ||
| @@ -162,6 +173,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { | |||
| 162 | } | 173 | } |
| 163 | 174 | ||
| 164 | impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { | 175 | impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { |
| 176 | /// Create a new GPIOTE input channel driver. | ||
| 165 | pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { | 177 | pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { |
| 166 | into_ref!(ch); | 178 | into_ref!(ch); |
| 167 | 179 | ||
| @@ -188,6 +200,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { | |||
| 188 | InputChannel { ch, pin } | 200 | InputChannel { ch, pin } |
| 189 | } | 201 | } |
| 190 | 202 | ||
| 203 | /// Asynchronously wait for an event in this channel. | ||
| 191 | pub async fn wait(&self) { | 204 | pub async fn wait(&self) { |
| 192 | let g = regs(); | 205 | let g = regs(); |
| 193 | let num = self.ch.number(); | 206 | let num = self.ch.number(); |
| @@ -231,6 +244,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { | |||
| 231 | } | 244 | } |
| 232 | 245 | ||
| 233 | impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { | 246 | impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { |
| 247 | /// Create a new GPIOTE output channel driver. | ||
| 234 | pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { | 248 | pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { |
| 235 | into_ref!(ch); | 249 | into_ref!(ch); |
| 236 | let g = regs(); | 250 | let g = regs(); |
| @@ -258,20 +272,20 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { | |||
| 258 | OutputChannel { ch, _pin: pin } | 272 | OutputChannel { ch, _pin: pin } |
| 259 | } | 273 | } |
| 260 | 274 | ||
| 261 | /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). | 275 | /// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle). |
| 262 | pub fn out(&self) { | 276 | pub fn out(&self) { |
| 263 | let g = regs(); | 277 | let g = regs(); |
| 264 | g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); | 278 | g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); |
| 265 | } | 279 | } |
| 266 | 280 | ||
| 267 | /// Triggers `task set` (set associated pin high). | 281 | /// Triggers the SET task (set associated pin high). |
| 268 | #[cfg(not(feature = "nrf51"))] | 282 | #[cfg(not(feature = "nrf51"))] |
| 269 | pub fn set(&self) { | 283 | pub fn set(&self) { |
| 270 | let g = regs(); | 284 | let g = regs(); |
| 271 | g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); | 285 | g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); |
| 272 | } | 286 | } |
| 273 | 287 | ||
| 274 | /// Triggers `task clear` (set associated pin low). | 288 | /// Triggers the CLEAR task (set associated pin low). |
| 275 | #[cfg(not(feature = "nrf51"))] | 289 | #[cfg(not(feature = "nrf51"))] |
| 276 | pub fn clear(&self) { | 290 | pub fn clear(&self) { |
| 277 | let g = regs(); | 291 | let g = regs(); |
| @@ -336,48 +350,58 @@ impl<'a> Future for PortInputFuture<'a> { | |||
| 336 | } | 350 | } |
| 337 | 351 | ||
| 338 | impl<'d, T: GpioPin> Input<'d, T> { | 352 | impl<'d, T: GpioPin> Input<'d, T> { |
| 353 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 339 | pub async fn wait_for_high(&mut self) { | 354 | pub async fn wait_for_high(&mut self) { |
| 340 | self.pin.wait_for_high().await | 355 | self.pin.wait_for_high().await |
| 341 | } | 356 | } |
| 342 | 357 | ||
| 358 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 343 | pub async fn wait_for_low(&mut self) { | 359 | pub async fn wait_for_low(&mut self) { |
| 344 | self.pin.wait_for_low().await | 360 | self.pin.wait_for_low().await |
| 345 | } | 361 | } |
| 346 | 362 | ||
| 363 | /// Wait for the pin to undergo a transition from low to high. | ||
| 347 | pub async fn wait_for_rising_edge(&mut self) { | 364 | pub async fn wait_for_rising_edge(&mut self) { |
| 348 | self.pin.wait_for_rising_edge().await | 365 | self.pin.wait_for_rising_edge().await |
| 349 | } | 366 | } |
| 350 | 367 | ||
| 368 | /// Wait for the pin to undergo a transition from high to low. | ||
| 351 | pub async fn wait_for_falling_edge(&mut self) { | 369 | pub async fn wait_for_falling_edge(&mut self) { |
| 352 | self.pin.wait_for_falling_edge().await | 370 | self.pin.wait_for_falling_edge().await |
| 353 | } | 371 | } |
| 354 | 372 | ||
| 373 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 355 | pub async fn wait_for_any_edge(&mut self) { | 374 | pub async fn wait_for_any_edge(&mut self) { |
| 356 | self.pin.wait_for_any_edge().await | 375 | self.pin.wait_for_any_edge().await |
| 357 | } | 376 | } |
| 358 | } | 377 | } |
| 359 | 378 | ||
| 360 | impl<'d, T: GpioPin> Flex<'d, T> { | 379 | impl<'d, T: GpioPin> Flex<'d, T> { |
| 380 | /// Wait until the pin is high. If it is already high, return immediately. | ||
| 361 | pub async fn wait_for_high(&mut self) { | 381 | pub async fn wait_for_high(&mut self) { |
| 362 | self.pin.conf().modify(|_, w| w.sense().high()); | 382 | self.pin.conf().modify(|_, w| w.sense().high()); |
| 363 | PortInputFuture::new(&mut self.pin).await | 383 | PortInputFuture::new(&mut self.pin).await |
| 364 | } | 384 | } |
| 365 | 385 | ||
| 386 | /// Wait until the pin is low. If it is already low, return immediately. | ||
| 366 | pub async fn wait_for_low(&mut self) { | 387 | pub async fn wait_for_low(&mut self) { |
| 367 | self.pin.conf().modify(|_, w| w.sense().low()); | 388 | self.pin.conf().modify(|_, w| w.sense().low()); |
| 368 | PortInputFuture::new(&mut self.pin).await | 389 | PortInputFuture::new(&mut self.pin).await |
| 369 | } | 390 | } |
| 370 | 391 | ||
| 392 | /// Wait for the pin to undergo a transition from low to high. | ||
| 371 | pub async fn wait_for_rising_edge(&mut self) { | 393 | pub async fn wait_for_rising_edge(&mut self) { |
| 372 | self.wait_for_low().await; | 394 | self.wait_for_low().await; |
| 373 | self.wait_for_high().await; | 395 | self.wait_for_high().await; |
| 374 | } | 396 | } |
| 375 | 397 | ||
| 398 | /// Wait for the pin to undergo a transition from high to low. | ||
| 376 | pub async fn wait_for_falling_edge(&mut self) { | 399 | pub async fn wait_for_falling_edge(&mut self) { |
| 377 | self.wait_for_high().await; | 400 | self.wait_for_high().await; |
| 378 | self.wait_for_low().await; | 401 | self.wait_for_low().await; |
| 379 | } | 402 | } |
| 380 | 403 | ||
| 404 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. | ||
| 381 | pub async fn wait_for_any_edge(&mut self) { | 405 | pub async fn wait_for_any_edge(&mut self) { |
| 382 | if self.is_high() { | 406 | if self.is_high() { |
| 383 | self.pin.conf().modify(|_, w| w.sense().low()); | 407 | self.pin.conf().modify(|_, w| w.sense().low()); |
| @@ -394,8 +418,17 @@ mod sealed { | |||
| 394 | pub trait Channel {} | 418 | pub trait Channel {} |
| 395 | } | 419 | } |
| 396 | 420 | ||
| 421 | /// GPIOTE channel trait. | ||
| 422 | /// | ||
| 423 | /// Implemented by all GPIOTE channels. | ||
| 397 | pub trait Channel: sealed::Channel + Sized { | 424 | pub trait Channel: sealed::Channel + Sized { |
| 425 | /// Get the channel number. | ||
| 398 | fn number(&self) -> usize; | 426 | fn number(&self) -> usize; |
| 427 | |||
| 428 | /// Convert this channel to a type-erased `AnyChannel`. | ||
| 429 | /// | ||
| 430 | /// This allows using several channels in situations that might require | ||
| 431 | /// them to be the same type, like putting them in an array. | ||
| 399 | fn degrade(self) -> AnyChannel { | 432 | fn degrade(self) -> AnyChannel { |
| 400 | AnyChannel { | 433 | AnyChannel { |
| 401 | number: self.number() as u8, | 434 | number: self.number() as u8, |
| @@ -403,6 +436,12 @@ pub trait Channel: sealed::Channel + Sized { | |||
| 403 | } | 436 | } |
| 404 | } | 437 | } |
| 405 | 438 | ||
| 439 | /// Type-erased channel. | ||
| 440 | /// | ||
| 441 | /// Obtained by calling `Channel::degrade`. | ||
| 442 | /// | ||
| 443 | /// This allows using several channels in situations that might require | ||
| 444 | /// them to be the same type, like putting them in an array. | ||
| 406 | pub struct AnyChannel { | 445 | pub struct AnyChannel { |
| 407 | number: u8, | 446 | number: u8, |
| 408 | } | 447 | } |
diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 7e9507751..770df7c89 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #![macro_use] | 1 | //! Inter-IC Sound (I2S) driver. |
| 2 | 2 | ||
| 3 | //! Support for I2S audio | 3 | #![macro_use] |
| 4 | 4 | ||
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| @@ -19,16 +19,23 @@ use crate::pac::i2s::RegisterBlock; | |||
| 19 | use crate::util::{slice_in_ram_or, slice_ptr_parts}; | 19 | use crate::util::{slice_in_ram_or, slice_ptr_parts}; |
| 20 | use crate::{Peripheral, EASY_DMA_SIZE}; | 20 | use crate::{Peripheral, EASY_DMA_SIZE}; |
| 21 | 21 | ||
| 22 | /// Type alias for `MultiBuffering` with 2 buffers. | ||
| 22 | pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>; | 23 | pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>; |
| 23 | 24 | ||
| 25 | /// I2S transfer error. | ||
| 24 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 26 | #[non_exhaustive] | 28 | #[non_exhaustive] |
| 27 | pub enum Error { | 29 | pub enum Error { |
| 30 | /// The buffer is too long. | ||
| 28 | BufferTooLong, | 31 | BufferTooLong, |
| 32 | /// The buffer is empty. | ||
| 29 | BufferZeroLength, | 33 | BufferZeroLength, |
| 30 | BufferNotInDataMemory, | 34 | /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. |
| 35 | BufferNotInRAM, | ||
| 36 | /// The buffer address is not aligned. | ||
| 31 | BufferMisaligned, | 37 | BufferMisaligned, |
| 38 | /// The buffer length is not a multiple of the alignment. | ||
| 32 | BufferLengthMisaligned, | 39 | BufferLengthMisaligned, |
| 33 | } | 40 | } |
| 34 | 41 | ||
| @@ -36,34 +43,16 @@ pub enum Error { | |||
| 36 | #[derive(Clone)] | 43 | #[derive(Clone)] |
| 37 | #[non_exhaustive] | 44 | #[non_exhaustive] |
| 38 | pub struct Config { | 45 | pub struct Config { |
| 46 | /// Sample width | ||
| 39 | pub sample_width: SampleWidth, | 47 | pub sample_width: SampleWidth, |
| 48 | /// Alignment | ||
| 40 | pub align: Align, | 49 | pub align: Align, |
| 50 | /// Sample format | ||
| 41 | pub format: Format, | 51 | pub format: Format, |
| 52 | /// Channel configuration. | ||
| 42 | pub channels: Channels, | 53 | pub channels: Channels, |
| 43 | } | 54 | } |
| 44 | 55 | ||
| 45 | impl Config { | ||
| 46 | pub fn sample_width(mut self, sample_width: SampleWidth) -> Self { | ||
| 47 | self.sample_width = sample_width; | ||
| 48 | self | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn align(mut self, align: Align) -> Self { | ||
| 52 | self.align = align; | ||
| 53 | self | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn format(mut self, format: Format) -> Self { | ||
| 57 | self.format = format; | ||
| 58 | self | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn channels(mut self, channels: Channels) -> Self { | ||
| 62 | self.channels = channels; | ||
| 63 | self | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Default for Config { | 56 | impl Default for Config { |
| 68 | fn default() -> Self { | 57 | fn default() -> Self { |
| 69 | Self { | 58 | Self { |
| @@ -75,7 +64,7 @@ impl Default for Config { | |||
| 75 | } | 64 | } |
| 76 | } | 65 | } |
| 77 | 66 | ||
| 78 | /// I2S Mode | 67 | /// I2S clock configuration. |
| 79 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 68 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 80 | pub struct MasterClock { | 69 | pub struct MasterClock { |
| 81 | freq: MckFreq, | 70 | freq: MckFreq, |
| @@ -83,12 +72,14 @@ pub struct MasterClock { | |||
| 83 | } | 72 | } |
| 84 | 73 | ||
| 85 | impl MasterClock { | 74 | impl MasterClock { |
| 75 | /// Create a new `MasterClock`. | ||
| 86 | pub fn new(freq: MckFreq, ratio: Ratio) -> Self { | 76 | pub fn new(freq: MckFreq, ratio: Ratio) -> Self { |
| 87 | Self { freq, ratio } | 77 | Self { freq, ratio } |
| 88 | } | 78 | } |
| 89 | } | 79 | } |
| 90 | 80 | ||
| 91 | impl MasterClock { | 81 | impl MasterClock { |
| 82 | /// Get the sample rate for this clock configuration. | ||
| 92 | pub fn sample_rate(&self) -> u32 { | 83 | pub fn sample_rate(&self) -> u32 { |
| 93 | self.freq.to_frequency() / self.ratio.to_divisor() | 84 | self.freq.to_frequency() / self.ratio.to_divisor() |
| 94 | } | 85 | } |
| @@ -97,18 +88,31 @@ impl MasterClock { | |||
| 97 | /// Master clock generator frequency. | 88 | /// Master clock generator frequency. |
| 98 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 89 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 99 | pub enum MckFreq { | 90 | pub enum MckFreq { |
| 91 | /// 32 Mhz / 8 = 4000.00 kHz | ||
| 100 | _32MDiv8, | 92 | _32MDiv8, |
| 93 | /// 32 Mhz / 10 = 3200.00 kHz | ||
| 101 | _32MDiv10, | 94 | _32MDiv10, |
| 95 | /// 32 Mhz / 11 = 2909.09 kHz | ||
| 102 | _32MDiv11, | 96 | _32MDiv11, |
| 97 | /// 32 Mhz / 15 = 2133.33 kHz | ||
| 103 | _32MDiv15, | 98 | _32MDiv15, |
| 99 | /// 32 Mhz / 16 = 2000.00 kHz | ||
| 104 | _32MDiv16, | 100 | _32MDiv16, |
| 101 | /// 32 Mhz / 21 = 1523.81 kHz | ||
| 105 | _32MDiv21, | 102 | _32MDiv21, |
| 103 | /// 32 Mhz / 23 = 1391.30 kHz | ||
| 106 | _32MDiv23, | 104 | _32MDiv23, |
| 105 | /// 32 Mhz / 30 = 1066.67 kHz | ||
| 107 | _32MDiv30, | 106 | _32MDiv30, |
| 107 | /// 32 Mhz / 31 = 1032.26 kHz | ||
| 108 | _32MDiv31, | 108 | _32MDiv31, |
| 109 | /// 32 Mhz / 32 = 1000.00 kHz | ||
| 109 | _32MDiv32, | 110 | _32MDiv32, |
| 111 | /// 32 Mhz / 42 = 761.90 kHz | ||
| 110 | _32MDiv42, | 112 | _32MDiv42, |
| 113 | /// 32 Mhz / 63 = 507.94 kHz | ||
| 111 | _32MDiv63, | 114 | _32MDiv63, |
| 115 | /// 32 Mhz / 125 = 256.00 kHz | ||
| 112 | _32MDiv125, | 116 | _32MDiv125, |
| 113 | } | 117 | } |
| 114 | 118 | ||
| @@ -146,14 +150,23 @@ impl From<MckFreq> for usize { | |||
| 146 | /// | 150 | /// |
| 147 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 151 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 148 | pub enum Ratio { | 152 | pub enum Ratio { |
| 153 | /// Divide by 32 | ||
| 149 | _32x, | 154 | _32x, |
| 155 | /// Divide by 48 | ||
| 150 | _48x, | 156 | _48x, |
| 157 | /// Divide by 64 | ||
| 151 | _64x, | 158 | _64x, |
| 159 | /// Divide by 96 | ||
| 152 | _96x, | 160 | _96x, |
| 161 | /// Divide by 128 | ||
| 153 | _128x, | 162 | _128x, |
| 163 | /// Divide by 192 | ||
| 154 | _192x, | 164 | _192x, |
| 165 | /// Divide by 256 | ||
| 155 | _256x, | 166 | _256x, |
| 167 | /// Divide by 384 | ||
| 156 | _384x, | 168 | _384x, |
| 169 | /// Divide by 512 | ||
| 157 | _512x, | 170 | _512x, |
| 158 | } | 171 | } |
| 159 | 172 | ||
| @@ -165,6 +178,7 @@ impl Ratio { | |||
| 165 | usize::from(*self) as u8 | 178 | usize::from(*self) as u8 |
| 166 | } | 179 | } |
| 167 | 180 | ||
| 181 | /// Return the divisor for this ratio | ||
| 168 | pub fn to_divisor(&self) -> u32 { | 182 | pub fn to_divisor(&self) -> u32 { |
| 169 | Self::RATIOS[usize::from(*self)] | 183 | Self::RATIOS[usize::from(*self)] |
| 170 | } | 184 | } |
| @@ -183,11 +197,17 @@ impl From<Ratio> for usize { | |||
| 183 | /// For custom master clock configuration, please refer to [MasterClock]. | 197 | /// For custom master clock configuration, please refer to [MasterClock]. |
| 184 | #[derive(Clone, Copy)] | 198 | #[derive(Clone, Copy)] |
| 185 | pub enum ApproxSampleRate { | 199 | pub enum ApproxSampleRate { |
| 200 | /// 11025 Hz | ||
| 186 | _11025, | 201 | _11025, |
| 202 | /// 16000 Hz | ||
| 187 | _16000, | 203 | _16000, |
| 204 | /// 22050 Hz | ||
| 188 | _22050, | 205 | _22050, |
| 206 | /// 32000 Hz | ||
| 189 | _32000, | 207 | _32000, |
| 208 | /// 44100 Hz | ||
| 190 | _44100, | 209 | _44100, |
| 210 | /// 48000 Hz | ||
| 191 | _48000, | 211 | _48000, |
| 192 | } | 212 | } |
| 193 | 213 | ||
| @@ -211,6 +231,7 @@ impl From<ApproxSampleRate> for MasterClock { | |||
| 211 | } | 231 | } |
| 212 | 232 | ||
| 213 | impl ApproxSampleRate { | 233 | impl ApproxSampleRate { |
| 234 | /// Get the sample rate as an integer. | ||
| 214 | pub fn sample_rate(&self) -> u32 { | 235 | pub fn sample_rate(&self) -> u32 { |
| 215 | MasterClock::from(*self).sample_rate() | 236 | MasterClock::from(*self).sample_rate() |
| 216 | } | 237 | } |
| @@ -223,20 +244,32 @@ impl ApproxSampleRate { | |||
| 223 | /// For custom master clock configuration, please refer to [Mode]. | 244 | /// For custom master clock configuration, please refer to [Mode]. |
| 224 | #[derive(Clone, Copy)] | 245 | #[derive(Clone, Copy)] |
| 225 | pub enum ExactSampleRate { | 246 | pub enum ExactSampleRate { |
| 247 | /// 8000 Hz | ||
| 226 | _8000, | 248 | _8000, |
| 249 | /// 10582 Hz | ||
| 227 | _10582, | 250 | _10582, |
| 251 | /// 12500 Hz | ||
| 228 | _12500, | 252 | _12500, |
| 253 | /// 15625 Hz | ||
| 229 | _15625, | 254 | _15625, |
| 255 | /// 15873 Hz | ||
| 230 | _15873, | 256 | _15873, |
| 257 | /// 25000 Hz | ||
| 231 | _25000, | 258 | _25000, |
| 259 | /// 31250 Hz | ||
| 232 | _31250, | 260 | _31250, |
| 261 | /// 50000 Hz | ||
| 233 | _50000, | 262 | _50000, |
| 263 | /// 62500 Hz | ||
| 234 | _62500, | 264 | _62500, |
| 265 | /// 100000 Hz | ||
| 235 | _100000, | 266 | _100000, |
| 267 | /// 125000 Hz | ||
| 236 | _125000, | 268 | _125000, |
| 237 | } | 269 | } |
| 238 | 270 | ||
| 239 | impl ExactSampleRate { | 271 | impl ExactSampleRate { |
| 272 | /// Get the sample rate as an integer. | ||
| 240 | pub fn sample_rate(&self) -> u32 { | 273 | pub fn sample_rate(&self) -> u32 { |
| 241 | MasterClock::from(*self).sample_rate() | 274 | MasterClock::from(*self).sample_rate() |
| 242 | } | 275 | } |
| @@ -263,8 +296,11 @@ impl From<ExactSampleRate> for MasterClock { | |||
| 263 | /// Sample width. | 296 | /// Sample width. |
| 264 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 297 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 265 | pub enum SampleWidth { | 298 | pub enum SampleWidth { |
| 299 | /// 8 bit samples. | ||
| 266 | _8bit, | 300 | _8bit, |
| 301 | /// 16 bit samples. | ||
| 267 | _16bit, | 302 | _16bit, |
| 303 | /// 24 bit samples. | ||
| 268 | _24bit, | 304 | _24bit, |
| 269 | } | 305 | } |
| 270 | 306 | ||
| @@ -277,7 +313,9 @@ impl From<SampleWidth> for u8 { | |||
| 277 | /// Channel used for the most significant sample value in a frame. | 313 | /// Channel used for the most significant sample value in a frame. |
| 278 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 314 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 279 | pub enum Align { | 315 | pub enum Align { |
| 316 | /// Left-align samples. | ||
| 280 | Left, | 317 | Left, |
| 318 | /// Right-align samples. | ||
| 281 | Right, | 319 | Right, |
| 282 | } | 320 | } |
| 283 | 321 | ||
| @@ -293,7 +331,9 @@ impl From<Align> for bool { | |||
| 293 | /// Frame format. | 331 | /// Frame format. |
| 294 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 332 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 295 | pub enum Format { | 333 | pub enum Format { |
| 334 | /// I2S frame format | ||
| 296 | I2S, | 335 | I2S, |
| 336 | /// Aligned frame format | ||
| 297 | Aligned, | 337 | Aligned, |
| 298 | } | 338 | } |
| 299 | 339 | ||
| @@ -309,8 +349,11 @@ impl From<Format> for bool { | |||
| 309 | /// Channels | 349 | /// Channels |
| 310 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 350 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 311 | pub enum Channels { | 351 | pub enum Channels { |
| 352 | /// Stereo (2 channels). | ||
| 312 | Stereo, | 353 | Stereo, |
| 354 | /// Mono, left channel only. | ||
| 313 | MonoLeft, | 355 | MonoLeft, |
| 356 | /// Mono, right channel only. | ||
| 314 | MonoRight, | 357 | MonoRight, |
| 315 | } | 358 | } |
| 316 | 359 | ||
| @@ -320,7 +363,7 @@ impl From<Channels> for u8 { | |||
| 320 | } | 363 | } |
| 321 | } | 364 | } |
| 322 | 365 | ||
| 323 | /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. | 366 | /// I2S driver. |
| 324 | pub struct I2S<'d, T: Instance> { | 367 | pub struct I2S<'d, T: Instance> { |
| 325 | i2s: PeripheralRef<'d, T>, | 368 | i2s: PeripheralRef<'d, T>, |
| 326 | irq: PeripheralRef<'d, T::Interrupt>, | 369 | irq: PeripheralRef<'d, T::Interrupt>, |
| @@ -566,7 +609,7 @@ impl<'d, T: Instance> I2S<'d, T> { | |||
| 566 | { | 609 | { |
| 567 | trace!("SEND: {}", buffer_ptr as *const S as u32); | 610 | trace!("SEND: {}", buffer_ptr as *const S as u32); |
| 568 | 611 | ||
| 569 | slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; | 612 | slice_in_ram_or(buffer_ptr, Error::BufferNotInRAM)?; |
| 570 | 613 | ||
| 571 | compiler_fence(Ordering::SeqCst); | 614 | compiler_fence(Ordering::SeqCst); |
| 572 | 615 | ||
| @@ -1003,7 +1046,10 @@ impl<T: Instance> Device<T> { | |||
| 1003 | 1046 | ||
| 1004 | /// Sample details | 1047 | /// Sample details |
| 1005 | pub trait Sample: Sized + Copy + Default { | 1048 | pub trait Sample: Sized + Copy + Default { |
| 1049 | /// Width of this sample type. | ||
| 1006 | const WIDTH: usize; | 1050 | const WIDTH: usize; |
| 1051 | |||
| 1052 | /// Scale of this sample. | ||
| 1007 | const SCALE: Self; | 1053 | const SCALE: Self; |
| 1008 | } | 1054 | } |
| 1009 | 1055 | ||
| @@ -1022,12 +1068,13 @@ impl Sample for i32 { | |||
| 1022 | const SCALE: Self = 1 << (Self::WIDTH - 1); | 1068 | const SCALE: Self = 1 << (Self::WIDTH - 1); |
| 1023 | } | 1069 | } |
| 1024 | 1070 | ||
| 1025 | /// A 4-bytes aligned buffer. | 1071 | /// A 4-bytes aligned buffer. Needed for DMA access. |
| 1026 | #[derive(Clone, Copy)] | 1072 | #[derive(Clone, Copy)] |
| 1027 | #[repr(align(4))] | 1073 | #[repr(align(4))] |
| 1028 | pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]); | 1074 | pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]); |
| 1029 | 1075 | ||
| 1030 | impl<T: Sample, const N: usize> AlignedBuffer<T, N> { | 1076 | impl<T: Sample, const N: usize> AlignedBuffer<T, N> { |
| 1077 | /// Create a new `AlignedBuffer`. | ||
| 1031 | pub fn new(array: [T; N]) -> Self { | 1078 | pub fn new(array: [T; N]) -> Self { |
| 1032 | Self(array) | 1079 | Self(array) |
| 1033 | } | 1080 | } |
| @@ -1052,12 +1099,14 @@ impl<T: Sample, const N: usize> DerefMut for AlignedBuffer<T, N> { | |||
| 1052 | } | 1099 | } |
| 1053 | } | 1100 | } |
| 1054 | 1101 | ||
| 1102 | /// Set of multiple buffers, for multi-buffering transfers. | ||
| 1055 | pub struct MultiBuffering<S: Sample, const NB: usize, const NS: usize> { | 1103 | pub struct MultiBuffering<S: Sample, const NB: usize, const NS: usize> { |
| 1056 | buffers: [AlignedBuffer<S, NS>; NB], | 1104 | buffers: [AlignedBuffer<S, NS>; NB], |
| 1057 | index: usize, | 1105 | index: usize, |
| 1058 | } | 1106 | } |
| 1059 | 1107 | ||
| 1060 | impl<S: Sample, const NB: usize, const NS: usize> MultiBuffering<S, NB, NS> { | 1108 | impl<S: Sample, const NB: usize, const NS: usize> MultiBuffering<S, NB, NS> { |
| 1109 | /// Create a new `MultiBuffering`. | ||
| 1061 | pub fn new() -> Self { | 1110 | pub fn new() -> Self { |
| 1062 | assert!(NB > 1); | 1111 | assert!(NB > 1); |
| 1063 | Self { | 1112 | Self { |
| @@ -1119,7 +1168,9 @@ pub(crate) mod sealed { | |||
| 1119 | } | 1168 | } |
| 1120 | } | 1169 | } |
| 1121 | 1170 | ||
| 1171 | /// I2S peripehral instance. | ||
| 1122 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | 1172 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { |
| 1173 | /// Interrupt for this peripheral. | ||
| 1123 | type Interrupt: Interrupt; | 1174 | type Interrupt: Interrupt; |
| 1124 | } | 1175 | } |
| 1125 | 1176 | ||
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 1dd0e7905..20e70a248 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -1,53 +1,11 @@ | |||
| 1 | //! # Embassy nRF HAL | ||
| 2 | //! | ||
| 3 | //! HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. | ||
| 4 | //! | ||
| 5 | //! The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs | ||
| 6 | //! for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to | ||
| 7 | //! complete operations in low power mod and handling interrupts, so that applications can focus on more important matters. | ||
| 8 | //! | ||
| 9 | //! ## EasyDMA considerations | ||
| 10 | //! | ||
| 11 | //! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting | ||
| 12 | //! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). | ||
| 13 | //! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust | ||
| 14 | //! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: | ||
| 15 | //! | ||
| 16 | //! ```no_run | ||
| 17 | //! // As we pass a slice to the function whose contents will not ever change, | ||
| 18 | //! // the compiler writes it into the flash and thus the pointer to it will | ||
| 19 | //! // reference static memory. Since EasyDMA requires slices to reside in RAM, | ||
| 20 | //! // this function call will fail. | ||
| 21 | //! let result = spim.write_from_ram(&[1, 2, 3]); | ||
| 22 | //! assert_eq!(result, Err(Error::DMABufferNotInDataMemory)); | ||
| 23 | //! | ||
| 24 | //! // The data is still static and located in flash. However, since we are assigning | ||
| 25 | //! // it to a variable, the compiler will load it into memory. Passing a reference to the | ||
| 26 | //! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. | ||
| 27 | //! // This function call succeeds. | ||
| 28 | //! let data = [1, 2, 3]; | ||
| 29 | //! let result = spim.write_from_ram(&data); | ||
| 30 | //! assert!(result.is_ok()); | ||
| 31 | //! ``` | ||
| 32 | //! | ||
| 33 | //! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: | ||
| 34 | //! - 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. | ||
| 35 | //! - 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. | ||
| 36 | //! | ||
| 37 | //! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will | ||
| 38 | //! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit | ||
| 39 | //! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage | ||
| 40 | //! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). | ||
| 41 | //! | ||
| 42 | //! 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 | ||
| 43 | //! mutable slices always reside in RAM. | ||
| 44 | |||
| 45 | #![no_std] | 1 | #![no_std] |
| 46 | #![cfg_attr( | 2 | #![cfg_attr( |
| 47 | feature = "nightly", | 3 | feature = "nightly", |
| 48 | feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) | 4 | feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) |
| 49 | )] | 5 | )] |
| 50 | #![cfg_attr(feature = "nightly", allow(incomplete_features))] | 6 | #![cfg_attr(feature = "nightly", allow(incomplete_features))] |
| 7 | #![doc = include_str!("../README.md")] | ||
| 8 | #![warn(missing_docs)] | ||
| 51 | 9 | ||
| 52 | #[cfg(not(any( | 10 | #[cfg(not(any( |
| 53 | feature = "nrf51", | 11 | feature = "nrf51", |
diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 405ea3171..c1ffa31aa 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! Non-Volatile Memory Controller (NVMC) module. | 1 | //! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver. |
| 2 | 2 | ||
| 3 | use core::{ptr, slice}; | 3 | use core::{ptr, slice}; |
| 4 | 4 | ||
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index b7c7022cf..54feca4c1 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! PDM mirophone interface | 1 | //! Pulse Density Modulation (PDM) mirophone driver. |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{compiler_fence, Ordering}; |
| @@ -22,12 +22,16 @@ pub struct Pdm<'d> { | |||
| 22 | phantom: PhantomData<&'d PDM>, | 22 | phantom: PhantomData<&'d PDM>, |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | /// PDM error. | ||
| 25 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 27 | #[non_exhaustive] | 28 | #[non_exhaustive] |
| 28 | pub enum Error { | 29 | pub enum Error { |
| 30 | /// Buffer is too long. | ||
| 29 | BufferTooLong, | 31 | BufferTooLong, |
| 32 | /// Buffer is empty | ||
| 30 | BufferZeroLength, | 33 | BufferZeroLength, |
| 34 | /// PDM is not running | ||
| 31 | NotRunning, | 35 | NotRunning, |
| 32 | } | 36 | } |
| 33 | 37 | ||
| @@ -119,6 +123,7 @@ impl<'d> Pdm<'d> { | |||
| 119 | r.events_started.reset(); | 123 | r.events_started.reset(); |
| 120 | } | 124 | } |
| 121 | 125 | ||
| 126 | /// Sample data into the given buffer. | ||
| 122 | pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { | 127 | pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { |
| 123 | if buffer.len() == 0 { | 128 | if buffer.len() == 0 { |
| 124 | return Err(Error::BufferZeroLength); | 129 | return Err(Error::BufferZeroLength); |
| @@ -215,14 +220,21 @@ impl Default for Config { | |||
| 215 | } | 220 | } |
| 216 | } | 221 | } |
| 217 | 222 | ||
| 223 | /// PDM operation mode. | ||
| 218 | #[derive(PartialEq)] | 224 | #[derive(PartialEq)] |
| 219 | pub enum OperationMode { | 225 | pub enum OperationMode { |
| 226 | /// Mono (1 channel) | ||
| 220 | Mono, | 227 | Mono, |
| 228 | /// Stereo (2 channels) | ||
| 221 | Stereo, | 229 | Stereo, |
| 222 | } | 230 | } |
| 231 | |||
| 232 | /// PDM edge polarity | ||
| 223 | #[derive(PartialEq)] | 233 | #[derive(PartialEq)] |
| 224 | pub enum Edge { | 234 | pub enum Edge { |
| 235 | /// Left edge is rising | ||
| 225 | LeftRising, | 236 | LeftRising, |
| 237 | /// Left edge is falling | ||
| 226 | LeftFalling, | 238 | LeftFalling, |
| 227 | } | 239 | } |
| 228 | 240 | ||
diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index de856c0ca..0908cd7be 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs | |||
| @@ -11,12 +11,14 @@ fn regs() -> &'static pac::dppic::RegisterBlock { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { | 13 | impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { |
| 14 | /// Configure PPI channel to trigger `task` on `event`. | ||
| 14 | pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self { | 15 | pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self { |
| 15 | Ppi::new_many_to_many(ch, [event], [task]) | 16 | Ppi::new_many_to_many(ch, [event], [task]) |
| 16 | } | 17 | } |
| 17 | } | 18 | } |
| 18 | 19 | ||
| 19 | impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { | 20 | impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { |
| 21 | /// Configure PPI channel to trigger both `task1` and `task2` on `event`. | ||
| 20 | pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self { | 22 | pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self { |
| 21 | Ppi::new_many_to_many(ch, [event], [task1, task2]) | 23 | Ppi::new_many_to_many(ch, [event], [task1, task2]) |
| 22 | } | 24 | } |
| @@ -25,6 +27,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { | |||
| 25 | impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize> | 27 | impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize> |
| 26 | Ppi<'d, C, EVENT_COUNT, TASK_COUNT> | 28 | Ppi<'d, C, EVENT_COUNT, TASK_COUNT> |
| 27 | { | 29 | { |
| 30 | /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires. | ||
| 28 | pub fn new_many_to_many( | 31 | pub fn new_many_to_many( |
| 29 | ch: impl Peripheral<P = C> + 'd, | 32 | ch: impl Peripheral<P = C> + 'd, |
| 30 | events: [Event; EVENT_COUNT], | 33 | events: [Event; EVENT_COUNT], |
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 8f5ed14cd..b76eccf0b 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | //! HAL interface for the PPI and DPPI peripheral. | 3 | //! Programmable Peripheral Interconnect (PPI/DPPI) driver. |
| 4 | //! | 4 | //! |
| 5 | //! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability | 5 | //! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability |
| 6 | //! between peripherals through their events and tasks. There are fixed PPI channels and fully | 6 | //! between peripherals through their events and tasks. There are fixed PPI channels and fully |
diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 19abc4e18..a96ab50b7 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs | |||
| @@ -48,7 +48,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { | |||
| 48 | 48 | ||
| 49 | #[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task | 49 | #[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task |
| 50 | impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { | 50 | impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { |
| 51 | /// Configure PPI channel to trigger `task1` and `task2` on `event`. | 51 | /// Configure PPI channel to trigger both `task1` and `task2` on `event`. |
| 52 | pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self { | 52 | pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self { |
| 53 | into_ref!(ch); | 53 | into_ref!(ch); |
| 54 | 54 | ||
diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 5f750a91e..708f23104 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Pulse Width Modulation (PWM) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 5 | use core::sync::atomic::{compiler_fence, Ordering}; |
| @@ -32,6 +34,7 @@ pub struct SequencePwm<'d, T: Instance> { | |||
| 32 | ch3: Option<PeripheralRef<'d, AnyPin>>, | 34 | ch3: Option<PeripheralRef<'d, AnyPin>>, |
| 33 | } | 35 | } |
| 34 | 36 | ||
| 37 | /// PWM error | ||
| 35 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 38 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 36 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 37 | #[non_exhaustive] | 40 | #[non_exhaustive] |
| @@ -41,7 +44,7 @@ pub enum Error { | |||
| 41 | /// Min Sequence count is 1 | 44 | /// Min Sequence count is 1 |
| 42 | SequenceTimesAtLeastOne, | 45 | SequenceTimesAtLeastOne, |
| 43 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. | 46 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. |
| 44 | DMABufferNotInDataMemory, | 47 | BufferNotInRAM, |
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | const MAX_SEQUENCE_LEN: usize = 32767; | 50 | const MAX_SEQUENCE_LEN: usize = 32767; |
| @@ -358,6 +361,7 @@ pub struct Sequence<'s> { | |||
| 358 | } | 361 | } |
| 359 | 362 | ||
| 360 | impl<'s> Sequence<'s> { | 363 | impl<'s> Sequence<'s> { |
| 364 | /// Create a new `Sequence` | ||
| 361 | pub fn new(words: &'s [u16], config: SequenceConfig) -> Self { | 365 | pub fn new(words: &'s [u16], config: SequenceConfig) -> Self { |
| 362 | Self { words, config } | 366 | Self { words, config } |
| 363 | } | 367 | } |
| @@ -367,7 +371,7 @@ impl<'s> Sequence<'s> { | |||
| 367 | /// Takes at one sequence along with its configuration. | 371 | /// Takes at one sequence along with its configuration. |
| 368 | #[non_exhaustive] | 372 | #[non_exhaustive] |
| 369 | pub struct SingleSequencer<'d, 's, T: Instance> { | 373 | pub struct SingleSequencer<'d, 's, T: Instance> { |
| 370 | pub sequencer: Sequencer<'d, 's, T>, | 374 | sequencer: Sequencer<'d, 's, T>, |
| 371 | } | 375 | } |
| 372 | 376 | ||
| 373 | impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { | 377 | impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { |
| @@ -428,8 +432,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { | |||
| 428 | let sequence0 = &self.sequence0; | 432 | let sequence0 = &self.sequence0; |
| 429 | let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0); | 433 | let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0); |
| 430 | 434 | ||
| 431 | slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?; | 435 | slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?; |
| 432 | slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?; | 436 | slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?; |
| 433 | 437 | ||
| 434 | if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { | 438 | if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { |
| 435 | return Err(Error::SequenceTooLong); | 439 | return Err(Error::SequenceTooLong); |
| @@ -536,13 +540,21 @@ pub enum SequenceMode { | |||
| 536 | /// PWM Base clock is system clock (16MHz) divided by prescaler | 540 | /// PWM Base clock is system clock (16MHz) divided by prescaler |
| 537 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 541 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 538 | pub enum Prescaler { | 542 | pub enum Prescaler { |
| 543 | /// Divide by 1 | ||
| 539 | Div1, | 544 | Div1, |
| 545 | /// Divide by 2 | ||
| 540 | Div2, | 546 | Div2, |
| 547 | /// Divide by 4 | ||
| 541 | Div4, | 548 | Div4, |
| 549 | /// Divide by 8 | ||
| 542 | Div8, | 550 | Div8, |
| 551 | /// Divide by 16 | ||
| 543 | Div16, | 552 | Div16, |
| 553 | /// Divide by 32 | ||
| 544 | Div32, | 554 | Div32, |
| 555 | /// Divide by 64 | ||
| 545 | Div64, | 556 | Div64, |
| 557 | /// Divide by 128 | ||
| 546 | Div128, | 558 | Div128, |
| 547 | } | 559 | } |
| 548 | 560 | ||
| @@ -828,7 +840,9 @@ pub(crate) mod sealed { | |||
| 828 | } | 840 | } |
| 829 | } | 841 | } |
| 830 | 842 | ||
| 843 | /// PWM peripheral instance. | ||
| 831 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { | 844 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { |
| 845 | /// Interrupt for this peripheral. | ||
| 832 | type Interrupt: Interrupt; | 846 | type Interrupt: Interrupt; |
| 833 | } | 847 | } |
| 834 | 848 | ||
diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 253c85c32..c01babca3 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! Quadrature decoder interface | 1 | //! Quadrature decoder (QDEC) driver. |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| @@ -12,17 +12,23 @@ use crate::interrupt::InterruptExt; | |||
| 12 | use crate::peripherals::QDEC; | 12 | use crate::peripherals::QDEC; |
| 13 | use crate::{interrupt, pac, Peripheral}; | 13 | use crate::{interrupt, pac, Peripheral}; |
| 14 | 14 | ||
| 15 | /// Quadrature decoder | 15 | /// Quadrature decoder driver. |
| 16 | pub struct Qdec<'d> { | 16 | pub struct Qdec<'d> { |
| 17 | _p: PeripheralRef<'d, QDEC>, | 17 | _p: PeripheralRef<'d, QDEC>, |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | /// QDEC config | ||
| 20 | #[non_exhaustive] | 21 | #[non_exhaustive] |
| 21 | pub struct Config { | 22 | pub struct Config { |
| 23 | /// Number of samples | ||
| 22 | pub num_samples: NumSamples, | 24 | pub num_samples: NumSamples, |
| 25 | /// Sample period | ||
| 23 | pub period: SamplePeriod, | 26 | pub period: SamplePeriod, |
| 27 | /// Set LED output pin polarity | ||
| 24 | pub led_polarity: LedPolarity, | 28 | pub led_polarity: LedPolarity, |
| 29 | /// Enable/disable input debounce filters | ||
| 25 | pub debounce: bool, | 30 | pub debounce: bool, |
| 31 | /// Time period the LED is switched ON prior to sampling (0..511 us). | ||
| 26 | pub led_pre_usecs: u16, | 32 | pub led_pre_usecs: u16, |
| 27 | } | 33 | } |
| 28 | 34 | ||
| @@ -41,6 +47,7 @@ impl Default for Config { | |||
| 41 | static WAKER: AtomicWaker = AtomicWaker::new(); | 47 | static WAKER: AtomicWaker = AtomicWaker::new(); |
| 42 | 48 | ||
| 43 | impl<'d> Qdec<'d> { | 49 | impl<'d> Qdec<'d> { |
| 50 | /// Create a new QDEC. | ||
| 44 | pub fn new( | 51 | pub fn new( |
| 45 | qdec: impl Peripheral<P = QDEC> + 'd, | 52 | qdec: impl Peripheral<P = QDEC> + 'd, |
| 46 | irq: impl Peripheral<P = interrupt::QDEC> + 'd, | 53 | irq: impl Peripheral<P = interrupt::QDEC> + 'd, |
| @@ -52,6 +59,7 @@ impl<'d> Qdec<'d> { | |||
| 52 | Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) | 59 | Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) |
| 53 | } | 60 | } |
| 54 | 61 | ||
| 62 | /// Create a new QDEC, with a pin for LED output. | ||
| 55 | pub fn new_with_led( | 63 | pub fn new_with_led( |
| 56 | qdec: impl Peripheral<P = QDEC> + 'd, | 64 | qdec: impl Peripheral<P = QDEC> + 'd, |
| 57 | irq: impl Peripheral<P = interrupt::QDEC> + 'd, | 65 | irq: impl Peripheral<P = interrupt::QDEC> + 'd, |
| @@ -170,36 +178,61 @@ impl<'d> Qdec<'d> { | |||
| 170 | } | 178 | } |
| 171 | } | 179 | } |
| 172 | 180 | ||
| 181 | /// Sample period | ||
| 173 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 182 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 174 | pub enum SamplePeriod { | 183 | pub enum SamplePeriod { |
| 184 | /// 128 us | ||
| 175 | _128us, | 185 | _128us, |
| 186 | /// 256 us | ||
| 176 | _256us, | 187 | _256us, |
| 188 | /// 512 us | ||
| 177 | _512us, | 189 | _512us, |
| 190 | /// 1024 us | ||
| 178 | _1024us, | 191 | _1024us, |
| 192 | /// 2048 us | ||
| 179 | _2048us, | 193 | _2048us, |
| 194 | /// 4096 us | ||
| 180 | _4096us, | 195 | _4096us, |
| 196 | /// 8192 us | ||
| 181 | _8192us, | 197 | _8192us, |
| 198 | /// 16384 us | ||
| 182 | _16384us, | 199 | _16384us, |
| 200 | /// 32 ms | ||
| 183 | _32ms, | 201 | _32ms, |
| 202 | /// 65 ms | ||
| 184 | _65ms, | 203 | _65ms, |
| 204 | /// 131 ms | ||
| 185 | _131ms, | 205 | _131ms, |
| 186 | } | 206 | } |
| 187 | 207 | ||
| 208 | /// Number of samples taken. | ||
| 188 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 209 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 189 | pub enum NumSamples { | 210 | pub enum NumSamples { |
| 211 | /// 10 samples | ||
| 190 | _10smpl, | 212 | _10smpl, |
| 213 | /// 40 samples | ||
| 191 | _40smpl, | 214 | _40smpl, |
| 215 | /// 80 samples | ||
| 192 | _80smpl, | 216 | _80smpl, |
| 217 | /// 120 samples | ||
| 193 | _120smpl, | 218 | _120smpl, |
| 219 | /// 160 samples | ||
| 194 | _160smpl, | 220 | _160smpl, |
| 221 | /// 200 samples | ||
| 195 | _200smpl, | 222 | _200smpl, |
| 223 | /// 240 samples | ||
| 196 | _240smpl, | 224 | _240smpl, |
| 225 | /// 280 samples | ||
| 197 | _280smpl, | 226 | _280smpl, |
| 227 | /// 1 sample | ||
| 198 | _1smpl, | 228 | _1smpl, |
| 199 | } | 229 | } |
| 200 | 230 | ||
| 231 | /// LED polarity | ||
| 201 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] | 232 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] |
| 202 | pub enum LedPolarity { | 233 | pub enum LedPolarity { |
| 234 | /// Active high (a high output turns on the LED). | ||
| 203 | ActiveHigh, | 235 | ActiveHigh, |
| 236 | /// Active low (a low output turns on the LED). | ||
| 204 | ActiveLow, | 237 | ActiveLow, |
| 205 | } | 238 | } |
diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index ea0a17031..07a970018 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Quad Serial Peripheral Interface (QSPI) flash driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| @@ -15,6 +17,7 @@ pub use crate::pac::qspi::ifconfig0::{ | |||
| 15 | pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; | 17 | pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; |
| 16 | use crate::{pac, Peripheral}; | 18 | use crate::{pac, Peripheral}; |
| 17 | 19 | ||
| 20 | /// Deep power-down config. | ||
| 18 | pub struct DeepPowerDownConfig { | 21 | pub struct DeepPowerDownConfig { |
| 19 | /// Time required for entering DPM, in units of 16us | 22 | /// Time required for entering DPM, in units of 16us |
| 20 | pub enter_time: u16, | 23 | pub enter_time: u16, |
| @@ -22,37 +25,62 @@ pub struct DeepPowerDownConfig { | |||
| 22 | pub exit_time: u16, | 25 | pub exit_time: u16, |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 28 | /// QSPI bus frequency. | ||
| 25 | pub enum Frequency { | 29 | pub enum Frequency { |
| 30 | /// 32 Mhz | ||
| 26 | M32 = 0, | 31 | M32 = 0, |
| 32 | /// 16 Mhz | ||
| 27 | M16 = 1, | 33 | M16 = 1, |
| 34 | /// 10.7 Mhz | ||
| 28 | M10_7 = 2, | 35 | M10_7 = 2, |
| 36 | /// 8 Mhz | ||
| 29 | M8 = 3, | 37 | M8 = 3, |
| 38 | /// 6.4 Mhz | ||
| 30 | M6_4 = 4, | 39 | M6_4 = 4, |
| 40 | /// 5.3 Mhz | ||
| 31 | M5_3 = 5, | 41 | M5_3 = 5, |
| 42 | /// 4.6 Mhz | ||
| 32 | M4_6 = 6, | 43 | M4_6 = 6, |
| 44 | /// 4 Mhz | ||
| 33 | M4 = 7, | 45 | M4 = 7, |
| 46 | /// 3.6 Mhz | ||
| 34 | M3_6 = 8, | 47 | M3_6 = 8, |
| 48 | /// 3.2 Mhz | ||
| 35 | M3_2 = 9, | 49 | M3_2 = 9, |
| 50 | /// 2.9 Mhz | ||
| 36 | M2_9 = 10, | 51 | M2_9 = 10, |
| 52 | /// 2.7 Mhz | ||
| 37 | M2_7 = 11, | 53 | M2_7 = 11, |
| 54 | /// 2.5 Mhz | ||
| 38 | M2_5 = 12, | 55 | M2_5 = 12, |
| 56 | /// 2.3 Mhz | ||
| 39 | M2_3 = 13, | 57 | M2_3 = 13, |
| 58 | /// 2.1 Mhz | ||
| 40 | M2_1 = 14, | 59 | M2_1 = 14, |
| 60 | /// 2 Mhz | ||
| 41 | M2 = 15, | 61 | M2 = 15, |
| 42 | } | 62 | } |
| 43 | 63 | ||
| 64 | /// QSPI config. | ||
| 44 | #[non_exhaustive] | 65 | #[non_exhaustive] |
| 45 | pub struct Config { | 66 | pub struct Config { |
| 67 | /// XIP offset. | ||
| 46 | pub xip_offset: u32, | 68 | pub xip_offset: u32, |
| 69 | /// Opcode used for read operations. | ||
| 47 | pub read_opcode: ReadOpcode, | 70 | pub read_opcode: ReadOpcode, |
| 71 | /// Opcode used for write operations. | ||
| 48 | pub write_opcode: WriteOpcode, | 72 | pub write_opcode: WriteOpcode, |
| 73 | /// Page size for write operations. | ||
| 49 | pub write_page_size: WritePageSize, | 74 | pub write_page_size: WritePageSize, |
| 75 | /// Configuration for deep power down. If None, deep power down is disabled. | ||
| 50 | pub deep_power_down: Option<DeepPowerDownConfig>, | 76 | pub deep_power_down: Option<DeepPowerDownConfig>, |
| 77 | /// QSPI bus frequency. | ||
| 51 | pub frequency: Frequency, | 78 | pub frequency: Frequency, |
| 52 | /// Value is specified in number of 16 MHz periods (62.5 ns) | 79 | /// Value is specified in number of 16 MHz periods (62.5 ns) |
| 53 | pub sck_delay: u8, | 80 | pub sck_delay: u8, |
| 54 | /// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3) | 81 | /// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3) |
| 55 | pub spi_mode: SpiMode, | 82 | pub spi_mode: SpiMode, |
| 83 | /// Addressing mode (24-bit or 32-bit) | ||
| 56 | pub address_mode: AddressMode, | 84 | pub address_mode: AddressMode, |
| 57 | } | 85 | } |
| 58 | 86 | ||
| @@ -72,20 +100,24 @@ impl Default for Config { | |||
| 72 | } | 100 | } |
| 73 | } | 101 | } |
| 74 | 102 | ||
| 103 | /// Error | ||
| 75 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 104 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 76 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 105 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 77 | #[non_exhaustive] | 106 | #[non_exhaustive] |
| 78 | pub enum Error { | 107 | pub enum Error { |
| 108 | /// Operation address was out of bounds. | ||
| 79 | OutOfBounds, | 109 | OutOfBounds, |
| 80 | // TODO add "not in data memory" error and check for it | 110 | // TODO add "not in data memory" error and check for it |
| 81 | } | 111 | } |
| 82 | 112 | ||
| 113 | /// QSPI flash driver. | ||
| 83 | pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { | 114 | pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { |
| 84 | irq: PeripheralRef<'d, T::Interrupt>, | 115 | irq: PeripheralRef<'d, T::Interrupt>, |
| 85 | dpm_enabled: bool, | 116 | dpm_enabled: bool, |
| 86 | } | 117 | } |
| 87 | 118 | ||
| 88 | impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | 119 | impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { |
| 120 | /// Create a new QSPI driver. | ||
| 89 | pub fn new( | 121 | pub fn new( |
| 90 | _qspi: impl Peripheral<P = T> + 'd, | 122 | _qspi: impl Peripheral<P = T> + 'd, |
| 91 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 123 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -183,6 +215,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | |||
| 183 | } | 215 | } |
| 184 | } | 216 | } |
| 185 | 217 | ||
| 218 | /// Do a custom QSPI instruction. | ||
| 186 | pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { | 219 | pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { |
| 187 | let bomb = DropBomb::new(); | 220 | let bomb = DropBomb::new(); |
| 188 | 221 | ||
| @@ -198,6 +231,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | |||
| 198 | Ok(()) | 231 | Ok(()) |
| 199 | } | 232 | } |
| 200 | 233 | ||
| 234 | /// Do a custom QSPI instruction, blocking version. | ||
| 201 | pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { | 235 | pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { |
| 202 | let len = core::cmp::max(req.len(), resp.len()) as u8; | 236 | let len = core::cmp::max(req.len(), resp.len()) as u8; |
| 203 | self.custom_instruction_start(opcode, req, len)?; | 237 | self.custom_instruction_start(opcode, req, len)?; |
| @@ -346,6 +380,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | |||
| 346 | Ok(()) | 380 | Ok(()) |
| 347 | } | 381 | } |
| 348 | 382 | ||
| 383 | /// Read data from the flash memory. | ||
| 349 | pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { | 384 | pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { |
| 350 | let bomb = DropBomb::new(); | 385 | let bomb = DropBomb::new(); |
| 351 | 386 | ||
| @@ -357,6 +392,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | |||
| 357 | Ok(()) | 392 | Ok(()) |
| 358 | } | 393 | } |
| 359 | 394 | ||
| 395 | /// Write data to the flash memory. | ||
| 360 | pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { | 396 | pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { |
| 361 | let bomb = DropBomb::new(); | 397 | let bomb = DropBomb::new(); |
| 362 | 398 | ||
| @@ -368,6 +404,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | |||
| 368 | Ok(()) | 404 | Ok(()) |
| 369 | } | 405 | } |
| 370 | 406 | ||
| 407 | /// Erase a sector on the flash memory. | ||
| 371 | pub async fn erase(&mut self, address: usize) -> Result<(), Error> { | 408 | pub async fn erase(&mut self, address: usize) -> Result<(), Error> { |
| 372 | let bomb = DropBomb::new(); | 409 | let bomb = DropBomb::new(); |
| 373 | 410 | ||
| @@ -379,18 +416,21 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | |||
| 379 | Ok(()) | 416 | Ok(()) |
| 380 | } | 417 | } |
| 381 | 418 | ||
| 419 | /// Read data from the flash memory, blocking version. | ||
| 382 | pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { | 420 | pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { |
| 383 | self.start_read(address, data)?; | 421 | self.start_read(address, data)?; |
| 384 | self.blocking_wait_ready(); | 422 | self.blocking_wait_ready(); |
| 385 | Ok(()) | 423 | Ok(()) |
| 386 | } | 424 | } |
| 387 | 425 | ||
| 426 | /// Write data to the flash memory, blocking version. | ||
| 388 | pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { | 427 | pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { |
| 389 | self.start_write(address, data)?; | 428 | self.start_write(address, data)?; |
| 390 | self.blocking_wait_ready(); | 429 | self.blocking_wait_ready(); |
| 391 | Ok(()) | 430 | Ok(()) |
| 392 | } | 431 | } |
| 393 | 432 | ||
| 433 | /// Erase a sector on the flash memory, blocking version. | ||
| 394 | pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { | 434 | pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { |
| 395 | self.start_erase(address)?; | 435 | self.start_erase(address)?; |
| 396 | self.blocking_wait_ready(); | 436 | self.blocking_wait_ready(); |
| @@ -547,7 +587,9 @@ pub(crate) mod sealed { | |||
| 547 | } | 587 | } |
| 548 | } | 588 | } |
| 549 | 589 | ||
| 590 | /// QSPI peripheral instance. | ||
| 550 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { | 591 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { |
| 592 | /// Interrupt for this peripheral. | ||
| 551 | type Interrupt: Interrupt; | 593 | type Interrupt: Interrupt; |
| 552 | } | 594 | } |
| 553 | 595 | ||
diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index e0caeaaee..b0b3a8eb8 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Random Number Generator (RNG) driver. | ||
| 2 | |||
| 1 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 2 | use core::ptr; | 4 | use core::ptr; |
| 3 | use core::sync::atomic::{AtomicPtr, Ordering}; | 5 | use core::sync::atomic::{AtomicPtr, Ordering}; |
| @@ -128,10 +130,11 @@ impl<'d> Rng<'d> { | |||
| 128 | /// However, this makes the generation of numbers slower. | 130 | /// However, this makes the generation of numbers slower. |
| 129 | /// | 131 | /// |
| 130 | /// Defaults to disabled. | 132 | /// Defaults to disabled. |
| 131 | pub fn bias_correction(&self, enable: bool) { | 133 | pub fn set_bias_correction(&self, enable: bool) { |
| 132 | RNG::regs().config.write(|w| w.dercen().bit(enable)) | 134 | RNG::regs().config.write(|w| w.dercen().bit(enable)) |
| 133 | } | 135 | } |
| 134 | 136 | ||
| 137 | /// Fill the buffer with random bytes. | ||
| 135 | pub async fn fill_bytes(&mut self, dest: &mut [u8]) { | 138 | pub async fn fill_bytes(&mut self, dest: &mut [u8]) { |
| 136 | if dest.len() == 0 { | 139 | if dest.len() == 0 { |
| 137 | return; // Nothing to fill | 140 | return; // Nothing to fill |
| @@ -175,6 +178,7 @@ impl<'d> Rng<'d> { | |||
| 175 | drop(on_drop); | 178 | drop(on_drop); |
| 176 | } | 179 | } |
| 177 | 180 | ||
| 181 | /// Fill the buffer with random bytes, blocking version. | ||
| 178 | pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) { | 182 | pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) { |
| 179 | self.start(); | 183 | self.start(); |
| 180 | 184 | ||
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 4592d4687..2d01a3dda 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Successive Approximation Analog-to-Digital Converter (SAADC) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| @@ -20,6 +22,7 @@ use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; | |||
| 20 | use crate::timer::{Frequency, Instance as TimerInstance, Timer}; | 22 | use crate::timer::{Frequency, Instance as TimerInstance, Timer}; |
| 21 | use crate::{interrupt, pac, peripherals, Peripheral}; | 23 | use crate::{interrupt, pac, peripherals, Peripheral}; |
| 22 | 24 | ||
| 25 | /// SAADC error | ||
| 23 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 26 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 24 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 25 | #[non_exhaustive] | 28 | #[non_exhaustive] |
| @@ -102,17 +105,17 @@ impl<'d> ChannelConfig<'d> { | |||
| 102 | } | 105 | } |
| 103 | } | 106 | } |
| 104 | 107 | ||
| 105 | /// The state of a continuously running sampler. While it reflects | 108 | /// Value returned by the SAADC callback, deciding what happens next. |
| 106 | /// the progress of a sampler, it also signals what should be done | ||
| 107 | /// next. For example, if the sampler has stopped then the Saadc implementation | ||
| 108 | /// can then tear down its infrastructure. | ||
| 109 | #[derive(PartialEq)] | 109 | #[derive(PartialEq)] |
| 110 | pub enum SamplerState { | 110 | pub enum CallbackResult { |
| 111 | Sampled, | 111 | /// The SAADC should keep sampling and calling the callback. |
| 112 | Stopped, | 112 | Continue, |
| 113 | /// The SAADC should stop sampling, and return. | ||
| 114 | Stop, | ||
| 113 | } | 115 | } |
| 114 | 116 | ||
| 115 | impl<'d, const N: usize> Saadc<'d, N> { | 117 | impl<'d, const N: usize> Saadc<'d, N> { |
| 118 | /// Create a new SAADC driver. | ||
| 116 | pub fn new( | 119 | pub fn new( |
| 117 | saadc: impl Peripheral<P = peripherals::SAADC> + 'd, | 120 | saadc: impl Peripheral<P = peripherals::SAADC> + 'd, |
| 118 | irq: impl Peripheral<P = interrupt::SAADC> + 'd, | 121 | irq: impl Peripheral<P = interrupt::SAADC> + 'd, |
| @@ -285,7 +288,7 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 285 | /// free the buffers from being used by the peripheral. Cancellation will | 288 | /// free the buffers from being used by the peripheral. Cancellation will |
| 286 | /// also cause the sampling to be stopped. | 289 | /// also cause the sampling to be stopped. |
| 287 | 290 | ||
| 288 | pub async fn run_task_sampler<S, T: TimerInstance, const N0: usize>( | 291 | pub async fn run_task_sampler<F, T: TimerInstance, const N0: usize>( |
| 289 | &mut self, | 292 | &mut self, |
| 290 | timer: &mut T, | 293 | timer: &mut T, |
| 291 | ppi_ch1: &mut impl ConfigurableChannel, | 294 | ppi_ch1: &mut impl ConfigurableChannel, |
| @@ -293,9 +296,9 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 293 | frequency: Frequency, | 296 | frequency: Frequency, |
| 294 | sample_counter: u32, | 297 | sample_counter: u32, |
| 295 | bufs: &mut [[[i16; N]; N0]; 2], | 298 | bufs: &mut [[[i16; N]; N0]; 2], |
| 296 | sampler: S, | 299 | callback: F, |
| 297 | ) where | 300 | ) where |
| 298 | S: FnMut(&[[i16; N]]) -> SamplerState, | 301 | F: FnMut(&[[i16; N]]) -> CallbackResult, |
| 299 | { | 302 | { |
| 300 | let r = Self::regs(); | 303 | let r = Self::regs(); |
| 301 | 304 | ||
| @@ -321,20 +324,20 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 321 | || { | 324 | || { |
| 322 | sample_ppi.enable(); | 325 | sample_ppi.enable(); |
| 323 | }, | 326 | }, |
| 324 | sampler, | 327 | callback, |
| 325 | ) | 328 | ) |
| 326 | .await; | 329 | .await; |
| 327 | } | 330 | } |
| 328 | 331 | ||
| 329 | async fn run_sampler<I, S, const N0: usize>( | 332 | async fn run_sampler<I, F, const N0: usize>( |
| 330 | &mut self, | 333 | &mut self, |
| 331 | bufs: &mut [[[i16; N]; N0]; 2], | 334 | bufs: &mut [[[i16; N]; N0]; 2], |
| 332 | sample_rate_divisor: Option<u16>, | 335 | sample_rate_divisor: Option<u16>, |
| 333 | mut init: I, | 336 | mut init: I, |
| 334 | mut sampler: S, | 337 | mut callback: F, |
| 335 | ) where | 338 | ) where |
| 336 | I: FnMut(), | 339 | I: FnMut(), |
| 337 | S: FnMut(&[[i16; N]]) -> SamplerState, | 340 | F: FnMut(&[[i16; N]]) -> CallbackResult, |
| 338 | { | 341 | { |
| 339 | // In case the future is dropped, stop the task and wait for it to end. | 342 | // In case the future is dropped, stop the task and wait for it to end. |
| 340 | let on_drop = OnDrop::new(Self::stop_sampling_immediately); | 343 | let on_drop = OnDrop::new(Self::stop_sampling_immediately); |
| @@ -395,12 +398,15 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 395 | r.events_end.reset(); | 398 | r.events_end.reset(); |
| 396 | r.intenset.write(|w| w.end().set()); | 399 | r.intenset.write(|w| w.end().set()); |
| 397 | 400 | ||
| 398 | if sampler(&bufs[current_buffer]) == SamplerState::Sampled { | 401 | match callback(&bufs[current_buffer]) { |
| 399 | let next_buffer = 1 - current_buffer; | 402 | CallbackResult::Continue => { |
| 400 | current_buffer = next_buffer; | 403 | let next_buffer = 1 - current_buffer; |
| 401 | } else { | 404 | current_buffer = next_buffer; |
| 402 | return Poll::Ready(()); | 405 | } |
| 403 | }; | 406 | CallbackResult::Stop => { |
| 407 | return Poll::Ready(()); | ||
| 408 | } | ||
| 409 | } | ||
| 404 | } | 410 | } |
| 405 | 411 | ||
| 406 | if r.events_started.read().bits() != 0 { | 412 | if r.events_started.read().bits() != 0 { |
| @@ -458,7 +464,7 @@ impl<'d> Saadc<'d, 1> { | |||
| 458 | sample_rate_divisor: u16, | 464 | sample_rate_divisor: u16, |
| 459 | sampler: S, | 465 | sampler: S, |
| 460 | ) where | 466 | ) where |
| 461 | S: FnMut(&[[i16; 1]]) -> SamplerState, | 467 | S: FnMut(&[[i16; 1]]) -> CallbackResult, |
| 462 | { | 468 | { |
| 463 | self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await; | 469 | self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await; |
| 464 | } | 470 | } |
| @@ -658,6 +664,10 @@ pub(crate) mod sealed { | |||
| 658 | 664 | ||
| 659 | /// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal. | 665 | /// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal. |
| 660 | pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized + 'static { | 666 | pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized + 'static { |
| 667 | /// Convert this SAADC input to a type-erased `AnyInput`. | ||
| 668 | /// | ||
| 669 | /// This allows using several inputs in situations that might require | ||
| 670 | /// them to be the same type, like putting them in an array. | ||
| 661 | fn degrade_saadc(self) -> AnyInput { | 671 | fn degrade_saadc(self) -> AnyInput { |
| 662 | AnyInput { | 672 | AnyInput { |
| 663 | channel: self.channel(), | 673 | channel: self.channel(), |
| @@ -665,6 +675,10 @@ pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized + | |||
| 665 | } | 675 | } |
| 666 | } | 676 | } |
| 667 | 677 | ||
| 678 | /// A type-erased SAADC input. | ||
| 679 | /// | ||
| 680 | /// This allows using several inputs in situations that might require | ||
| 681 | /// them to be the same type, like putting them in an array. | ||
| 668 | pub struct AnyInput { | 682 | pub struct AnyInput { |
| 669 | channel: InputChannel, | 683 | channel: InputChannel, |
| 670 | } | 684 | } |
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 7bb4e39f7..17e435787 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Serial Peripheral Instance in master mode (SPIM) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| @@ -16,27 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt}; | |||
| 16 | use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; | 18 | use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; |
| 17 | use crate::{pac, Peripheral}; | 19 | use crate::{pac, Peripheral}; |
| 18 | 20 | ||
| 21 | /// SPIM error | ||
| 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 21 | #[non_exhaustive] | 24 | #[non_exhaustive] |
| 22 | pub enum Error { | 25 | pub enum Error { |
| 26 | /// TX buffer was too long. | ||
| 23 | TxBufferTooLong, | 27 | TxBufferTooLong, |
| 28 | /// RX buffer was too long. | ||
| 24 | RxBufferTooLong, | 29 | RxBufferTooLong, |
| 25 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. | 30 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. |
| 26 | DMABufferNotInDataMemory, | 31 | BufferNotInRAM, |
| 27 | } | 32 | } |
| 28 | 33 | ||
| 29 | /// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. | 34 | /// SPIM driver. |
| 30 | /// | ||
| 31 | /// For more details about EasyDMA, consult the module documentation. | ||
| 32 | pub struct Spim<'d, T: Instance> { | 35 | pub struct Spim<'d, T: Instance> { |
| 33 | _p: PeripheralRef<'d, T>, | 36 | _p: PeripheralRef<'d, T>, |
| 34 | } | 37 | } |
| 35 | 38 | ||
| 39 | /// SPIM configuration. | ||
| 36 | #[non_exhaustive] | 40 | #[non_exhaustive] |
| 37 | pub struct Config { | 41 | pub struct Config { |
| 42 | /// Frequency | ||
| 38 | pub frequency: Frequency, | 43 | pub frequency: Frequency, |
| 44 | |||
| 45 | /// SPI mode | ||
| 39 | pub mode: Mode, | 46 | pub mode: Mode, |
| 47 | |||
| 48 | /// Overread character. | ||
| 49 | /// | ||
| 50 | /// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer, | ||
| 51 | /// this byte will be transmitted in the MOSI line for the left-over bytes. | ||
| 40 | pub orc: u8, | 52 | pub orc: u8, |
| 41 | } | 53 | } |
| 42 | 54 | ||
| @@ -51,6 +63,7 @@ impl Default for Config { | |||
| 51 | } | 63 | } |
| 52 | 64 | ||
| 53 | impl<'d, T: Instance> Spim<'d, T> { | 65 | impl<'d, T: Instance> Spim<'d, T> { |
| 66 | /// Create a new SPIM driver. | ||
| 54 | pub fn new( | 67 | pub fn new( |
| 55 | spim: impl Peripheral<P = T> + 'd, | 68 | spim: impl Peripheral<P = T> + 'd, |
| 56 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 69 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -70,6 +83,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 70 | ) | 83 | ) |
| 71 | } | 84 | } |
| 72 | 85 | ||
| 86 | /// Create a new SPIM driver, capable of TX only (MOSI only). | ||
| 73 | pub fn new_txonly( | 87 | pub fn new_txonly( |
| 74 | spim: impl Peripheral<P = T> + 'd, | 88 | spim: impl Peripheral<P = T> + 'd, |
| 75 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 89 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -81,6 +95,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 81 | Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) | 95 | Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) |
| 82 | } | 96 | } |
| 83 | 97 | ||
| 98 | /// Create a new SPIM driver, capable of RX only (MISO only). | ||
| 84 | pub fn new_rxonly( | 99 | pub fn new_rxonly( |
| 85 | spim: impl Peripheral<P = T> + 'd, | 100 | spim: impl Peripheral<P = T> + 'd, |
| 86 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 101 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -194,7 +209,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 194 | } | 209 | } |
| 195 | 210 | ||
| 196 | fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | 211 | fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { |
| 197 | slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; | 212 | slice_in_ram_or(tx, Error::BufferNotInRAM)?; |
| 198 | // NOTE: RAM slice check for rx is not necessary, as a mutable | 213 | // NOTE: RAM slice check for rx is not necessary, as a mutable |
| 199 | // slice can only be built from data located in RAM. | 214 | // slice can only be built from data located in RAM. |
| 200 | 215 | ||
| @@ -236,7 +251,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 236 | fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { | 251 | fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { |
| 237 | match self.blocking_inner_from_ram(rx, tx) { | 252 | match self.blocking_inner_from_ram(rx, tx) { |
| 238 | Ok(_) => Ok(()), | 253 | Ok(_) => Ok(()), |
| 239 | Err(Error::DMABufferNotInDataMemory) => { | 254 | Err(Error::BufferNotInRAM) => { |
| 240 | trace!("Copying SPIM tx buffer into RAM for DMA"); | 255 | trace!("Copying SPIM tx buffer into RAM for DMA"); |
| 241 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; | 256 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; |
| 242 | tx_ram_buf.copy_from_slice(tx); | 257 | tx_ram_buf.copy_from_slice(tx); |
| @@ -268,7 +283,7 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 268 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { | 283 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { |
| 269 | match self.async_inner_from_ram(rx, tx).await { | 284 | match self.async_inner_from_ram(rx, tx).await { |
| 270 | Ok(_) => Ok(()), | 285 | Ok(_) => Ok(()), |
| 271 | Err(Error::DMABufferNotInDataMemory) => { | 286 | Err(Error::BufferNotInRAM) => { |
| 272 | trace!("Copying SPIM tx buffer into RAM for DMA"); | 287 | trace!("Copying SPIM tx buffer into RAM for DMA"); |
| 273 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; | 288 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; |
| 274 | tx_ram_buf.copy_from_slice(tx); | 289 | tx_ram_buf.copy_from_slice(tx); |
| @@ -385,7 +400,9 @@ pub(crate) mod sealed { | |||
| 385 | } | 400 | } |
| 386 | } | 401 | } |
| 387 | 402 | ||
| 403 | /// SPIM peripheral instance | ||
| 388 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { | 404 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { |
| 405 | /// Interrupt for this peripheral. | ||
| 389 | type Interrupt: Interrupt; | 406 | type Interrupt: Interrupt; |
| 390 | } | 407 | } |
| 391 | 408 | ||
| @@ -437,7 +454,7 @@ mod eh1 { | |||
| 437 | match *self { | 454 | match *self { |
| 438 | Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, | 455 | Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, |
| 439 | Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, | 456 | Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, |
| 440 | Self::DMABufferNotInDataMemory => embedded_hal_1::spi::ErrorKind::Other, | 457 | Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other, |
| 441 | } | 458 | } |
| 442 | } | 459 | } |
| 443 | } | 460 | } |
diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 44af61a19..1b7436477 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Serial Peripheral Instance in slave mode (SPIS) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | use core::future::poll_fn; | 4 | use core::future::poll_fn; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 5 | use core::sync::atomic::{compiler_fence, Ordering}; |
| @@ -14,28 +16,43 @@ use crate::interrupt::{Interrupt, InterruptExt}; | |||
| 14 | use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; | 16 | use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; |
| 15 | use crate::{pac, Peripheral}; | 17 | use crate::{pac, Peripheral}; |
| 16 | 18 | ||
| 19 | /// SPIS error | ||
| 17 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 19 | #[non_exhaustive] | 22 | #[non_exhaustive] |
| 20 | pub enum Error { | 23 | pub enum Error { |
| 24 | /// TX buffer was too long. | ||
| 21 | TxBufferTooLong, | 25 | TxBufferTooLong, |
| 26 | /// RX buffer was too long. | ||
| 22 | RxBufferTooLong, | 27 | RxBufferTooLong, |
| 23 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. | 28 | /// EasyDMA can only read from data memory, read only buffers in flash will fail. |
| 24 | DMABufferNotInDataMemory, | 29 | BufferNotInRAM, |
| 25 | } | 30 | } |
| 26 | 31 | ||
| 27 | /// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload. | 32 | /// SPIS driver. |
| 28 | /// | ||
| 29 | /// For more details about EasyDMA, consult the module documentation. | ||
| 30 | pub struct Spis<'d, T: Instance> { | 33 | pub struct Spis<'d, T: Instance> { |
| 31 | _p: PeripheralRef<'d, T>, | 34 | _p: PeripheralRef<'d, T>, |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 37 | /// SPIS configuration. | ||
| 34 | #[non_exhaustive] | 38 | #[non_exhaustive] |
| 35 | pub struct Config { | 39 | pub struct Config { |
| 40 | /// SPI mode | ||
| 36 | pub mode: Mode, | 41 | pub mode: Mode, |
| 42 | |||
| 43 | /// Overread character. | ||
| 44 | /// | ||
| 45 | /// If the master keeps clocking the bus after all the bytes in the TX buffer have | ||
| 46 | /// already been transmitted, this byte will be constantly transmitted in the MISO line. | ||
| 37 | pub orc: u8, | 47 | pub orc: u8, |
| 48 | |||
| 49 | /// Default byte. | ||
| 50 | /// | ||
| 51 | /// This is the byte clocked out in the MISO line for ignored transactions (if the master | ||
| 52 | /// sets CSN low while the semaphore is owned by the firmware) | ||
| 38 | pub def: u8, | 53 | pub def: u8, |
| 54 | |||
| 55 | /// Automatically make the firmware side acquire the semaphore on transfer end. | ||
| 39 | pub auto_acquire: bool, | 56 | pub auto_acquire: bool, |
| 40 | } | 57 | } |
| 41 | 58 | ||
| @@ -51,6 +68,7 @@ impl Default for Config { | |||
| 51 | } | 68 | } |
| 52 | 69 | ||
| 53 | impl<'d, T: Instance> Spis<'d, T> { | 70 | impl<'d, T: Instance> Spis<'d, T> { |
| 71 | /// Create a new SPIS driver. | ||
| 54 | pub fn new( | 72 | pub fn new( |
| 55 | spis: impl Peripheral<P = T> + 'd, | 73 | spis: impl Peripheral<P = T> + 'd, |
| 56 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 74 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -72,6 +90,7 @@ impl<'d, T: Instance> Spis<'d, T> { | |||
| 72 | ) | 90 | ) |
| 73 | } | 91 | } |
| 74 | 92 | ||
| 93 | /// Create a new SPIS driver, capable of TX only (MISO only). | ||
| 75 | pub fn new_txonly( | 94 | pub fn new_txonly( |
| 76 | spis: impl Peripheral<P = T> + 'd, | 95 | spis: impl Peripheral<P = T> + 'd, |
| 77 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 96 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -92,6 +111,7 @@ impl<'d, T: Instance> Spis<'d, T> { | |||
| 92 | ) | 111 | ) |
| 93 | } | 112 | } |
| 94 | 113 | ||
| 114 | /// Create a new SPIS driver, capable of RX only (MOSI only). | ||
| 95 | pub fn new_rxonly( | 115 | pub fn new_rxonly( |
| 96 | spis: impl Peripheral<P = T> + 'd, | 116 | spis: impl Peripheral<P = T> + 'd, |
| 97 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 117 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -212,7 +232,7 @@ impl<'d, T: Instance> Spis<'d, T> { | |||
| 212 | } | 232 | } |
| 213 | 233 | ||
| 214 | fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { | 234 | fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { |
| 215 | slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; | 235 | slice_in_ram_or(tx, Error::BufferNotInRAM)?; |
| 216 | // NOTE: RAM slice check for rx is not necessary, as a mutable | 236 | // NOTE: RAM slice check for rx is not necessary, as a mutable |
| 217 | // slice can only be built from data located in RAM. | 237 | // slice can only be built from data located in RAM. |
| 218 | 238 | ||
| @@ -267,7 +287,7 @@ impl<'d, T: Instance> Spis<'d, T> { | |||
| 267 | fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { | 287 | fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { |
| 268 | match self.blocking_inner_from_ram(rx, tx) { | 288 | match self.blocking_inner_from_ram(rx, tx) { |
| 269 | Ok(n) => Ok(n), | 289 | Ok(n) => Ok(n), |
| 270 | Err(Error::DMABufferNotInDataMemory) => { | 290 | Err(Error::BufferNotInRAM) => { |
| 271 | trace!("Copying SPIS tx buffer into RAM for DMA"); | 291 | trace!("Copying SPIS tx buffer into RAM for DMA"); |
| 272 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; | 292 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; |
| 273 | tx_ram_buf.copy_from_slice(tx); | 293 | tx_ram_buf.copy_from_slice(tx); |
| @@ -330,7 +350,7 @@ impl<'d, T: Instance> Spis<'d, T> { | |||
| 330 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { | 350 | async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { |
| 331 | match self.async_inner_from_ram(rx, tx).await { | 351 | match self.async_inner_from_ram(rx, tx).await { |
| 332 | Ok(n) => Ok(n), | 352 | Ok(n) => Ok(n), |
| 333 | Err(Error::DMABufferNotInDataMemory) => { | 353 | Err(Error::BufferNotInRAM) => { |
| 334 | trace!("Copying SPIS tx buffer into RAM for DMA"); | 354 | trace!("Copying SPIS tx buffer into RAM for DMA"); |
| 335 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; | 355 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; |
| 336 | tx_ram_buf.copy_from_slice(tx); | 356 | tx_ram_buf.copy_from_slice(tx); |
| @@ -468,7 +488,9 @@ pub(crate) mod sealed { | |||
| 468 | } | 488 | } |
| 469 | } | 489 | } |
| 470 | 490 | ||
| 491 | /// SPIS peripheral instance | ||
| 471 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { | 492 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { |
| 493 | /// Interrupt for this peripheral. | ||
| 472 | type Interrupt: Interrupt; | 494 | type Interrupt: Interrupt; |
| 473 | } | 495 | } |
| 474 | 496 | ||
diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 7a7f61b51..5298faabc 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! Temperature sensor interface. | 1 | //! Builtin temperature sensor driver. |
| 2 | 2 | ||
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| @@ -12,7 +12,7 @@ use crate::interrupt::InterruptExt; | |||
| 12 | use crate::peripherals::TEMP; | 12 | use crate::peripherals::TEMP; |
| 13 | use crate::{interrupt, pac, Peripheral}; | 13 | use crate::{interrupt, pac, Peripheral}; |
| 14 | 14 | ||
| 15 | /// Integrated temperature sensor. | 15 | /// Builtin temperature sensor driver. |
| 16 | pub struct Temp<'d> { | 16 | pub struct Temp<'d> { |
| 17 | _irq: PeripheralRef<'d, interrupt::TEMP>, | 17 | _irq: PeripheralRef<'d, interrupt::TEMP>, |
| 18 | } | 18 | } |
| @@ -20,6 +20,7 @@ pub struct Temp<'d> { | |||
| 20 | static WAKER: AtomicWaker = AtomicWaker::new(); | 20 | static WAKER: AtomicWaker = AtomicWaker::new(); |
| 21 | 21 | ||
| 22 | impl<'d> Temp<'d> { | 22 | impl<'d> Temp<'d> { |
| 23 | /// Create a new temperature sensor driver. | ||
| 23 | pub fn new(_t: impl Peripheral<P = TEMP> + 'd, irq: impl Peripheral<P = interrupt::TEMP> + 'd) -> Self { | 24 | pub fn new(_t: impl Peripheral<P = TEMP> + 'd, irq: impl Peripheral<P = interrupt::TEMP> + 'd) -> Self { |
| 24 | into_ref!(_t, irq); | 25 | into_ref!(_t, irq); |
| 25 | 26 | ||
diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index bc8710640..d1ae57237 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs | |||
| @@ -1,3 +1,9 @@ | |||
| 1 | //! Timer driver. | ||
| 2 | //! | ||
| 3 | //! Important note! This driver is very low level. For most time-related use cases, like | ||
| 4 | //! "sleep for X seconds", "do something every X seconds", or measuring time, you should | ||
| 5 | //! use [`embassy-time`](https://crates.io/crates/embassy-time) instead! | ||
| 6 | |||
| 1 | #![macro_use] | 7 | #![macro_use] |
| 2 | 8 | ||
| 3 | use core::future::poll_fn; | 9 | use core::future::poll_fn; |
| @@ -28,9 +34,13 @@ pub(crate) mod sealed { | |||
| 28 | pub trait TimerType {} | 34 | pub trait TimerType {} |
| 29 | } | 35 | } |
| 30 | 36 | ||
| 37 | /// Basic Timer instance. | ||
| 31 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | 38 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { |
| 39 | /// Interrupt for this peripheral. | ||
| 32 | type Interrupt: Interrupt; | 40 | type Interrupt: Interrupt; |
| 33 | } | 41 | } |
| 42 | |||
| 43 | /// Extended timer instance. | ||
| 34 | pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} | 44 | pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} |
| 35 | 45 | ||
| 36 | macro_rules! impl_timer { | 46 | macro_rules! impl_timer { |
| @@ -61,18 +71,28 @@ macro_rules! impl_timer { | |||
| 61 | }; | 71 | }; |
| 62 | } | 72 | } |
| 63 | 73 | ||
| 74 | /// Timer frequency | ||
| 64 | #[repr(u8)] | 75 | #[repr(u8)] |
| 65 | pub enum Frequency { | 76 | pub enum Frequency { |
| 66 | // I'd prefer not to prefix these with `F`, but Rust identifiers can't start with digits. | 77 | /// 16MHz |
| 67 | F16MHz = 0, | 78 | F16MHz = 0, |
| 79 | /// 8MHz | ||
| 68 | F8MHz = 1, | 80 | F8MHz = 1, |
| 81 | /// 4MHz | ||
| 69 | F4MHz = 2, | 82 | F4MHz = 2, |
| 83 | /// 2MHz | ||
| 70 | F2MHz = 3, | 84 | F2MHz = 3, |
| 85 | /// 1MHz | ||
| 71 | F1MHz = 4, | 86 | F1MHz = 4, |
| 87 | /// 500kHz | ||
| 72 | F500kHz = 5, | 88 | F500kHz = 5, |
| 89 | /// 250kHz | ||
| 73 | F250kHz = 6, | 90 | F250kHz = 6, |
| 91 | /// 125kHz | ||
| 74 | F125kHz = 7, | 92 | F125kHz = 7, |
| 93 | /// 62500Hz | ||
| 75 | F62500Hz = 8, | 94 | F62500Hz = 8, |
| 95 | /// 31250Hz | ||
| 76 | F31250Hz = 9, | 96 | F31250Hz = 9, |
| 77 | } | 97 | } |
| 78 | 98 | ||
| @@ -86,7 +106,10 @@ pub enum Frequency { | |||
| 86 | 106 | ||
| 87 | pub trait TimerType: sealed::TimerType {} | 107 | pub trait TimerType: sealed::TimerType {} |
| 88 | 108 | ||
| 109 | /// Marker type indicating the timer driver can await expiration (it owns the timer interrupt). | ||
| 89 | pub enum Awaitable {} | 110 | pub enum Awaitable {} |
| 111 | |||
| 112 | /// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt). | ||
| 90 | pub enum NotAwaitable {} | 113 | pub enum NotAwaitable {} |
| 91 | 114 | ||
| 92 | impl sealed::TimerType for Awaitable {} | 115 | impl sealed::TimerType for Awaitable {} |
| @@ -94,12 +117,14 @@ impl sealed::TimerType for NotAwaitable {} | |||
| 94 | impl TimerType for Awaitable {} | 117 | impl TimerType for Awaitable {} |
| 95 | impl TimerType for NotAwaitable {} | 118 | impl TimerType for NotAwaitable {} |
| 96 | 119 | ||
| 120 | /// Timer driver. | ||
| 97 | pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { | 121 | pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { |
| 98 | _p: PeripheralRef<'d, T>, | 122 | _p: PeripheralRef<'d, T>, |
| 99 | _i: PhantomData<I>, | 123 | _i: PhantomData<I>, |
| 100 | } | 124 | } |
| 101 | 125 | ||
| 102 | impl<'d, T: Instance> Timer<'d, T, Awaitable> { | 126 | impl<'d, T: Instance> Timer<'d, T, Awaitable> { |
| 127 | /// Create a new async-capable timer driver. | ||
| 103 | pub fn new_awaitable(timer: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd) -> Self { | 128 | pub fn new_awaitable(timer: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd) -> Self { |
| 104 | into_ref!(irq); | 129 | into_ref!(irq); |
| 105 | 130 | ||
| @@ -107,16 +132,17 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> { | |||
| 107 | irq.unpend(); | 132 | irq.unpend(); |
| 108 | irq.enable(); | 133 | irq.enable(); |
| 109 | 134 | ||
| 110 | Self::new_irqless(timer) | 135 | Self::new_inner(timer) |
| 111 | } | 136 | } |
| 112 | } | 137 | } |
| 138 | |||
| 113 | impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { | 139 | impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { |
| 114 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. | 140 | /// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work. |
| 115 | /// | 141 | /// |
| 116 | /// This can be useful for triggering tasks via PPI | 142 | /// This can be useful for triggering tasks via PPI |
| 117 | /// `Uarte` uses this internally. | 143 | /// `Uarte` uses this internally. |
| 118 | pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self { | 144 | pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self { |
| 119 | Self::new_irqless(timer) | 145 | Self::new_inner(timer) |
| 120 | } | 146 | } |
| 121 | } | 147 | } |
| 122 | 148 | ||
| @@ -124,7 +150,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { | |||
| 124 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. | 150 | /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. |
| 125 | /// | 151 | /// |
| 126 | /// This is used by the public constructors. | 152 | /// This is used by the public constructors. |
| 127 | fn new_irqless(timer: impl Peripheral<P = T> + 'd) -> Self { | 153 | fn new_inner(timer: impl Peripheral<P = T> + 'd) -> Self { |
| 128 | into_ref!(timer); | 154 | into_ref!(timer); |
| 129 | 155 | ||
| 130 | let regs = T::regs(); | 156 | let regs = T::regs(); |
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 4eafd18c2..0dcb2b0da 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs | |||
| @@ -1,11 +1,7 @@ | |||
| 1 | //! I2C-compatible Two Wire Interface in master mode (TWIM) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | //! HAL interface to the TWIM peripheral. | ||
| 4 | //! | ||
| 5 | //! See product specification: | ||
| 6 | //! | ||
| 7 | //! - nRF52832: Section 33 | ||
| 8 | //! - nRF52840: Section 6.31 | ||
| 9 | use core::future::{poll_fn, Future}; | 5 | use core::future::{poll_fn, Future}; |
| 10 | use core::sync::atomic::compiler_fence; | 6 | use core::sync::atomic::compiler_fence; |
| 11 | use core::sync::atomic::Ordering::SeqCst; | 7 | use core::sync::atomic::Ordering::SeqCst; |
| @@ -23,22 +19,39 @@ use crate::interrupt::{Interrupt, InterruptExt}; | |||
| 23 | use crate::util::{slice_in_ram, slice_in_ram_or}; | 19 | use crate::util::{slice_in_ram, slice_in_ram_or}; |
| 24 | use crate::{gpio, pac, Peripheral}; | 20 | use crate::{gpio, pac, Peripheral}; |
| 25 | 21 | ||
| 22 | /// TWI frequency | ||
| 26 | #[derive(Clone, Copy)] | 23 | #[derive(Clone, Copy)] |
| 27 | pub enum Frequency { | 24 | pub enum Frequency { |
| 28 | #[doc = "26738688: 100 kbps"] | 25 | /// 100 kbps |
| 29 | K100 = 26738688, | 26 | K100 = 26738688, |
| 30 | #[doc = "67108864: 250 kbps"] | 27 | /// 250 kbps |
| 31 | K250 = 67108864, | 28 | K250 = 67108864, |
| 32 | #[doc = "104857600: 400 kbps"] | 29 | /// 400 kbps |
| 33 | K400 = 104857600, | 30 | K400 = 104857600, |
| 34 | } | 31 | } |
| 35 | 32 | ||
| 33 | /// TWIM config. | ||
| 36 | #[non_exhaustive] | 34 | #[non_exhaustive] |
| 37 | pub struct Config { | 35 | pub struct Config { |
| 36 | /// Frequency | ||
| 38 | pub frequency: Frequency, | 37 | pub frequency: Frequency, |
| 38 | |||
| 39 | /// Enable high drive for the SDA line. | ||
| 39 | pub sda_high_drive: bool, | 40 | pub sda_high_drive: bool, |
| 41 | |||
| 42 | /// Enable internal pullup for the SDA line. | ||
| 43 | /// | ||
| 44 | /// Note that using external pullups is recommended for I2C, and | ||
| 45 | /// most boards already have them. | ||
| 40 | pub sda_pullup: bool, | 46 | pub sda_pullup: bool, |
| 47 | |||
| 48 | /// Enable high drive for the SCL line. | ||
| 41 | pub scl_high_drive: bool, | 49 | pub scl_high_drive: bool, |
| 50 | |||
| 51 | /// Enable internal pullup for the SCL line. | ||
| 52 | /// | ||
| 53 | /// Note that using external pullups is recommended for I2C, and | ||
| 54 | /// most boards already have them. | ||
| 42 | pub scl_pullup: bool, | 55 | pub scl_pullup: bool, |
| 43 | } | 56 | } |
| 44 | 57 | ||
| @@ -54,29 +67,38 @@ impl Default for Config { | |||
| 54 | } | 67 | } |
| 55 | } | 68 | } |
| 56 | 69 | ||
| 70 | /// TWI error. | ||
| 57 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 71 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 58 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 72 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 59 | #[non_exhaustive] | 73 | #[non_exhaustive] |
| 60 | pub enum Error { | 74 | pub enum Error { |
| 75 | /// TX buffer was too long. | ||
| 61 | TxBufferTooLong, | 76 | TxBufferTooLong, |
| 77 | /// RX buffer was too long. | ||
| 62 | RxBufferTooLong, | 78 | RxBufferTooLong, |
| 79 | /// Data transmit failed. | ||
| 63 | Transmit, | 80 | Transmit, |
| 81 | /// Data reception failed. | ||
| 64 | Receive, | 82 | Receive, |
| 65 | DMABufferNotInDataMemory, | 83 | /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. |
| 84 | BufferNotInRAM, | ||
| 85 | /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly. | ||
| 66 | AddressNack, | 86 | AddressNack, |
| 87 | /// Didn't receive an ACK bit after a data byte. | ||
| 67 | DataNack, | 88 | DataNack, |
| 89 | /// Overrun error. | ||
| 68 | Overrun, | 90 | Overrun, |
| 91 | /// Timeout error. | ||
| 69 | Timeout, | 92 | Timeout, |
| 70 | } | 93 | } |
| 71 | 94 | ||
| 72 | /// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload. | 95 | /// TWI driver. |
| 73 | /// | ||
| 74 | /// For more details about EasyDMA, consult the module documentation. | ||
| 75 | pub struct Twim<'d, T: Instance> { | 96 | pub struct Twim<'d, T: Instance> { |
| 76 | _p: PeripheralRef<'d, T>, | 97 | _p: PeripheralRef<'d, T>, |
| 77 | } | 98 | } |
| 78 | 99 | ||
| 79 | impl<'d, T: Instance> Twim<'d, T> { | 100 | impl<'d, T: Instance> Twim<'d, T> { |
| 101 | /// Create a new TWI driver. | ||
| 80 | pub fn new( | 102 | pub fn new( |
| 81 | twim: impl Peripheral<P = T> + 'd, | 103 | twim: impl Peripheral<P = T> + 'd, |
| 82 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 104 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -153,7 +175,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 153 | 175 | ||
| 154 | /// Set TX buffer, checking that it is in RAM and has suitable length. | 176 | /// Set TX buffer, checking that it is in RAM and has suitable length. |
| 155 | unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { | 177 | unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 156 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; | 178 | slice_in_ram_or(buffer, Error::BufferNotInRAM)?; |
| 157 | 179 | ||
| 158 | if buffer.len() > EASY_DMA_SIZE { | 180 | if buffer.len() > EASY_DMA_SIZE { |
| 159 | return Err(Error::TxBufferTooLong); | 181 | return Err(Error::TxBufferTooLong); |
| @@ -233,7 +255,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 233 | return Err(Error::DataNack); | 255 | return Err(Error::DataNack); |
| 234 | } | 256 | } |
| 235 | if err.overrun().is_received() { | 257 | if err.overrun().is_received() { |
| 236 | return Err(Error::DataNack); | 258 | return Err(Error::Overrun); |
| 237 | } | 259 | } |
| 238 | Ok(()) | 260 | Ok(()) |
| 239 | } | 261 | } |
| @@ -435,7 +457,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 435 | ) -> Result<(), Error> { | 457 | ) -> Result<(), Error> { |
| 436 | match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { | 458 | match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { |
| 437 | Ok(_) => Ok(()), | 459 | Ok(_) => Ok(()), |
| 438 | Err(Error::DMABufferNotInDataMemory) => { | 460 | Err(Error::BufferNotInRAM) => { |
| 439 | trace!("Copying TWIM tx buffer into RAM for DMA"); | 461 | trace!("Copying TWIM tx buffer into RAM for DMA"); |
| 440 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | 462 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; |
| 441 | tx_ram_buf.copy_from_slice(wr_buffer); | 463 | tx_ram_buf.copy_from_slice(wr_buffer); |
| @@ -448,7 +470,7 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 448 | fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { | 470 | fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { |
| 449 | match self.setup_write_from_ram(address, wr_buffer, inten) { | 471 | match self.setup_write_from_ram(address, wr_buffer, inten) { |
| 450 | Ok(_) => Ok(()), | 472 | Ok(_) => Ok(()), |
| 451 | Err(Error::DMABufferNotInDataMemory) => { | 473 | Err(Error::BufferNotInRAM) => { |
| 452 | trace!("Copying TWIM tx buffer into RAM for DMA"); | 474 | trace!("Copying TWIM tx buffer into RAM for DMA"); |
| 453 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | 475 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; |
| 454 | tx_ram_buf.copy_from_slice(wr_buffer); | 476 | tx_ram_buf.copy_from_slice(wr_buffer); |
| @@ -612,6 +634,10 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 612 | 634 | ||
| 613 | // =========================================== | 635 | // =========================================== |
| 614 | 636 | ||
| 637 | /// Read from an I2C slave. | ||
| 638 | /// | ||
| 639 | /// The buffer must have a length of at most 255 bytes on the nRF52832 | ||
| 640 | /// and at most 65535 bytes on the nRF52840. | ||
| 615 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 641 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 616 | self.setup_read(address, buffer, true)?; | 642 | self.setup_read(address, buffer, true)?; |
| 617 | self.async_wait().await; | 643 | self.async_wait().await; |
| @@ -621,6 +647,10 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 621 | Ok(()) | 647 | Ok(()) |
| 622 | } | 648 | } |
| 623 | 649 | ||
| 650 | /// Write to an I2C slave. | ||
| 651 | /// | ||
| 652 | /// The buffer must have a length of at most 255 bytes on the nRF52832 | ||
| 653 | /// and at most 65535 bytes on the nRF52840. | ||
| 624 | pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { | 654 | pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { |
| 625 | self.setup_write(address, buffer, true)?; | 655 | self.setup_write(address, buffer, true)?; |
| 626 | self.async_wait().await; | 656 | self.async_wait().await; |
| @@ -640,6 +670,11 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 640 | Ok(()) | 670 | Ok(()) |
| 641 | } | 671 | } |
| 642 | 672 | ||
| 673 | /// Write data to an I2C slave, then read data from the slave without | ||
| 674 | /// triggering a stop condition between the two. | ||
| 675 | /// | ||
| 676 | /// The buffers must have a length of at most 255 bytes on the nRF52832 | ||
| 677 | /// and at most 65535 bytes on the nRF52840. | ||
| 643 | pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { | 678 | pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { |
| 644 | self.setup_write_read(address, wr_buffer, rd_buffer, true)?; | 679 | self.setup_write_read(address, wr_buffer, rd_buffer, true)?; |
| 645 | self.async_wait().await; | 680 | self.async_wait().await; |
| @@ -705,7 +740,9 @@ pub(crate) mod sealed { | |||
| 705 | } | 740 | } |
| 706 | } | 741 | } |
| 707 | 742 | ||
| 743 | /// TWIM peripheral instance. | ||
| 708 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { | 744 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { |
| 745 | /// Interrupt for this peripheral. | ||
| 709 | type Interrupt: Interrupt; | 746 | type Interrupt: Interrupt; |
| 710 | } | 747 | } |
| 711 | 748 | ||
| @@ -776,7 +813,7 @@ mod eh1 { | |||
| 776 | Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, | 813 | Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, |
| 777 | Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, | 814 | Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, |
| 778 | Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, | 815 | Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, |
| 779 | Self::DMABufferNotInDataMemory => embedded_hal_1::i2c::ErrorKind::Other, | 816 | Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other, |
| 780 | Self::AddressNack => { | 817 | Self::AddressNack => { |
| 781 | embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) | 818 | embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) |
| 782 | } | 819 | } |
diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 4091b017e..51a70c308 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs | |||
| @@ -1,11 +1,7 @@ | |||
| 1 | //! I2C-compatible Two Wire Interface in slave mode (TWIM) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | //! HAL interface to the TWIS peripheral. | ||
| 4 | //! | ||
| 5 | //! See product specification: | ||
| 6 | //! | ||
| 7 | //! - nRF52832: Section 33 | ||
| 8 | //! - nRF52840: Section 6.31 | ||
| 9 | use core::future::{poll_fn, Future}; | 5 | use core::future::{poll_fn, Future}; |
| 10 | use core::sync::atomic::compiler_fence; | 6 | use core::sync::atomic::compiler_fence; |
| 11 | use core::sync::atomic::Ordering::SeqCst; | 7 | use core::sync::atomic::Ordering::SeqCst; |
| @@ -22,14 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt}; | |||
| 22 | use crate::util::slice_in_ram_or; | 18 | use crate::util::slice_in_ram_or; |
| 23 | use crate::{gpio, pac, Peripheral}; | 19 | use crate::{gpio, pac, Peripheral}; |
| 24 | 20 | ||
| 21 | /// TWIS config. | ||
| 25 | #[non_exhaustive] | 22 | #[non_exhaustive] |
| 26 | pub struct Config { | 23 | pub struct Config { |
| 24 | /// First address | ||
| 27 | pub address0: u8, | 25 | pub address0: u8, |
| 26 | |||
| 27 | /// Second address, optional. | ||
| 28 | pub address1: Option<u8>, | 28 | pub address1: Option<u8>, |
| 29 | |||
| 30 | /// Overread character. | ||
| 31 | /// | ||
| 32 | /// If the master keeps clocking the bus after all the bytes in the TX buffer have | ||
| 33 | /// already been transmitted, this byte will be constantly transmitted. | ||
| 29 | pub orc: u8, | 34 | pub orc: u8, |
| 35 | |||
| 36 | /// Enable high drive for the SDA line. | ||
| 30 | pub sda_high_drive: bool, | 37 | pub sda_high_drive: bool, |
| 38 | |||
| 39 | /// Enable internal pullup for the SDA line. | ||
| 40 | /// | ||
| 41 | /// Note that using external pullups is recommended for I2C, and | ||
| 42 | /// most boards already have them. | ||
| 31 | pub sda_pullup: bool, | 43 | pub sda_pullup: bool, |
| 44 | |||
| 45 | /// Enable high drive for the SCL line. | ||
| 32 | pub scl_high_drive: bool, | 46 | pub scl_high_drive: bool, |
| 47 | |||
| 48 | /// Enable internal pullup for the SCL line. | ||
| 49 | /// | ||
| 50 | /// Note that using external pullups is recommended for I2C, and | ||
| 51 | /// most boards already have them. | ||
| 33 | pub scl_pullup: bool, | 52 | pub scl_pullup: bool, |
| 34 | } | 53 | } |
| 35 | 54 | ||
| @@ -54,36 +73,48 @@ enum Status { | |||
| 54 | Write, | 73 | Write, |
| 55 | } | 74 | } |
| 56 | 75 | ||
| 76 | /// TWIS error. | ||
| 57 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 77 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 58 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 78 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 59 | #[non_exhaustive] | 79 | #[non_exhaustive] |
| 60 | pub enum Error { | 80 | pub enum Error { |
| 81 | /// TX buffer was too long. | ||
| 61 | TxBufferTooLong, | 82 | TxBufferTooLong, |
| 83 | /// TX buffer was too long. | ||
| 62 | RxBufferTooLong, | 84 | RxBufferTooLong, |
| 85 | /// Didn't receive an ACK bit after a data byte. | ||
| 63 | DataNack, | 86 | DataNack, |
| 87 | /// Bus error. | ||
| 64 | Bus, | 88 | Bus, |
| 65 | DMABufferNotInDataMemory, | 89 | /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. |
| 90 | BufferNotInRAM, | ||
| 91 | /// Overflow | ||
| 66 | Overflow, | 92 | Overflow, |
| 93 | /// Overread | ||
| 67 | OverRead, | 94 | OverRead, |
| 95 | /// Timeout | ||
| 68 | Timeout, | 96 | Timeout, |
| 69 | } | 97 | } |
| 70 | 98 | ||
| 99 | /// Received command | ||
| 71 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | 100 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 72 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 101 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 73 | pub enum Command { | 102 | pub enum Command { |
| 103 | /// Read | ||
| 74 | Read, | 104 | Read, |
| 105 | /// Write+read | ||
| 75 | WriteRead(usize), | 106 | WriteRead(usize), |
| 107 | /// Write | ||
| 76 | Write(usize), | 108 | Write(usize), |
| 77 | } | 109 | } |
| 78 | 110 | ||
| 79 | /// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload. | 111 | /// TWIS driver. |
| 80 | /// | ||
| 81 | /// For more details about EasyDMA, consult the module documentation. | ||
| 82 | pub struct Twis<'d, T: Instance> { | 112 | pub struct Twis<'d, T: Instance> { |
| 83 | _p: PeripheralRef<'d, T>, | 113 | _p: PeripheralRef<'d, T>, |
| 84 | } | 114 | } |
| 85 | 115 | ||
| 86 | impl<'d, T: Instance> Twis<'d, T> { | 116 | impl<'d, T: Instance> Twis<'d, T> { |
| 117 | /// Create a new TWIS driver. | ||
| 87 | pub fn new( | 118 | pub fn new( |
| 88 | twis: impl Peripheral<P = T> + 'd, | 119 | twis: impl Peripheral<P = T> + 'd, |
| 89 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 120 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| @@ -174,7 +205,7 @@ impl<'d, T: Instance> Twis<'d, T> { | |||
| 174 | 205 | ||
| 175 | /// Set TX buffer, checking that it is in RAM and has suitable length. | 206 | /// Set TX buffer, checking that it is in RAM and has suitable length. |
| 176 | unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { | 207 | unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 177 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; | 208 | slice_in_ram_or(buffer, Error::BufferNotInRAM)?; |
| 178 | 209 | ||
| 179 | if buffer.len() > EASY_DMA_SIZE { | 210 | if buffer.len() > EASY_DMA_SIZE { |
| 180 | return Err(Error::TxBufferTooLong); | 211 | return Err(Error::TxBufferTooLong); |
| @@ -535,7 +566,7 @@ impl<'d, T: Instance> Twis<'d, T> { | |||
| 535 | fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { | 566 | fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { |
| 536 | match self.setup_respond_from_ram(wr_buffer, inten) { | 567 | match self.setup_respond_from_ram(wr_buffer, inten) { |
| 537 | Ok(_) => Ok(()), | 568 | Ok(_) => Ok(()), |
| 538 | Err(Error::DMABufferNotInDataMemory) => { | 569 | Err(Error::BufferNotInRAM) => { |
| 539 | trace!("Copying TWIS tx buffer into RAM for DMA"); | 570 | trace!("Copying TWIS tx buffer into RAM for DMA"); |
| 540 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; | 571 | let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; |
| 541 | tx_ram_buf.copy_from_slice(wr_buffer); | 572 | tx_ram_buf.copy_from_slice(wr_buffer); |
| @@ -737,7 +768,9 @@ pub(crate) mod sealed { | |||
| 737 | } | 768 | } |
| 738 | } | 769 | } |
| 739 | 770 | ||
| 771 | /// TWIS peripheral instance. | ||
| 740 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { | 772 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { |
| 773 | /// Interrupt for this peripheral. | ||
| 741 | type Interrupt: Interrupt; | 774 | type Interrupt: Interrupt; |
| 742 | } | 775 | } |
| 743 | 776 | ||
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 031cf82fd..48457744b 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #![macro_use] | 1 | //! Universal Asynchronous Receiver Transmitter (UART) driver. |
| 2 | |||
| 3 | //! Async UART | ||
| 4 | //! | 2 | //! |
| 5 | //! Async UART is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte]. | 3 | //! The UART driver is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte]. |
| 6 | //! The [Uarte] here is useful for those use-cases where reading the UARTE peripheral is | 4 | //! The [Uarte] here is useful for those use-cases where reading the UARTE peripheral is |
| 7 | //! exclusively awaited on. If the [Uarte] is required to be awaited on with some other future, | 5 | //! exclusively awaited on. If the [Uarte] is required to be awaited on with some other future, |
| 8 | //! for example when using `futures_util::future::select`, then you should consider | 6 | //! for example when using `futures_util::future::select`, then you should consider |
| @@ -13,6 +11,8 @@ | |||
| 13 | //! memory may be used given that buffers are passed in directly to its read and write | 11 | //! memory may be used given that buffers are passed in directly to its read and write |
| 14 | //! methods. | 12 | //! methods. |
| 15 | 13 | ||
| 14 | #![macro_use] | ||
| 15 | |||
| 16 | use core::future::poll_fn; | 16 | use core::future::poll_fn; |
| 17 | use core::sync::atomic::{compiler_fence, Ordering}; | 17 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 18 | use core::task::Poll; | 18 | use core::task::Poll; |
| @@ -32,10 +32,13 @@ use crate::timer::{Frequency, Instance as TimerInstance, Timer}; | |||
| 32 | use crate::util::slice_in_ram_or; | 32 | use crate::util::slice_in_ram_or; |
| 33 | use crate::{pac, Peripheral}; | 33 | use crate::{pac, Peripheral}; |
| 34 | 34 | ||
| 35 | /// UARTE config. | ||
| 35 | #[derive(Clone)] | 36 | #[derive(Clone)] |
| 36 | #[non_exhaustive] | 37 | #[non_exhaustive] |
| 37 | pub struct Config { | 38 | pub struct Config { |
| 39 | /// Parity bit. | ||
| 38 | pub parity: Parity, | 40 | pub parity: Parity, |
| 41 | /// Baud rate. | ||
| 39 | pub baudrate: Baudrate, | 42 | pub baudrate: Baudrate, |
| 40 | } | 43 | } |
| 41 | 44 | ||
| @@ -48,31 +51,33 @@ impl Default for Config { | |||
| 48 | } | 51 | } |
| 49 | } | 52 | } |
| 50 | 53 | ||
| 54 | /// UART error. | ||
| 51 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 55 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 53 | #[non_exhaustive] | 57 | #[non_exhaustive] |
| 54 | pub enum Error { | 58 | pub enum Error { |
| 59 | /// Buffer was too long. | ||
| 55 | BufferTooLong, | 60 | BufferTooLong, |
| 56 | DMABufferNotInDataMemory, | 61 | /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. |
| 57 | // TODO: add other error variants. | 62 | BufferNotInRAM, |
| 58 | } | 63 | } |
| 59 | 64 | ||
| 60 | /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. | 65 | /// UARTE driver. |
| 61 | /// | ||
| 62 | /// For more details about EasyDMA, consult the module documentation. | ||
| 63 | pub struct Uarte<'d, T: Instance> { | 66 | pub struct Uarte<'d, T: Instance> { |
| 64 | tx: UarteTx<'d, T>, | 67 | tx: UarteTx<'d, T>, |
| 65 | rx: UarteRx<'d, T>, | 68 | rx: UarteRx<'d, T>, |
| 66 | } | 69 | } |
| 67 | 70 | ||
| 68 | /// Transmitter interface to the UARTE peripheral obtained | 71 | /// Transmitter part of the UARTE driver. |
| 69 | /// via [Uarte]::split. | 72 | /// |
| 73 | /// This can be obtained via [`Uarte::split`], or created directly. | ||
| 70 | pub struct UarteTx<'d, T: Instance> { | 74 | pub struct UarteTx<'d, T: Instance> { |
| 71 | _p: PeripheralRef<'d, T>, | 75 | _p: PeripheralRef<'d, T>, |
| 72 | } | 76 | } |
| 73 | 77 | ||
| 74 | /// Receiver interface to the UARTE peripheral obtained | 78 | /// Receiver part of the UARTE driver. |
| 75 | /// via [Uarte]::split. | 79 | /// |
| 80 | /// This can be obtained via [`Uarte::split`], or created directly. | ||
| 76 | pub struct UarteRx<'d, T: Instance> { | 81 | pub struct UarteRx<'d, T: Instance> { |
| 77 | _p: PeripheralRef<'d, T>, | 82 | _p: PeripheralRef<'d, T>, |
| 78 | } | 83 | } |
| @@ -165,16 +170,16 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 165 | } | 170 | } |
| 166 | } | 171 | } |
| 167 | 172 | ||
| 168 | /// Split the Uarte into a transmitter and receiver, which is | 173 | /// Split the Uarte into the transmitter and receiver parts. |
| 169 | /// particularly useful when having two tasks correlating to | 174 | /// |
| 170 | /// transmitting and receiving. | 175 | /// This is useful to concurrently transmit and receive from independent tasks. |
| 171 | pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { | 176 | pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { |
| 172 | (self.tx, self.rx) | 177 | (self.tx, self.rx) |
| 173 | } | 178 | } |
| 174 | 179 | ||
| 175 | /// Split the Uarte into a transmitter and receiver that will | 180 | /// Split the Uarte into the transmitter and receiver with idle support parts. |
| 176 | /// return on idle, which is determined as the time it takes | 181 | /// |
| 177 | /// for two bytes to be received. | 182 | /// This is useful to concurrently transmit and receive from independent tasks. |
| 178 | pub fn split_with_idle<U: TimerInstance>( | 183 | pub fn split_with_idle<U: TimerInstance>( |
| 179 | self, | 184 | self, |
| 180 | timer: impl Peripheral<P = U> + 'd, | 185 | timer: impl Peripheral<P = U> + 'd, |
| @@ -247,10 +252,12 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 247 | } | 252 | } |
| 248 | } | 253 | } |
| 249 | 254 | ||
| 255 | /// Read bytes until the buffer is filled. | ||
| 250 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 256 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 251 | self.rx.read(buffer).await | 257 | self.rx.read(buffer).await |
| 252 | } | 258 | } |
| 253 | 259 | ||
| 260 | /// Write all bytes in the buffer. | ||
| 254 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 261 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 255 | self.tx.write(buffer).await | 262 | self.tx.write(buffer).await |
| 256 | } | 263 | } |
| @@ -260,10 +267,12 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 260 | self.tx.write_from_ram(buffer).await | 267 | self.tx.write_from_ram(buffer).await |
| 261 | } | 268 | } |
| 262 | 269 | ||
| 270 | /// Read bytes until the buffer is filled. | ||
| 263 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 271 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 264 | self.rx.blocking_read(buffer) | 272 | self.rx.blocking_read(buffer) |
| 265 | } | 273 | } |
| 266 | 274 | ||
| 275 | /// Write all bytes in the buffer. | ||
| 267 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 276 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 268 | self.tx.blocking_write(buffer) | 277 | self.tx.blocking_write(buffer) |
| 269 | } | 278 | } |
| @@ -355,10 +364,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { | |||
| 355 | Self { _p: uarte } | 364 | Self { _p: uarte } |
| 356 | } | 365 | } |
| 357 | 366 | ||
| 367 | /// Write all bytes in the buffer. | ||
| 358 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 368 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 359 | match self.write_from_ram(buffer).await { | 369 | match self.write_from_ram(buffer).await { |
| 360 | Ok(_) => Ok(()), | 370 | Ok(_) => Ok(()), |
| 361 | Err(Error::DMABufferNotInDataMemory) => { | 371 | Err(Error::BufferNotInRAM) => { |
| 362 | trace!("Copying UARTE tx buffer into RAM for DMA"); | 372 | trace!("Copying UARTE tx buffer into RAM for DMA"); |
| 363 | let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; | 373 | let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; |
| 364 | ram_buf.copy_from_slice(buffer); | 374 | ram_buf.copy_from_slice(buffer); |
| @@ -368,12 +378,13 @@ impl<'d, T: Instance> UarteTx<'d, T> { | |||
| 368 | } | 378 | } |
| 369 | } | 379 | } |
| 370 | 380 | ||
| 381 | /// Same as [`write`](Self::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. | ||
| 371 | pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | 382 | pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 372 | if buffer.len() == 0 { | 383 | if buffer.len() == 0 { |
| 373 | return Ok(()); | 384 | return Ok(()); |
| 374 | } | 385 | } |
| 375 | 386 | ||
| 376 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; | 387 | slice_in_ram_or(buffer, Error::BufferNotInRAM)?; |
| 377 | if buffer.len() > EASY_DMA_SIZE { | 388 | if buffer.len() > EASY_DMA_SIZE { |
| 378 | return Err(Error::BufferTooLong); | 389 | return Err(Error::BufferTooLong); |
| 379 | } | 390 | } |
| @@ -423,10 +434,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { | |||
| 423 | Ok(()) | 434 | Ok(()) |
| 424 | } | 435 | } |
| 425 | 436 | ||
| 437 | /// Write all bytes in the buffer. | ||
| 426 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 438 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 427 | match self.blocking_write_from_ram(buffer) { | 439 | match self.blocking_write_from_ram(buffer) { |
| 428 | Ok(_) => Ok(()), | 440 | Ok(_) => Ok(()), |
| 429 | Err(Error::DMABufferNotInDataMemory) => { | 441 | Err(Error::BufferNotInRAM) => { |
| 430 | trace!("Copying UARTE tx buffer into RAM for DMA"); | 442 | trace!("Copying UARTE tx buffer into RAM for DMA"); |
| 431 | let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; | 443 | let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; |
| 432 | ram_buf.copy_from_slice(buffer); | 444 | ram_buf.copy_from_slice(buffer); |
| @@ -436,12 +448,13 @@ impl<'d, T: Instance> UarteTx<'d, T> { | |||
| 436 | } | 448 | } |
| 437 | } | 449 | } |
| 438 | 450 | ||
| 451 | /// 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. | ||
| 439 | pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { | 452 | pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 440 | if buffer.len() == 0 { | 453 | if buffer.len() == 0 { |
| 441 | return Ok(()); | 454 | return Ok(()); |
| 442 | } | 455 | } |
| 443 | 456 | ||
| 444 | slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; | 457 | slice_in_ram_or(buffer, Error::BufferNotInRAM)?; |
| 445 | if buffer.len() > EASY_DMA_SIZE { | 458 | if buffer.len() > EASY_DMA_SIZE { |
| 446 | return Err(Error::BufferTooLong); | 459 | return Err(Error::BufferTooLong); |
| 447 | } | 460 | } |
| @@ -549,6 +562,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { | |||
| 549 | Self { _p: uarte } | 562 | Self { _p: uarte } |
| 550 | } | 563 | } |
| 551 | 564 | ||
| 565 | /// Read bytes until the buffer is filled. | ||
| 552 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 566 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 553 | if buffer.len() == 0 { | 567 | if buffer.len() == 0 { |
| 554 | return Ok(()); | 568 | return Ok(()); |
| @@ -602,6 +616,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { | |||
| 602 | Ok(()) | 616 | Ok(()) |
| 603 | } | 617 | } |
| 604 | 618 | ||
| 619 | /// Read bytes until the buffer is filled. | ||
| 605 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 620 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 606 | if buffer.len() == 0 { | 621 | if buffer.len() == 0 { |
| 607 | return Ok(()); | 622 | return Ok(()); |
| @@ -653,6 +668,9 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { | |||
| 653 | } | 668 | } |
| 654 | } | 669 | } |
| 655 | 670 | ||
| 671 | /// Receiver part of the UARTE driver, with `read_until_idle` support. | ||
| 672 | /// | ||
| 673 | /// This can be obtained via [`Uarte::split_with_idle`]. | ||
| 656 | pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { | 674 | pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { |
| 657 | rx: UarteRx<'d, T>, | 675 | rx: UarteRx<'d, T>, |
| 658 | timer: Timer<'d, U>, | 676 | timer: Timer<'d, U>, |
| @@ -661,16 +679,21 @@ pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { | |||
| 661 | } | 679 | } |
| 662 | 680 | ||
| 663 | impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { | 681 | impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { |
| 682 | /// Read bytes until the buffer is filled. | ||
| 664 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 683 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 665 | self.ppi_ch1.disable(); | 684 | self.ppi_ch1.disable(); |
| 666 | self.rx.read(buffer).await | 685 | self.rx.read(buffer).await |
| 667 | } | 686 | } |
| 668 | 687 | ||
| 688 | /// Read bytes until the buffer is filled. | ||
| 669 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 689 | pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 670 | self.ppi_ch1.disable(); | 690 | self.ppi_ch1.disable(); |
| 671 | self.rx.blocking_read(buffer) | 691 | self.rx.blocking_read(buffer) |
| 672 | } | 692 | } |
| 673 | 693 | ||
| 694 | /// Read bytes until the buffer is filled, or the line becomes idle. | ||
| 695 | /// | ||
| 696 | /// Returns the amount of bytes read. | ||
| 674 | pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | 697 | pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { |
| 675 | if buffer.len() == 0 { | 698 | if buffer.len() == 0 { |
| 676 | return Ok(0); | 699 | return Ok(0); |
| @@ -727,6 +750,9 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { | |||
| 727 | Ok(n) | 750 | Ok(n) |
| 728 | } | 751 | } |
| 729 | 752 | ||
| 753 | /// Read bytes until the buffer is filled, or the line becomes idle. | ||
| 754 | /// | ||
| 755 | /// Returns the amount of bytes read. | ||
| 730 | pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | 756 | pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { |
| 731 | if buffer.len() == 0 { | 757 | if buffer.len() == 0 { |
| 732 | return Ok(0); | 758 | return Ok(0); |
| @@ -860,7 +886,9 @@ pub(crate) mod sealed { | |||
| 860 | } | 886 | } |
| 861 | } | 887 | } |
| 862 | 888 | ||
| 889 | /// UARTE peripheral instance. | ||
| 863 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | 890 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { |
| 891 | /// Interrupt for this peripheral. | ||
| 864 | type Interrupt: Interrupt; | 892 | type Interrupt: Interrupt; |
| 865 | } | 893 | } |
| 866 | 894 | ||
| @@ -919,7 +947,7 @@ mod eh1 { | |||
| 919 | fn kind(&self) -> embedded_hal_1::serial::ErrorKind { | 947 | fn kind(&self) -> embedded_hal_1::serial::ErrorKind { |
| 920 | match *self { | 948 | match *self { |
| 921 | Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, | 949 | Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, |
| 922 | Self::DMABufferNotInDataMemory => embedded_hal_1::serial::ErrorKind::Other, | 950 | Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other, |
| 923 | } | 951 | } |
| 924 | } | 952 | } |
| 925 | } | 953 | } |
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index f030b909f..cd142f00f 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! Universal Serial Bus (USB) driver. | ||
| 2 | |||
| 1 | #![macro_use] | 3 | #![macro_use] |
| 2 | 4 | ||
| 3 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| @@ -24,38 +26,38 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; | |||
| 24 | static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; | 26 | static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; |
| 25 | static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); | 27 | static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); |
| 26 | 28 | ||
| 29 | /// Trait for detecting USB VBUS power. | ||
| 30 | /// | ||
| 27 | /// There are multiple ways to detect USB power. The behavior | 31 | /// There are multiple ways to detect USB power. The behavior |
| 28 | /// here provides a hook into determining whether it is. | 32 | /// here provides a hook into determining whether it is. |
| 29 | pub trait UsbSupply { | 33 | pub trait VbusDetect { |
| 34 | /// Report whether power is detected. | ||
| 35 | /// | ||
| 36 | /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the | ||
| 37 | /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. | ||
| 30 | fn is_usb_detected(&self) -> bool; | 38 | fn is_usb_detected(&self) -> bool; |
| 31 | async fn wait_power_ready(&mut self) -> Result<(), ()>; | ||
| 32 | } | ||
| 33 | 39 | ||
| 34 | pub struct Driver<'d, T: Instance, P: UsbSupply> { | 40 | /// Wait until USB power is ready. |
| 35 | _p: PeripheralRef<'d, T>, | 41 | /// |
| 36 | alloc_in: Allocator, | 42 | /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the |
| 37 | alloc_out: Allocator, | 43 | /// `USBPWRRDY` event from the `POWER` peripheral. |
| 38 | usb_supply: P, | 44 | async fn wait_power_ready(&mut self) -> Result<(), ()>; |
| 39 | } | 45 | } |
| 40 | 46 | ||
| 41 | /// Uses the POWER peripheral to detect when power is available | 47 | /// [`VbusDetect`] implementation using the native hardware POWER peripheral. |
| 42 | /// for USB. Unsuitable for usage with the nRF softdevice. | 48 | /// |
| 49 | /// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces | ||
| 50 | /// to POWER. In that case, use [`VbusDetectSignal`]. | ||
| 43 | #[cfg(not(feature = "_nrf5340-app"))] | 51 | #[cfg(not(feature = "_nrf5340-app"))] |
| 44 | pub struct PowerUsb { | 52 | pub struct HardwareVbusDetect { |
| 45 | _private: (), | 53 | _private: (), |
| 46 | } | 54 | } |
| 47 | 55 | ||
| 48 | /// Can be used to signal that power is available. Particularly suited for | ||
| 49 | /// use with the nRF softdevice. | ||
| 50 | pub struct SignalledSupply { | ||
| 51 | usb_detected: AtomicBool, | ||
| 52 | power_ready: AtomicBool, | ||
| 53 | } | ||
| 54 | |||
| 55 | static POWER_WAKER: AtomicWaker = NEW_AW; | 56 | static POWER_WAKER: AtomicWaker = NEW_AW; |
| 56 | 57 | ||
| 57 | #[cfg(not(feature = "_nrf5340-app"))] | 58 | #[cfg(not(feature = "_nrf5340-app"))] |
| 58 | impl PowerUsb { | 59 | impl HardwareVbusDetect { |
| 60 | /// Create a new `VbusDetectNative`. | ||
| 59 | pub fn new(power_irq: impl Interrupt) -> Self { | 61 | pub fn new(power_irq: impl Interrupt) -> Self { |
| 60 | let regs = unsafe { &*pac::POWER::ptr() }; | 62 | let regs = unsafe { &*pac::POWER::ptr() }; |
| 61 | 63 | ||
| @@ -92,7 +94,7 @@ impl PowerUsb { | |||
| 92 | } | 94 | } |
| 93 | 95 | ||
| 94 | #[cfg(not(feature = "_nrf5340-app"))] | 96 | #[cfg(not(feature = "_nrf5340-app"))] |
| 95 | impl UsbSupply for PowerUsb { | 97 | impl VbusDetect for HardwareVbusDetect { |
| 96 | fn is_usb_detected(&self) -> bool { | 98 | fn is_usb_detected(&self) -> bool { |
| 97 | let regs = unsafe { &*pac::POWER::ptr() }; | 99 | let regs = unsafe { &*pac::POWER::ptr() }; |
| 98 | regs.usbregstatus.read().vbusdetect().is_vbus_present() | 100 | regs.usbregstatus.read().vbusdetect().is_vbus_present() |
| @@ -115,7 +117,20 @@ impl UsbSupply for PowerUsb { | |||
| 115 | } | 117 | } |
| 116 | } | 118 | } |
| 117 | 119 | ||
| 118 | impl SignalledSupply { | 120 | /// Software-backed [`VbusDetect`] implementation. |
| 121 | /// | ||
| 122 | /// This implementation does not interact with the hardware, it allows user code | ||
| 123 | /// to notify the power events by calling functions instead. | ||
| 124 | /// | ||
| 125 | /// This is suitable for use with the nRF softdevice, by calling the functions | ||
| 126 | /// when the softdevice reports power-related events. | ||
| 127 | pub struct SoftwareVbusDetect { | ||
| 128 | usb_detected: AtomicBool, | ||
| 129 | power_ready: AtomicBool, | ||
| 130 | } | ||
| 131 | |||
| 132 | impl SoftwareVbusDetect { | ||
| 133 | /// Create a new `SoftwareVbusDetect`. | ||
| 119 | pub fn new(usb_detected: bool, power_ready: bool) -> Self { | 134 | pub fn new(usb_detected: bool, power_ready: bool) -> Self { |
| 120 | BUS_WAKER.wake(); | 135 | BUS_WAKER.wake(); |
| 121 | 136 | ||
| @@ -125,6 +140,9 @@ impl SignalledSupply { | |||
| 125 | } | 140 | } |
| 126 | } | 141 | } |
| 127 | 142 | ||
| 143 | /// Report whether power was detected. | ||
| 144 | /// | ||
| 145 | /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. | ||
| 128 | pub fn detected(&self, detected: bool) { | 146 | pub fn detected(&self, detected: bool) { |
| 129 | self.usb_detected.store(detected, Ordering::Relaxed); | 147 | self.usb_detected.store(detected, Ordering::Relaxed); |
| 130 | self.power_ready.store(false, Ordering::Relaxed); | 148 | self.power_ready.store(false, Ordering::Relaxed); |
| @@ -132,13 +150,16 @@ impl SignalledSupply { | |||
| 132 | POWER_WAKER.wake(); | 150 | POWER_WAKER.wake(); |
| 133 | } | 151 | } |
| 134 | 152 | ||
| 153 | /// Report when USB power is ready. | ||
| 154 | /// | ||
| 155 | /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. | ||
| 135 | pub fn ready(&self) { | 156 | pub fn ready(&self) { |
| 136 | self.power_ready.store(true, Ordering::Relaxed); | 157 | self.power_ready.store(true, Ordering::Relaxed); |
| 137 | POWER_WAKER.wake(); | 158 | POWER_WAKER.wake(); |
| 138 | } | 159 | } |
| 139 | } | 160 | } |
| 140 | 161 | ||
| 141 | impl UsbSupply for &SignalledSupply { | 162 | impl VbusDetect for &SoftwareVbusDetect { |
| 142 | fn is_usb_detected(&self) -> bool { | 163 | fn is_usb_detected(&self) -> bool { |
| 143 | self.usb_detected.load(Ordering::Relaxed) | 164 | self.usb_detected.load(Ordering::Relaxed) |
| 144 | } | 165 | } |
| @@ -159,7 +180,16 @@ impl UsbSupply for &SignalledSupply { | |||
| 159 | } | 180 | } |
| 160 | } | 181 | } |
| 161 | 182 | ||
| 162 | impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { | 183 | /// USB driver. |
| 184 | pub struct Driver<'d, T: Instance, P: VbusDetect> { | ||
| 185 | _p: PeripheralRef<'d, T>, | ||
| 186 | alloc_in: Allocator, | ||
| 187 | alloc_out: Allocator, | ||
| 188 | usb_supply: P, | ||
| 189 | } | ||
| 190 | |||
| 191 | impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { | ||
| 192 | /// Create a new USB driver. | ||
| 163 | pub fn new(usb: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd, usb_supply: P) -> Self { | 193 | pub fn new(usb: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd, usb_supply: P) -> Self { |
| 164 | into_ref!(usb, irq); | 194 | into_ref!(usb, irq); |
| 165 | irq.set_handler(Self::on_interrupt); | 195 | irq.set_handler(Self::on_interrupt); |
| @@ -225,7 +255,7 @@ impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { | |||
| 225 | } | 255 | } |
| 226 | } | 256 | } |
| 227 | 257 | ||
| 228 | impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> { | 258 | impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { |
| 229 | type EndpointOut = Endpoint<'d, T, Out>; | 259 | type EndpointOut = Endpoint<'d, T, Out>; |
| 230 | type EndpointIn = Endpoint<'d, T, In>; | 260 | type EndpointIn = Endpoint<'d, T, In>; |
| 231 | type ControlPipe = ControlPipe<'d, T>; | 261 | type ControlPipe = ControlPipe<'d, T>; |
| @@ -278,13 +308,14 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> | |||
| 278 | } | 308 | } |
| 279 | } | 309 | } |
| 280 | 310 | ||
| 281 | pub struct Bus<'d, T: Instance, P: UsbSupply> { | 311 | /// USB bus. |
| 312 | pub struct Bus<'d, T: Instance, P: VbusDetect> { | ||
| 282 | _p: PeripheralRef<'d, T>, | 313 | _p: PeripheralRef<'d, T>, |
| 283 | power_available: bool, | 314 | power_available: bool, |
| 284 | usb_supply: P, | 315 | usb_supply: P, |
| 285 | } | 316 | } |
| 286 | 317 | ||
| 287 | impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { | 318 | impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { |
| 288 | async fn enable(&mut self) { | 319 | async fn enable(&mut self) { |
| 289 | let regs = T::regs(); | 320 | let regs = T::regs(); |
| 290 | 321 | ||
| @@ -513,7 +544,10 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { | |||
| 513 | } | 544 | } |
| 514 | } | 545 | } |
| 515 | 546 | ||
| 547 | /// Type-level marker for OUT endpoints. | ||
| 516 | pub enum Out {} | 548 | pub enum Out {} |
| 549 | |||
| 550 | /// Type-level marker for IN endpoints. | ||
| 517 | pub enum In {} | 551 | pub enum In {} |
| 518 | 552 | ||
| 519 | trait EndpointDir { | 553 | trait EndpointDir { |
| @@ -556,6 +590,7 @@ impl EndpointDir for Out { | |||
| 556 | } | 590 | } |
| 557 | } | 591 | } |
| 558 | 592 | ||
| 593 | /// USB endpoint. | ||
| 559 | pub struct Endpoint<'d, T: Instance, Dir> { | 594 | pub struct Endpoint<'d, T: Instance, Dir> { |
| 560 | _phantom: PhantomData<(&'d mut T, Dir)>, | 595 | _phantom: PhantomData<(&'d mut T, Dir)>, |
| 561 | info: EndpointInfo, | 596 | info: EndpointInfo, |
| @@ -715,6 +750,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 715 | } | 750 | } |
| 716 | } | 751 | } |
| 717 | 752 | ||
| 753 | /// USB control pipe. | ||
| 718 | pub struct ControlPipe<'d, T: Instance> { | 754 | pub struct ControlPipe<'d, T: Instance> { |
| 719 | _p: PeripheralRef<'d, T>, | 755 | _p: PeripheralRef<'d, T>, |
| 720 | max_packet_size: u16, | 756 | max_packet_size: u16, |
| @@ -905,7 +941,9 @@ pub(crate) mod sealed { | |||
| 905 | } | 941 | } |
| 906 | } | 942 | } |
| 907 | 943 | ||
| 944 | /// USB peripheral instance. | ||
| 908 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | 945 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { |
| 946 | /// Interrupt for this peripheral. | ||
| 909 | type Interrupt: Interrupt; | 947 | type Interrupt: Interrupt; |
| 910 | } | 948 | } |
| 911 | 949 | ||
diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index 330ca98bf..40a674424 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | //! HAL interface to the WDT peripheral. | 1 | //! Watchdog Timer (WDT) driver. |
| 2 | //! | 2 | //! |
| 3 | //! This HAL implements a basic watchdog timer with 1..=8 handles. | 3 | //! This HAL implements a basic watchdog timer with 1..=8 handles. |
| 4 | //! Once the watchdog has been started, it cannot be stopped. | 4 | //! Once the watchdog has been started, it cannot be stopped. |
| @@ -8,6 +8,7 @@ use crate::peripherals; | |||
| 8 | 8 | ||
| 9 | const MIN_TICKS: u32 = 15; | 9 | const MIN_TICKS: u32 = 15; |
| 10 | 10 | ||
| 11 | /// WDT configuration. | ||
| 11 | #[non_exhaustive] | 12 | #[non_exhaustive] |
| 12 | pub struct Config { | 13 | pub struct Config { |
| 13 | /// Number of 32768 Hz ticks in each watchdog period. | 14 | /// Number of 32768 Hz ticks in each watchdog period. |
| @@ -57,13 +58,13 @@ impl Default for Config { | |||
| 57 | } | 58 | } |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 60 | /// An interface to the Watchdog. | 61 | /// Watchdog driver. |
| 61 | pub struct Watchdog { | 62 | pub struct Watchdog { |
| 62 | _private: (), | 63 | _private: (), |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 65 | impl Watchdog { | 66 | impl Watchdog { |
| 66 | /// Try to create a new watchdog instance from the peripheral. | 67 | /// Try to create a new watchdog driver. |
| 67 | /// | 68 | /// |
| 68 | /// This function will return an error if the watchdog is already active | 69 | /// This function will return an error if the watchdog is already active |
| 69 | /// with a `config` different to the requested one, or a different number of | 70 | /// with a `config` different to the requested one, or a different number of |
| @@ -155,6 +156,7 @@ impl Watchdog { | |||
| 155 | } | 156 | } |
| 156 | } | 157 | } |
| 157 | 158 | ||
| 159 | /// Watchdog handle. | ||
| 158 | pub struct WatchdogHandle { | 160 | pub struct WatchdogHandle { |
| 159 | index: u8, | 161 | index: u8, |
| 160 | } | 162 | } |
