Merge remote-tracking branch 'origin/main' into nrf-pdm

This commit is contained in:
Quentin Smith
2023-07-17 21:31:43 -04:00
863 changed files with 74741 additions and 28517 deletions

View File

@ -2,12 +2,13 @@
name = "embassy-nrf"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/"
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
features = ["nightly", "time", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
flavors = [
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
@ -15,13 +16,26 @@ flavors = [
]
[features]
default = ["rt"]
rt = [
"nrf52805-pac?/rt",
"nrf52810-pac?/rt",
"nrf52811-pac?/rt",
"nrf52820-pac?/rt",
"nrf52832-pac?/rt",
"nrf52833-pac?/rt",
"nrf52840-pac?/rt",
"nrf5340-app-pac?/rt",
"nrf5340-net-pac?/rt",
"nrf9160-pac?/rt",
]
time = ["dep:embassy-time"]
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
# Enable nightly-only features
nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
@ -33,22 +47,30 @@ unstable-pac = []
# Implement embedded-hal-async traits if `nightly` is set as well.
unstable-traits = ["embedded-hal-1"]
nrf52805 = ["nrf52805-pac", "_ppi"]
nrf52810 = ["nrf52810-pac", "_ppi"]
nrf52811 = ["nrf52811-pac", "_ppi"]
nrf52820 = ["nrf52820-pac", "_ppi"]
nrf52832 = ["nrf52832-pac", "_ppi"]
nrf52833 = ["nrf52833-pac", "_ppi", "_gpio-p1"]
nrf52840 = ["nrf52840-pac", "_ppi", "_gpio-p1"]
nrf5340-app-s = ["_nrf5340-app"]
nrf5340-app-ns = ["_nrf5340-app"]
nrf52805 = ["nrf52805-pac", "_nrf52"]
nrf52810 = ["nrf52810-pac", "_nrf52"]
nrf52811 = ["nrf52811-pac", "_nrf52"]
nrf52820 = ["nrf52820-pac", "_nrf52"]
nrf52832 = ["nrf52832-pac", "_nrf52"]
nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"]
nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"]
nrf5340-app-s = ["_nrf5340-app", "_s"]
nrf5340-app-ns = ["_nrf5340-app", "_ns"]
nrf5340-net = ["_nrf5340-net"]
nrf9160-s = ["_nrf9160"]
nrf9160-ns = ["_nrf9160"]
nrf9160-s = ["_nrf9160", "_s"]
nrf9160-ns = ["_nrf9160", "_ns"]
gpiote = []
time-driver-rtc1 = ["_time-driver"]
# Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53)
nfc-pins-as-gpio = []
# Allow using the RST pin as a regular GPIO pin.
# nrf52805, nrf52810, nrf52811, nrf52832: P0_21
# nrf52820, nrf52833, nrf52840: P0_18
reset-pin-as-gpio = []
# Features starting with `_` are for internal use only. They're not intended
# to be enabled by other crates, and are not covered by semver guarantees.
@ -56,46 +78,50 @@ _nrf5340-app = ["_nrf5340", "nrf5340-app-pac"]
_nrf5340-net = ["_nrf5340", "nrf5340-net-pac"]
_nrf5340 = ["_gpio-p1", "_dppi"]
_nrf9160 = ["nrf9160-pac", "_dppi"]
_nrf52 = ["_ppi"]
_time-driver = ["dep:embassy-time", "embassy-time?/tick-32768hz"]
_time-driver = ["dep:embassy-time", "embassy-time?/tick-hz-32_768"]
# trustzone state.
_s = []
_ns = []
_ppi = []
_dppi = []
_gpio-p1 = []
[dependencies]
embassy-executor = { version = "0.1.0", path = "../embassy-executor", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]}
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
cortex-m-rt = ">=0.6.15,<0.8"
cortex-m = "0.7.6"
futures = { version = "0.3.17", default-features = false }
futures = { version = "0.3.17", default-features = false }
critical-section = "1.1"
rand_core = "0.6.3"
fixed = "1.10.0"
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.3.0", optional = true }
embedded-storage-async = { version = "0.4.0", optional = true }
cfg-if = "1.0.0"
nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52811-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52820-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52832-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52833-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52840-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf5340-app-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf5340-net-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf9160-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true }
nrf52820-pac = { version = "0.12.0", optional = true }
nrf52832-pac = { version = "0.12.0", optional = true }
nrf52833-pac = { version = "0.12.0", optional = true }
nrf52840-pac = { version = "0.12.0", optional = true }
nrf5340-app-pac = { version = "0.12.0", optional = true }
nrf5340-net-pac = { version = "0.12.0", optional = true }
nrf9160-pac = { version = "0.12.0", optional = true }

58
embassy-nrf/README.md Normal file
View File

@ -0,0 +1,58 @@
# Embassy nRF HAL
HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed.
The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs
for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to
complete operations in low power mod and handling interrupts, so that applications can focus on more important matters.
## EasyDMA considerations
On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting
with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI).
However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust
slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen:
```rust,ignore
// As we pass a slice to the function whose contents will not ever change,
// the compiler writes it into the flash and thus the pointer to it will
// reference static memory. Since EasyDMA requires slices to reside in RAM,
// this function call will fail.
let result = spim.write_from_ram(&[1, 2, 3]);
assert_eq!(result, Err(Error::BufferNotInRAM));
// The data is still static and located in flash. However, since we are assigning
// it to a variable, the compiler will load it into memory. Passing a reference to the
// variable will yield a pointer that references dynamic memory, thus making EasyDMA happy.
// This function call succeeds.
let data = [1, 2, 3];
let result = spim.write_from_ram(&data);
assert!(result.is_ok());
```
Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions:
- Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM.
- Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission.
Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will
fail and notify you, or the more convenient versions without the suffix which are potentially a little bit
more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage
as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840).
Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as
mutable slices always reside in RAM.
## Minimum supported Rust version (MSRV)
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 256;
pub const FLASH_SIZE: usize = 192 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_common::peripherals! {
// RTC
RTC0,
@ -108,6 +110,7 @@ embassy_hal_common::peripherals! {
P0_18,
P0_19,
P0_20,
#[cfg(feature="reset-pin-as-gpio")]
P0_21,
P0_22,
P0_23,
@ -131,8 +134,16 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -158,6 +169,7 @@ impl_pin!(P0_17, 0, 17);
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_21, 0, 21);
impl_pin!(P0_22, 0, 22);
impl_pin!(P0_23, 0, 23);
@ -193,36 +205,32 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(TWIM0_TWIS0_TWI0);
declare!(SPIM0_SPIS0_SPI0);
declare!(GPIOTE);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2);
declare!(SWI3);
declare!(SWI4);
declare!(SWI5);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
TWIM0_TWIS0_TWI0,
SPIM0_SPIS0_SPI0,
GPIOTE,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
SWI0_EGU0,
SWI1_EGU1,
SWI2,
SWI3,
SWI4,
SWI5,
);

View File

@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 256;
pub const FLASH_SIZE: usize = 192 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_common::peripherals! {
// RTC
RTC0,
@ -111,6 +113,7 @@ embassy_hal_common::peripherals! {
P0_18,
P0_19,
P0_20,
#[cfg(feature="reset-pin-as-gpio")]
P0_21,
P0_22,
P0_23,
@ -137,10 +140,20 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0);
impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0);
impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0);
impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -166,6 +179,7 @@ impl_pin!(P0_17, 0, 17);
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_21, 0, 21);
impl_pin!(P0_22, 0, 22);
impl_pin!(P0_23, 0, 23);
@ -211,45 +225,41 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(TWIM0_TWIS0_TWI0);
declare!(SPIM0_SPIS0_SPI0);
declare!(GPIOTE);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(COMP);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2);
declare!(SWI3);
declare!(SWI4);
declare!(SWI5);
declare!(PWM0);
declare!(PDM);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
TWIM0_TWIS0_TWI0,
SPIM0_SPIS0_SPI0,
GPIOTE,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
COMP,
SWI0_EGU0,
SWI1_EGU1,
SWI2,
SWI3,
SWI4,
SWI5,
PWM0,
PDM,
);

View File

@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 256;
pub const FLASH_SIZE: usize = 192 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_common::peripherals! {
// RTC
RTC0,
@ -111,6 +113,7 @@ embassy_hal_common::peripherals! {
P0_18,
P0_19,
P0_20,
#[cfg(feature="reset-pin-as-gpio")]
P0_21,
P0_22,
P0_23,
@ -138,10 +141,21 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1);
impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1);
impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -167,6 +181,7 @@ impl_pin!(P0_17, 0, 17);
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_21, 0, 21);
impl_pin!(P0_22, 0, 22);
impl_pin!(P0_23, 0, 23);
@ -212,45 +227,41 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0);
declare!(SPIM1_SPIS1_SPI1);
declare!(GPIOTE);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(COMP);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2);
declare!(SWI3);
declare!(SWI4);
declare!(SWI5);
declare!(PWM0);
declare!(PDM);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0,
SPIM1_SPIS1_SPI1,
GPIOTE,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
COMP,
SWI0_EGU0,
SWI1_EGU1,
SWI2,
SWI3,
SWI4,
SWI5,
PWM0,
PDM,
);

View File

@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512;
pub const FLASH_SIZE: usize = 256 * 1024;
pub const RESET_PIN: u32 = 18;
embassy_hal_common::peripherals! {
// USB
USBD,
@ -106,6 +108,7 @@ embassy_hal_common::peripherals! {
P0_15,
P0_16,
P0_17,
#[cfg(feature="reset-pin-as-gpio")]
P0_18,
P0_19,
P0_20,
@ -136,14 +139,24 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2);
@ -162,6 +175,7 @@ impl_pin!(P0_14, 0, 14);
impl_pin!(P0_15, 0, 15);
impl_pin!(P0_16, 0, 16);
impl_pin!(P0_17, 0, 17);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
@ -210,35 +224,31 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
declare!(GPIOTE);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(COMP);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2_EGU2);
declare!(SWI3_EGU3);
declare!(SWI4_EGU4);
declare!(SWI5_EGU5);
declare!(TIMER3);
declare!(USBD);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
GPIOTE,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
COMP,
SWI0_EGU0,
SWI1_EGU1,
SWI2_EGU2,
SWI3_EGU3,
SWI4_EGU4,
SWI5_EGU5,
TIMER3,
USBD,
);

View File

@ -10,6 +10,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 255;
// nrf52832xxAB = 256kb
pub const FLASH_SIZE: usize = 512 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_common::peripherals! {
// RTC
RTC0,
@ -109,7 +111,9 @@ embassy_hal_common::peripherals! {
P0_06,
P0_07,
P0_08,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_09,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_10,
P0_11,
P0_12,
@ -121,6 +125,7 @@ embassy_hal_common::peripherals! {
P0_18,
P0_19,
P0_20,
#[cfg(feature="reset-pin-as-gpio")]
P0_21,
P0_22,
P0_23,
@ -139,6 +144,9 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// I2S
I2S,
// PDM
PDM,
}
@ -149,13 +157,26 @@ impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -171,7 +192,9 @@ impl_pin!(P0_05, 0, 5);
impl_pin!(P0_06, 0, 6);
impl_pin!(P0_07, 0, 7);
impl_pin!(P0_08, 0, 8);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_09, 0, 9);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_10, 0, 10);
impl_pin!(P0_11, 0, 11);
impl_pin!(P0_12, 0, 12);
@ -183,6 +206,7 @@ impl_pin!(P0_17, 0, 17);
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_21, 0, 21);
impl_pin!(P0_22, 0, 22);
impl_pin!(P0_23, 0, 23);
@ -228,55 +252,53 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
impl_i2s!(I2S, I2S, I2S);
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
declare!(NFCT);
declare!(GPIOTE);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(COMP_LPCOMP);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2_EGU2);
declare!(SWI3_EGU3);
declare!(SWI4_EGU4);
declare!(SWI5_EGU5);
declare!(TIMER3);
declare!(TIMER4);
declare!(PWM0);
declare!(PDM);
declare!(MWU);
declare!(PWM1);
declare!(PWM2);
declare!(SPIM2_SPIS2_SPI2);
declare!(RTC2);
declare!(I2S);
declare!(FPU);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
NFCT,
GPIOTE,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
COMP_LPCOMP,
SWI0_EGU0,
SWI1_EGU1,
SWI2_EGU2,
SWI3_EGU3,
SWI4_EGU4,
SWI5_EGU5,
TIMER3,
TIMER4,
PWM0,
PDM,
MWU,
PWM1,
PWM2,
SPIM2_SPIS2_SPI2,
RTC2,
FPU,
I2S,
);

View File

@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512;
pub const FLASH_SIZE: usize = 512 * 1024;
pub const RESET_PIN: u32 = 18;
embassy_hal_common::peripherals! {
// USB
USBD,
@ -111,7 +113,9 @@ embassy_hal_common::peripherals! {
P0_06,
P0_07,
P0_08,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_09,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_10,
P0_11,
P0_12,
@ -120,6 +124,7 @@ embassy_hal_common::peripherals! {
P0_15,
P0_16,
P0_17,
#[cfg(feature="reset-pin-as-gpio")]
P0_18,
P0_19,
P0_20,
@ -161,6 +166,9 @@ embassy_hal_common::peripherals! {
// PDM
PDM,
// I2S
I2S,
}
#[cfg(feature = "nightly")]
@ -174,14 +182,27 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
impl_spim!(SPI3, SPIM3, SPIM3);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
impl_pwm!(PWM3, PWM3, PWM3);
impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -197,7 +218,9 @@ impl_pin!(P0_05, 0, 5);
impl_pin!(P0_06, 0, 6);
impl_pin!(P0_07, 0, 7);
impl_pin!(P0_08, 0, 8);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_09, 0, 9);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_10, 0, 10);
impl_pin!(P0_11, 0, 11);
impl_pin!(P0_12, 0, 12);
@ -206,6 +229,7 @@ impl_pin!(P0_14, 0, 14);
impl_pin!(P0_15, 0, 15);
impl_pin!(P0_16, 0, 16);
impl_pin!(P0_17, 0, 17);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
@ -271,59 +295,57 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
impl_i2s!(I2S, I2S, I2S);
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
declare!(NFCT);
declare!(GPIOTE);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(COMP_LPCOMP);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2_EGU2);
declare!(SWI3_EGU3);
declare!(SWI4_EGU4);
declare!(SWI5_EGU5);
declare!(TIMER3);
declare!(TIMER4);
declare!(PWM0);
declare!(PDM);
declare!(MWU);
declare!(PWM1);
declare!(PWM2);
declare!(SPIM2_SPIS2_SPI2);
declare!(RTC2);
declare!(I2S);
declare!(FPU);
declare!(USBD);
declare!(UARTE1);
declare!(PWM3);
declare!(SPIM3);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
NFCT,
GPIOTE,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
COMP_LPCOMP,
SWI0_EGU0,
SWI1_EGU1,
SWI2_EGU2,
SWI3_EGU3,
SWI4_EGU4,
SWI5_EGU5,
TIMER3,
TIMER4,
PWM0,
PDM,
MWU,
PWM1,
PWM2,
SPIM2_SPIS2_SPI2,
RTC2,
FPU,
USBD,
UARTE1,
PWM3,
SPIM3,
I2S,
);

View File

@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512;
pub const FLASH_SIZE: usize = 1024 * 1024;
pub const RESET_PIN: u32 = 18;
embassy_hal_common::peripherals! {
// USB
USBD,
@ -117,7 +119,9 @@ embassy_hal_common::peripherals! {
P0_06,
P0_07,
P0_08,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_09,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_10,
P0_11,
P0_12,
@ -126,6 +130,7 @@ embassy_hal_common::peripherals! {
P0_15,
P0_16,
P0_17,
#[cfg(feature="reset-pin-as-gpio")]
P0_18,
P0_19,
P0_20,
@ -164,6 +169,9 @@ embassy_hal_common::peripherals! {
// PDM
PDM,
// I2S
I2S,
}
#[cfg(feature = "nightly")]
@ -177,9 +185,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2);
impl_spim!(SPI3, SPIM3, SPIM3);
impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2);
impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
@ -193,6 +208,12 @@ impl_timer!(TIMER4, TIMER4, TIMER4, extended);
impl_qspi!(QSPI, QSPI, QSPI);
impl_pdm!(PDM, PDM, PDM);
impl_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2);
@ -202,7 +223,9 @@ impl_pin!(P0_05, 0, 5);
impl_pin!(P0_06, 0, 6);
impl_pin!(P0_07, 0, 7);
impl_pin!(P0_08, 0, 8);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_09, 0, 9);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_10, 0, 10);
impl_pin!(P0_11, 0, 11);
impl_pin!(P0_12, 0, 12);
@ -211,6 +234,7 @@ impl_pin!(P0_14, 0, 14);
impl_pin!(P0_15, 0, 15);
impl_pin!(P0_16, 0, 16);
impl_pin!(P0_17, 0, 17);
#[cfg(feature = "reset-pin-as-gpio")]
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
@ -276,61 +300,59 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_02, ANALOGINPUT0);
impl_saadc_input!(P0_03, ANALOGINPUT1);
impl_saadc_input!(P0_04, ANALOGINPUT2);
impl_saadc_input!(P0_05, ANALOGINPUT3);
impl_saadc_input!(P0_28, ANALOGINPUT4);
impl_saadc_input!(P0_29, ANALOGINPUT5);
impl_saadc_input!(P0_30, ANALOGINPUT6);
impl_saadc_input!(P0_31, ANALOGINPUT7);
impl_saadc_input!(P0_02, ANALOG_INPUT0);
impl_saadc_input!(P0_03, ANALOG_INPUT1);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
impl_saadc_input!(P0_28, ANALOG_INPUT4);
impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
impl_i2s!(I2S, I2S, I2S);
use crate::pac::Interrupt as InterruptEnum;
declare!(POWER_CLOCK);
declare!(RADIO);
declare!(UARTE0_UART0);
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
declare!(NFCT);
declare!(GPIOTE);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(TEMP);
declare!(RNG);
declare!(ECB);
declare!(CCM_AAR);
declare!(WDT);
declare!(RTC1);
declare!(QDEC);
declare!(COMP_LPCOMP);
declare!(SWI0_EGU0);
declare!(SWI1_EGU1);
declare!(SWI2_EGU2);
declare!(SWI3_EGU3);
declare!(SWI4_EGU4);
declare!(SWI5_EGU5);
declare!(TIMER3);
declare!(TIMER4);
declare!(PWM0);
declare!(PDM);
declare!(MWU);
declare!(PWM1);
declare!(PWM2);
declare!(SPIM2_SPIS2_SPI2);
declare!(RTC2);
declare!(I2S);
declare!(FPU);
declare!(USBD);
declare!(UARTE1);
declare!(QSPI);
declare!(CRYPTOCELL);
declare!(PWM3);
declare!(SPIM3);
}
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
NFCT,
GPIOTE,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
TEMP,
RNG,
ECB,
CCM_AAR,
WDT,
RTC1,
QDEC,
COMP_LPCOMP,
SWI0_EGU0,
SWI1_EGU1,
SWI2_EGU2,
SWI3_EGU3,
SWI4_EGU4,
SWI5_EGU5,
TIMER3,
TIMER4,
PWM0,
PDM,
MWU,
PWM1,
PWM2,
SPIM2_SPIS2_SPI2,
RTC2,
FPU,
USBD,
UARTE1,
QSPI,
CRYPTOCELL,
PWM3,
SPIM3,
I2S,
);

View File

@ -1,9 +1,12 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {
// The nRF5340 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf5340_app_pac::NVIC_PRIO_BITS;
#[doc(no_inline)]
pub use nrf5340_app_pac::{
interrupt,
@ -33,10 +36,10 @@ pub mod pac {
nvmc_ns as nvmc,
oscillators_ns as oscillators,
p0_ns as p0,
pdm0_ns as pdm0,
pdm0_ns as pdm,
power_ns as power,
pwm0_ns as pwm0,
qdec0_ns as qdec0,
qdec0_ns as qdec,
qspi_ns as qspi,
regulators_ns as regulators,
reset_ns as reset,
@ -213,6 +216,8 @@ pub mod pac {
pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_common::peripherals! {
// USB
USBD,
@ -224,11 +229,14 @@ embassy_hal_common::peripherals! {
// WDT
WDT,
// NVMC
NVMC,
// UARTE, TWI & SPI
UARTETWISPI0,
UARTETWISPI1,
UARTETWISPI2,
UARTETWISPI3,
SERIAL0,
SERIAL1,
SERIAL2,
SERIAL3,
// SAADC
SAADC,
@ -244,6 +252,16 @@ embassy_hal_common::peripherals! {
TIMER1,
TIMER2,
// QSPI
QSPI,
// PDM
PDM0,
// QDEC
QDEC0,
QDEC1,
// GPIOTE
GPIOTE_CH0,
GPIOTE_CH1,
@ -298,7 +316,9 @@ embassy_hal_common::peripherals! {
// GPIO port 0
P0_00,
P0_01,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_02,
#[cfg(feature = "nfc-pins-as-gpio")]
P0_03,
P0_04,
P0_05,
@ -351,20 +371,30 @@ embassy_hal_common::peripherals! {
#[cfg(feature = "nightly")]
impl_usb!(USBD, USBD, USBD);
impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
impl_uarte!(UARTETWISPI1, UARTE1, SERIAL1);
impl_uarte!(UARTETWISPI2, UARTE2, SERIAL2);
impl_uarte!(UARTETWISPI3, UARTE3, SERIAL3);
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
impl_uarte!(SERIAL1, UARTE1, SERIAL1);
impl_uarte!(SERIAL2, UARTE2, SERIAL2);
impl_uarte!(SERIAL3, UARTE3, SERIAL3);
impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
impl_spim!(UARTETWISPI1, SPIM1, SERIAL1);
impl_spim!(UARTETWISPI2, SPIM2, SERIAL2);
impl_spim!(UARTETWISPI3, SPIM3, SERIAL3);
impl_spim!(SERIAL0, SPIM0, SERIAL0);
impl_spim!(SERIAL1, SPIM1, SERIAL1);
impl_spim!(SERIAL2, SPIM2, SERIAL2);
impl_spim!(SERIAL3, SPIM3, SERIAL3);
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
impl_twim!(UARTETWISPI1, TWIM1, SERIAL1);
impl_twim!(UARTETWISPI2, TWIM2, SERIAL2);
impl_twim!(UARTETWISPI3, TWIM3, SERIAL3);
impl_spis!(SERIAL0, SPIS0, SERIAL0);
impl_spis!(SERIAL1, SPIS1, SERIAL1);
impl_spis!(SERIAL2, SPIS2, SERIAL2);
impl_spis!(SERIAL3, SPIS3, SERIAL3);
impl_twim!(SERIAL0, TWIM0, SERIAL0);
impl_twim!(SERIAL1, TWIM1, SERIAL1);
impl_twim!(SERIAL2, TWIM2, SERIAL2);
impl_twim!(SERIAL3, TWIM3, SERIAL3);
impl_twis!(SERIAL0, TWIS0, SERIAL0);
impl_twis!(SERIAL1, TWIS1, SERIAL1);
impl_twis!(SERIAL2, TWIS2, SERIAL2);
impl_twis!(SERIAL3, TWIS3, SERIAL3);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
@ -375,9 +405,18 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
impl_qspi!(QSPI, QSPI, QSPI);
impl_pdm!(PDM0, PDM0, PDM0);
impl_qdec!(QDEC0, QDEC0, QDEC0);
impl_qdec!(QDEC1, QDEC1, QDEC1);
impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_02, 0, 2);
#[cfg(feature = "nfc-pins-as-gpio")]
impl_pin!(P0_03, 0, 3);
impl_pin!(P0_04, 0, 4);
impl_pin!(P0_05, 0, 5);
@ -458,59 +497,55 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
impl_ppi_channel!(PPI_CH30, 30 => configurable);
impl_ppi_channel!(PPI_CH31, 31 => configurable);
impl_saadc_input!(P0_13, ANALOGINPUT0);
impl_saadc_input!(P0_14, ANALOGINPUT1);
impl_saadc_input!(P0_15, ANALOGINPUT2);
impl_saadc_input!(P0_16, ANALOGINPUT3);
impl_saadc_input!(P0_17, ANALOGINPUT4);
impl_saadc_input!(P0_18, ANALOGINPUT5);
impl_saadc_input!(P0_19, ANALOGINPUT6);
impl_saadc_input!(P0_20, ANALOGINPUT7);
impl_saadc_input!(P0_13, ANALOG_INPUT0);
impl_saadc_input!(P0_14, ANALOG_INPUT1);
impl_saadc_input!(P0_15, ANALOG_INPUT2);
impl_saadc_input!(P0_16, ANALOG_INPUT3);
impl_saadc_input!(P0_17, ANALOG_INPUT4);
impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(FPU);
declare!(CACHE);
declare!(SPU);
declare!(CLOCK_POWER);
declare!(SERIAL0);
declare!(SERIAL1);
declare!(SPIM4);
declare!(SERIAL2);
declare!(SERIAL3);
declare!(GPIOTE0);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(RTC1);
declare!(WDT0);
declare!(WDT1);
declare!(COMP_LPCOMP);
declare!(EGU0);
declare!(EGU1);
declare!(EGU2);
declare!(EGU3);
declare!(EGU4);
declare!(EGU5);
declare!(PWM0);
declare!(PWM1);
declare!(PWM2);
declare!(PWM3);
declare!(PDM0);
declare!(I2S0);
declare!(IPC);
declare!(QSPI);
declare!(NFCT);
declare!(GPIOTE1);
declare!(QDEC0);
declare!(QDEC1);
declare!(USBD);
declare!(USBREGULATOR);
declare!(KMU);
declare!(CRYPTOCELL);
}
embassy_hal_common::interrupt_mod!(
FPU,
CACHE,
SPU,
CLOCK_POWER,
SERIAL0,
SERIAL1,
SPIM4,
SERIAL2,
SERIAL3,
GPIOTE0,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
RTC1,
WDT0,
WDT1,
COMP_LPCOMP,
EGU0,
EGU1,
EGU2,
EGU3,
EGU4,
EGU5,
PWM0,
PWM1,
PWM2,
PWM3,
PDM0,
I2S0,
IPC,
QSPI,
NFCT,
GPIOTE1,
QDEC0,
QDEC1,
USBD,
USBREGULATOR,
KMU,
CRYPTOCELL,
);

View File

@ -1,9 +1,12 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {
// The nRF5340 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf5340_net_pac::NVIC_PRIO_BITS;
#[doc(no_inline)]
pub use nrf5340_net_pac::{
interrupt,
@ -104,6 +107,8 @@ pub mod pac {
pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 256 * 1024;
embassy_hal_common::peripherals! {
// RTC
RTC0,
@ -112,15 +117,21 @@ embassy_hal_common::peripherals! {
// WDT
WDT,
// NVMC
NVMC,
// UARTE, TWI & SPI
UARTETWISPI0,
UARTETWISPI1,
UARTETWISPI2,
UARTETWISPI3,
SERIAL0,
SERIAL1,
SERIAL2,
SERIAL3,
// SAADC
SAADC,
// RNG
RNG,
// PWM
PWM0,
PWM1,
@ -236,14 +247,18 @@ embassy_hal_common::peripherals! {
P1_15,
}
impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0);
impl_spim!(UARTETWISPI0, SPIM0, SERIAL0);
impl_twim!(UARTETWISPI0, TWIM0, SERIAL0);
impl_uarte!(SERIAL0, UARTE0, SERIAL0);
impl_spim!(SERIAL0, SPIM0, SERIAL0);
impl_spis!(SERIAL0, SPIS0, SERIAL0);
impl_twim!(SERIAL0, TWIM0, SERIAL0);
impl_twis!(SERIAL0, TWIS0, SERIAL0);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
impl_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2);
@ -327,29 +342,25 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
impl_ppi_channel!(PPI_CH30, 30 => configurable);
impl_ppi_channel!(PPI_CH31, 31 => configurable);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(CLOCK_POWER);
declare!(RADIO);
declare!(RNG);
declare!(GPIOTE);
declare!(WDT);
declare!(TIMER0);
declare!(ECB);
declare!(AAR_CCM);
declare!(TEMP);
declare!(RTC0);
declare!(IPC);
declare!(SERIAL0);
declare!(EGU0);
declare!(RTC1);
declare!(TIMER1);
declare!(TIMER2);
declare!(SWI0);
declare!(SWI1);
declare!(SWI2);
declare!(SWI3);
}
embassy_hal_common::interrupt_mod!(
CLOCK_POWER,
RADIO,
RNG,
GPIOTE,
WDT,
TIMER0,
ECB,
AAR_CCM,
TEMP,
RTC0,
IPC,
SERIAL0,
EGU0,
RTC1,
TIMER1,
TIMER2,
SWI0,
SWI1,
SWI2,
SWI3,
);

View File

@ -1,9 +1,12 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {
// The nRF9160 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf9160_pac::NVIC_PRIO_BITS;
#[doc(no_inline)]
pub use nrf9160_pac::{
interrupt,
@ -164,6 +167,8 @@ pub mod pac {
pub const EASY_DMA_SIZE: usize = (1 << 13) - 1;
pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_common::peripherals! {
// RTC
RTC0,
@ -172,11 +177,14 @@ embassy_hal_common::peripherals! {
// WDT
WDT,
// NVMC
NVMC,
// UARTE, TWI & SPI
UARTETWISPI0,
UARTETWISPI1,
UARTETWISPI2,
UARTETWISPI3,
SERIAL0,
SERIAL1,
SERIAL2,
SERIAL3,
// SAADC
SAADC,
@ -260,28 +268,43 @@ embassy_hal_common::peripherals! {
P0_29,
P0_30,
P0_31,
// PDM
PDM,
}
impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_uarte!(UARTETWISPI1, UARTE1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_uarte!(UARTETWISPI2, UARTE2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_uarte!(UARTETWISPI3, UARTE3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_uarte!(SERIAL0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_uarte!(SERIAL1, UARTE1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_uarte!(SERIAL2, UARTE2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_uarte!(SERIAL3, UARTE3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_spim!(UARTETWISPI0, SPIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_spim!(UARTETWISPI1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_spim!(SERIAL0, SPIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_spim!(SERIAL1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_spim!(SERIAL2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_spim!(SERIAL3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_twim!(UARTETWISPI3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_spis!(SERIAL0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_spis!(SERIAL1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_spis!(SERIAL2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_spis!(SERIAL3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_twim!(SERIAL0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_twim!(SERIAL1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_twim!(SERIAL2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_twim!(SERIAL3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_twis!(SERIAL0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
impl_twis!(SERIAL1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
impl_twis!(SERIAL2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
impl_twis!(SERIAL3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
impl_pwm!(PWM3, PWM3, PWM3);
impl_pdm!(PDM, PDM, PDM);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
@ -336,49 +359,45 @@ impl_ppi_channel!(PPI_CH13, 13 => configurable);
impl_ppi_channel!(PPI_CH14, 14 => configurable);
impl_ppi_channel!(PPI_CH15, 15 => configurable);
impl_saadc_input!(P0_13, ANALOGINPUT0);
impl_saadc_input!(P0_14, ANALOGINPUT1);
impl_saadc_input!(P0_15, ANALOGINPUT2);
impl_saadc_input!(P0_16, ANALOGINPUT3);
impl_saadc_input!(P0_17, ANALOGINPUT4);
impl_saadc_input!(P0_18, ANALOGINPUT5);
impl_saadc_input!(P0_19, ANALOGINPUT6);
impl_saadc_input!(P0_20, ANALOGINPUT7);
impl_saadc_input!(P0_13, ANALOG_INPUT0);
impl_saadc_input!(P0_14, ANALOG_INPUT1);
impl_saadc_input!(P0_15, ANALOG_INPUT2);
impl_saadc_input!(P0_16, ANALOG_INPUT3);
impl_saadc_input!(P0_17, ANALOG_INPUT4);
impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
pub mod irqs {
use embassy_cortex_m::interrupt::_export::declare;
use crate::pac::Interrupt as InterruptEnum;
declare!(SPU);
declare!(CLOCK_POWER);
declare!(UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
declare!(UARTE1_SPIM1_SPIS1_TWIM1_TWIS1);
declare!(UARTE2_SPIM2_SPIS2_TWIM2_TWIS2);
declare!(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3);
declare!(GPIOTE0);
declare!(SAADC);
declare!(TIMER0);
declare!(TIMER1);
declare!(TIMER2);
declare!(RTC0);
declare!(RTC1);
declare!(WDT);
declare!(EGU0);
declare!(EGU1);
declare!(EGU2);
declare!(EGU3);
declare!(EGU4);
declare!(EGU5);
declare!(PWM0);
declare!(PWM1);
declare!(PWM2);
declare!(PDM);
declare!(PWM3);
declare!(I2S);
declare!(IPC);
declare!(FPU);
declare!(GPIOTE1);
declare!(KMU);
declare!(CRYPTOCELL);
}
embassy_hal_common::interrupt_mod!(
SPU,
CLOCK_POWER,
UARTE0_SPIM0_SPIS0_TWIM0_TWIS0,
UARTE1_SPIM1_SPIS1_TWIM1_TWIS1,
UARTE2_SPIM2_SPIS2_TWIM2_TWIS2,
UARTE3_SPIM3_SPIS3_TWIM3_TWIS3,
GPIOTE0,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
RTC1,
WDT,
EGU0,
EGU1,
EGU2,
EGU3,
EGU4,
EGU5,
PWM0,
PWM1,
PWM2,
PDM,
PWM3,
I2S,
IPC,
FPU,
GPIOTE1,
KMU,
CRYPTOCELL,
);

View File

@ -1,4 +1,4 @@
//! General purpose input/output for nRF.
//! General purpose input/output (GPIO) driver.
#![macro_use]
use core::convert::Infallible;
@ -70,7 +70,7 @@ impl<'d, T: Pin> Input<'d, T> {
}
/// Digital input or output level.
#[derive(Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Level {
/// Logical low.
@ -88,9 +88,9 @@ impl From<bool> for Level {
}
}
impl Into<bool> for Level {
fn into(self) -> bool {
match self {
impl From<Level> for bool {
fn from(level: Level) -> bool {
match level {
Level::Low => false,
Level::High => true,
}
@ -574,7 +574,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -588,7 +588,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -598,7 +598,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -615,7 +615,7 @@ mod eh1 {
/// Implement [`InputPin`] for [`Flex`];
///
/// If the pin is not in input mode the result is unspecified.
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -625,7 +625,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -635,7 +635,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}

View File

@ -1,40 +1,50 @@
//! GPIO task/event (GPIOTE) driver.
use core::convert::Infallible;
use core::future::Future;
use core::future::{poll_fn, Future};
use core::task::{Context, Poll};
use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef};
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin};
use crate::interrupt::{Interrupt, InterruptExt};
use crate::interrupt::InterruptExt;
use crate::ppi::{Event, Task};
use crate::{interrupt, pac, peripherals};
pub const CHANNEL_COUNT: usize = 8;
/// Amount of GPIOTE channels in the chip.
const CHANNEL_COUNT: usize = 8;
#[cfg(any(feature = "nrf52833", feature = "nrf52840"))]
pub const PIN_COUNT: usize = 48;
const PIN_COUNT: usize = 48;
#[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))]
pub const PIN_COUNT: usize = 32;
const PIN_COUNT: usize = 32;
#[allow(clippy::declare_interior_mutable_const)]
const NEW_AW: AtomicWaker = AtomicWaker::new();
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];
static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT];
/// Polarity for listening to events for GPIOTE input channels.
pub enum InputChannelPolarity {
/// Don't listen for any pin changes.
None,
/// Listen for high to low changes.
HiToLo,
/// Listen for low to high changes.
LoToHi,
/// Listen for any change, either low to high or high to low.
Toggle,
}
/// Polarity of the `task out` operation.
/// Polarity of the OUT task operation for GPIOTE output channels.
pub enum OutputChannelPolarity {
/// Set the pin high.
Set,
/// Set the pin low.
Clear,
/// Toggle the pin.
Toggle,
}
@ -64,42 +74,41 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
}
// Enable interrupts
cfg_if::cfg_if! {
if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s"))] {
let irq = unsafe { interrupt::GPIOTE0::steal() };
} else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns"))] {
let irq = unsafe { interrupt::GPIOTE1::steal() };
} else {
let irq = unsafe { interrupt::GPIOTE::steal() };
}
}
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))]
let irq = interrupt::GPIOTE0;
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
let irq = interrupt::GPIOTE1;
#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))]
let irq = interrupt::GPIOTE;
irq.unpend();
irq.set_priority(irq_prio);
irq.enable();
unsafe { irq.enable() };
let g = regs();
g.events_port.write(|w| w);
g.intenset.write(|w| w.port().set());
}
cfg_if::cfg_if! {
if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s"))] {
#[interrupt]
fn GPIOTE0() {
unsafe { handle_gpiote_interrupt() };
}
} else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns"))] {
#[interrupt]
fn GPIOTE1() {
unsafe { handle_gpiote_interrupt() };
}
} else {
#[interrupt]
fn GPIOTE() {
unsafe { handle_gpiote_interrupt() };
}
}
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))]
#[cfg(feature = "rt")]
#[interrupt]
fn GPIOTE0() {
unsafe { handle_gpiote_interrupt() };
}
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
#[cfg(feature = "rt")]
#[interrupt]
fn GPIOTE1() {
unsafe { handle_gpiote_interrupt() };
}
#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))]
#[cfg(feature = "rt")]
#[interrupt]
fn GPIOTE() {
unsafe { handle_gpiote_interrupt() };
}
unsafe fn handle_gpiote_interrupt() {
@ -149,7 +158,7 @@ impl Iterator for BitIter {
/// GPIOTE channel driver in input mode
pub struct InputChannel<'d, C: Channel, T: GpioPin> {
ch: C,
ch: PeripheralRef<'d, C>,
pin: Input<'d, T>,
}
@ -163,7 +172,10 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> {
}
impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
pub fn new(ch: C, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
/// Create a new GPIOTE input channel driver.
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self {
into_ref!(ch);
let g = regs();
let num = ch.number();
@ -187,6 +199,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
InputChannel { ch, pin }
}
/// Asynchronously wait for an event in this channel.
pub async fn wait(&self) {
let g = regs();
let num = self.ch.number();
@ -208,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
}
/// Returns the IN event, for use with PPI.
pub fn event_in(&self) -> Event {
pub fn event_in(&self) -> Event<'d> {
let g = regs();
Event::from_reg(&g.events_in[self.ch.number()])
}
@ -216,7 +229,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
/// GPIOTE channel driver in output mode
pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
ch: C,
ch: PeripheralRef<'d, C>,
_pin: Output<'d, T>,
}
@ -230,7 +243,9 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
}
impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
pub fn new(ch: C, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
/// Create a new GPIOTE output channel driver.
pub fn new(ch: impl Peripheral<P = C> + 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self {
into_ref!(ch);
let g = regs();
let num = ch.number();
@ -256,20 +271,20 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
OutputChannel { ch, _pin: pin }
}
/// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle).
/// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle).
pub fn out(&self) {
let g = regs();
g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) });
}
/// Triggers `task set` (set associated pin high).
/// Triggers the SET task (set associated pin high).
#[cfg(not(feature = "nrf51"))]
pub fn set(&self) {
let g = regs();
g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) });
}
/// Triggers `task clear` (set associated pin low).
/// Triggers the CLEAR task (set associated pin low).
#[cfg(not(feature = "nrf51"))]
pub fn clear(&self) {
let g = regs();
@ -277,21 +292,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
}
/// Returns the OUT task, for use with PPI.
pub fn task_out(&self) -> Task {
pub fn task_out(&self) -> Task<'d> {
let g = regs();
Task::from_reg(&g.tasks_out[self.ch.number()])
}
/// Returns the CLR task, for use with PPI.
#[cfg(not(feature = "nrf51"))]
pub fn task_clr(&self) -> Task {
pub fn task_clr(&self) -> Task<'d> {
let g = regs();
Task::from_reg(&g.tasks_clr[self.ch.number()])
}
/// Returns the SET task, for use with PPI.
#[cfg(not(feature = "nrf51"))]
pub fn task_set(&self) -> Task {
pub fn task_set(&self) -> Task<'d> {
let g = regs();
Task::from_reg(&g.tasks_set[self.ch.number()])
}
@ -299,6 +314,7 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
// =======================
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub(crate) struct PortInputFuture<'a> {
pin: PeripheralRef<'a, AnyPin>,
}
@ -334,48 +350,58 @@ impl<'a> Future for PortInputFuture<'a> {
}
impl<'d, T: GpioPin> Input<'d, T> {
/// Wait until the pin is high. If it is already high, return immediately.
pub async fn wait_for_high(&mut self) {
self.pin.wait_for_high().await
}
/// Wait until the pin is low. If it is already low, return immediately.
pub async fn wait_for_low(&mut self) {
self.pin.wait_for_low().await
}
/// Wait for the pin to undergo a transition from low to high.
pub async fn wait_for_rising_edge(&mut self) {
self.pin.wait_for_rising_edge().await
}
/// Wait for the pin to undergo a transition from high to low.
pub async fn wait_for_falling_edge(&mut self) {
self.pin.wait_for_falling_edge().await
}
/// Wait for the pin to undergo any transition, i.e low to high OR high to low.
pub async fn wait_for_any_edge(&mut self) {
self.pin.wait_for_any_edge().await
}
}
impl<'d, T: GpioPin> Flex<'d, T> {
/// Wait until the pin is high. If it is already high, return immediately.
pub async fn wait_for_high(&mut self) {
self.pin.conf().modify(|_, w| w.sense().high());
PortInputFuture::new(&mut self.pin).await
}
/// Wait until the pin is low. If it is already low, return immediately.
pub async fn wait_for_low(&mut self) {
self.pin.conf().modify(|_, w| w.sense().low());
PortInputFuture::new(&mut self.pin).await
}
/// Wait for the pin to undergo a transition from low to high.
pub async fn wait_for_rising_edge(&mut self) {
self.wait_for_low().await;
self.wait_for_high().await;
}
/// Wait for the pin to undergo a transition from high to low.
pub async fn wait_for_falling_edge(&mut self) {
self.wait_for_high().await;
self.wait_for_low().await;
}
/// Wait for the pin to undergo any transition, i.e low to high OR high to low.
pub async fn wait_for_any_edge(&mut self) {
if self.is_high() {
self.pin.conf().modify(|_, w| w.sense().low());
@ -392,8 +418,17 @@ mod sealed {
pub trait Channel {}
}
/// GPIOTE channel trait.
///
/// Implemented by all GPIOTE channels.
pub trait Channel: sealed::Channel + Sized {
/// Get the channel number.
fn number(&self) -> usize;
/// Convert this channel to a type-erased `AnyChannel`.
///
/// This allows using several channels in situations that might require
/// them to be the same type, like putting them in an array.
fn degrade(self) -> AnyChannel {
AnyChannel {
number: self.number() as u8,
@ -401,6 +436,12 @@ pub trait Channel: sealed::Channel + Sized {
}
}
/// Type-erased channel.
///
/// Obtained by calling `Channel::degrade`.
///
/// This allows using several channels in situations that might require
/// them to be the same type, like putting them in an array.
pub struct AnyChannel {
number: u8,
}
@ -458,7 +499,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for InputChannel<'d, C, T> {
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.pin.is_high())
}
@ -469,72 +510,51 @@ mod eh1 {
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
use futures::FutureExt;
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use super::*;
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.wait_for_high().map(Ok)
}
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.wait_for_low().map(Ok)
}
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.wait_for_rising_edge().map(Ok)
}
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.wait_for_falling_edge().map(Ok)
}
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.wait_for_any_edge().map(Ok)
}
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_high().await)
}
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_low().await)
}
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
self.wait_for_high().map(Ok)
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_rising_edge().await)
}
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_falling_edge().await)
}
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
self.wait_for_low().map(Ok)
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_any_edge().await)
}
}
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_high().await)
}
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
self.wait_for_rising_edge().map(Ok)
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_low().await)
}
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_rising_edge().await)
}
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
self.wait_for_falling_edge().map(Ok)
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_falling_edge().await)
}
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
self.wait_for_any_edge().map(Ok)
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
Ok(self.wait_for_any_edge().await)
}
}
}

1194
embassy-nrf/src/i2s.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +1,7 @@
//! # Embassy nRF HAL
//!
//! HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed.
//!
//! The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs
//! for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to
//! complete operations in low power mod and handling interrupts, so that applications can focus on more important matters.
//!
//! ## EasyDMA considerations
//!
//! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting
//! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI).
//! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust
//! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen:
//!
//! ```no_run
//! // As we pass a slice to the function whose contents will not ever change,
//! // the compiler writes it into the flash and thus the pointer to it will
//! // reference static memory. Since EasyDMA requires slices to reside in RAM,
//! // this function call will fail.
//! let result = spim.write_from_ram(&[1, 2, 3]);
//! assert_eq!(result, Err(Error::DMABufferNotInDataMemory));
//!
//! // The data is still static and located in flash. However, since we are assigning
//! // it to a variable, the compiler will load it into memory. Passing a reference to the
//! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy.
//! // This function call succeeds.
//! let data = [1, 2, 3];
//! let result = spim.write_from_ram(&data);
//! assert!(result.is_ok());
//! ```
//!
//! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions:
//! - Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM.
//! - Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission.
//!
//! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will
//! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit
//! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage
//! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840).
//!
//! Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as
//! mutable slices always reside in RAM.
#![no_std]
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#[cfg(not(any(
feature = "nrf51",
@ -62,6 +20,12 @@
)))]
compile_error!("No chip feature activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840");
#[cfg(all(feature = "reset-pin-as-gpio", not(feature = "_nrf52")))]
compile_error!("feature `reset-pin-as-gpio` is only valid for nRF52 series chips.");
#[cfg(all(feature = "nfc-pins-as-gpio", not(any(feature = "_nrf52", feature = "_nrf5340-app"))))]
compile_error!("feature `nfc-pins-as-gpio` is only valid for nRF52, or nRF53's application core.");
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
pub(crate) mod util;
@ -69,29 +33,41 @@ pub(crate) mod util;
#[cfg(feature = "_time-driver")]
mod time_driver;
#[cfg(feature = "nightly")]
pub mod buffered_uarte;
pub mod gpio;
#[cfg(feature = "gpiote")]
pub mod gpiote;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
pub mod i2s;
pub mod nvmc;
#[cfg(any(
feature = "nrf52810",
feature = "nrf52811",
feature = "nrf52832",
feature = "nrf52833",
feature = "nrf52840",
feature = "_nrf5340-app",
feature = "_nrf9160"
))]
pub mod pdm;
pub mod ppi;
#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod pwm;
#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))]
#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))]
pub mod qdec;
#[cfg(feature = "nrf52840")]
#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))]
pub mod qspi;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))]
pub mod rng;
#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc;
pub mod spim;
pub mod spis;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub mod temp;
pub mod timer;
pub mod twim;
pub mod twis;
pub mod uarte;
#[cfg(any(
feature = "_nrf5340-app",
@ -103,14 +79,6 @@ pub mod uarte;
pub mod usb;
#[cfg(not(feature = "_nrf5340"))]
pub mod wdt;
#[cfg(any(
feature = "nrf52810",
feature = "nrf52811",
feature = "nrf52832",
feature = "nrf52833",
feature = "nrf52840",
))]
pub mod pdm;
// This mod MUST go last, so that it sees all the `impl_foo!` macros
#[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")]
@ -125,15 +93,32 @@ pub mod pdm;
#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")]
mod chip;
pub use chip::EASY_DMA_SIZE;
/// Macro to bind interrupts to handlers.
///
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
$vis struct $name;
pub mod interrupt {
//! nRF interrupts for cortex-m devices.
pub use cortex_m::interrupt::{CriticalSection, Mutex};
pub use embassy_cortex_m::interrupt::*;
$(
#[allow(non_snake_case)]
#[no_mangle]
unsafe extern "C" fn $irq() {
$(
<$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
)*
}
pub use crate::chip::irqs::*;
}
$(
unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
)*
)*
};
}
// Reexports
@ -141,11 +126,12 @@ pub mod interrupt {
pub use chip::pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use chip::pac;
pub use chip::{peripherals, Peripherals};
pub use embassy_cortex_m::executor;
pub use embassy_cortex_m::interrupt::_export::interrupt;
pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
pub use crate::chip::interrupt;
pub use crate::pac::NVIC_PRIO_BITS;
pub mod config {
//! Configuration options used when initializing the HAL.
@ -174,6 +160,47 @@ pub mod config {
ExternalFullSwing,
}
/// SWD access port protection setting.
#[non_exhaustive]
pub enum Debug {
/// Debugging is allowed (APPROTECT is disabled). Default.
Allowed,
/// Debugging is not allowed (APPROTECT is enabled).
Disallowed,
/// APPROTECT is not configured (neither to enable it or disable it).
/// This can be useful if you're already doing it by other means and
/// you don't want embassy-nrf to touch UICR.
NotConfigured,
}
/// Settings for enabling the built in DCDC converters.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub struct DcdcConfig {
/// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used.
#[cfg(feature = "nrf52840")]
pub reg0: bool,
/// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used.
pub reg1: bool,
}
/// Settings for enabling the built in DCDC converters.
#[cfg(feature = "_nrf5340-app")]
pub struct DcdcConfig {
/// Config for the high voltage stage, if disabled LDO will be used.
pub regh: bool,
/// Config for the main rail, if disabled LDO will be used.
pub regmain: bool,
/// Config for the radio rail, if disabled LDO will be used.
pub regradio: bool,
}
/// Settings for enabling the built in DCDC converter.
#[cfg(feature = "_nrf9160")]
pub struct DcdcConfig {
/// Config for the main rail, if disabled LDO will be used.
pub regmain: bool,
}
/// Configuration for peripherals. Default configuration should work on any nRF chip.
#[non_exhaustive]
pub struct Config {
@ -181,12 +208,17 @@ pub mod config {
pub hfclk_source: HfclkSource,
/// Low frequency clock source.
pub lfclk_source: LfclkSource,
#[cfg(not(feature = "_nrf5340-net"))]
/// DCDC configuration.
pub dcdc: DcdcConfig,
/// GPIOTE interrupt priority. Should be lower priority than softdevice if used.
#[cfg(feature = "gpiote")]
pub gpiote_interrupt_priority: crate::interrupt::Priority,
/// Time driver interrupt priority. Should be lower priority than softdevice if used.
#[cfg(feature = "_time-driver")]
pub time_interrupt_priority: crate::interrupt::Priority,
/// Enable or disable the debug port.
pub debug: Debug,
}
impl Default for Config {
@ -197,21 +229,218 @@ pub mod config {
// xtals if they know they have them.
hfclk_source: HfclkSource::Internal,
lfclk_source: LfclkSource::InternalRC,
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
dcdc: DcdcConfig {
#[cfg(feature = "nrf52840")]
reg0: false,
reg1: false,
},
#[cfg(feature = "_nrf5340-app")]
dcdc: DcdcConfig {
regh: false,
regmain: false,
regradio: false,
},
#[cfg(feature = "_nrf9160")]
dcdc: DcdcConfig { regmain: false },
#[cfg(feature = "gpiote")]
gpiote_interrupt_priority: crate::interrupt::Priority::P0,
#[cfg(feature = "_time-driver")]
time_interrupt_priority: crate::interrupt::Priority::P0,
// In NS mode, default to NotConfigured, assuming the S firmware will do it.
#[cfg(feature = "_ns")]
debug: Debug::NotConfigured,
#[cfg(not(feature = "_ns"))]
debug: Debug::Allowed,
}
}
}
}
#[cfg(feature = "_nrf9160")]
#[allow(unused)]
mod consts {
pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32;
pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32;
pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
}
#[cfg(feature = "_nrf5340-app")]
#[allow(unused)]
mod consts {
pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32;
pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF801C as *mut u32;
pub const UICR_NFCPINS: *mut u32 = 0x00FF8028 as *mut u32;
pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
pub const APPROTECT_DISABLED: u32 = 0x50FA50FA;
}
#[cfg(feature = "_nrf5340-net")]
#[allow(unused)]
mod consts {
pub const UICR_APPROTECT: *mut u32 = 0x01FF8000 as *mut u32;
pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
pub const APPROTECT_DISABLED: u32 = 0x50FA50FA;
}
#[cfg(feature = "_nrf52")]
#[allow(unused)]
mod consts {
pub const UICR_PSELRESET1: *mut u32 = 0x10001200 as *mut u32;
pub const UICR_PSELRESET2: *mut u32 = 0x10001204 as *mut u32;
pub const UICR_NFCPINS: *mut u32 = 0x1000120C as *mut u32;
pub const UICR_APPROTECT: *mut u32 = 0x10001208 as *mut u32;
pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
pub const APPROTECT_DISABLED: u32 = 0x0000_005a;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum WriteResult {
/// Word was written successfully, needs reset.
Written,
/// Word was already set to the value we wanted to write, nothing was done.
Noop,
/// Word is already set to something else, we couldn't write the desired value.
Failed,
}
unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult {
uicr_write_masked(address, value, 0xFFFF_FFFF)
}
unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult {
let curr_val = address.read_volatile();
if curr_val & mask == value & mask {
return WriteResult::Noop;
}
// We can only change `1` bits to `0` bits.
if curr_val & value & mask != value & mask {
return WriteResult::Failed;
}
let nvmc = &*pac::NVMC::ptr();
nvmc.config.write(|w| w.wen().wen());
while nvmc.ready.read().ready().is_busy() {}
address.write_volatile(value | !mask);
while nvmc.ready.read().ready().is_busy() {}
nvmc.config.reset();
while nvmc.ready.read().ready().is_busy() {}
WriteResult::Written
}
/// Initialize peripherals with the provided configuration. This should only be called once at startup.
pub fn init(config: config::Config) -> Peripherals {
// Do this first, so that it panics if user is calling `init` a second time
// before doing anything important.
let peripherals = Peripherals::take();
let mut needs_reset = false;
// Setup debug protection.
match config.debug {
config::Debug::Allowed => {
#[cfg(feature = "_nrf52")]
unsafe {
let variant = (0x1000_0104 as *mut u32).read_volatile();
// Get the letter for the build code (b'A' .. b'F')
let build_code = (variant >> 8) as u8;
if build_code >= b'F' {
// Chips with build code F and higher (revision 3 and higher) have an
// improved APPROTECT ("hardware and software controlled access port protection")
// which needs explicit action by the firmware to keep it unlocked
// UICR.APPROTECT = SwDisabled
let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);
needs_reset |= res == WriteResult::Written;
// APPROTECT.DISABLE = SwDisabled
(0x4000_0558 as *mut u32).write_volatile(consts::APPROTECT_DISABLED);
} else {
// nothing to do on older chips, debug is allowed by default.
}
}
#[cfg(feature = "_nrf5340")]
unsafe {
let p = &*pac::CTRLAP::ptr();
let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);
needs_reset |= res == WriteResult::Written;
p.approtect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED));
#[cfg(feature = "_nrf5340-app")]
{
let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED);
needs_reset |= res == WriteResult::Written;
p.secureapprotect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED));
}
}
// nothing to do on the nrf9160, debug is allowed by default.
}
config::Debug::Disallowed => unsafe {
// UICR.APPROTECT = Enabled
let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_ENABLED);
needs_reset |= res == WriteResult::Written;
#[cfg(any(feature = "_nrf5340-app", feature = "_nrf9160"))]
{
let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED);
needs_reset |= res == WriteResult::Written;
}
},
config::Debug::NotConfigured => {}
}
#[cfg(feature = "_nrf52")]
unsafe {
let value = if cfg!(feature = "reset-pin-as-gpio") {
!0
} else {
chip::RESET_PIN
};
let res1 = uicr_write(consts::UICR_PSELRESET1, value);
let res2 = uicr_write(consts::UICR_PSELRESET2, value);
needs_reset |= res1 == WriteResult::Written || res2 == WriteResult::Written;
if res1 == WriteResult::Failed || res2 == WriteResult::Failed {
#[cfg(not(feature = "reset-pin-as-gpio"))]
warn!(
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
);
#[cfg(feature = "reset-pin-as-gpio")]
warn!(
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
);
}
}
#[cfg(any(feature = "_nrf52", feature = "_nrf5340-app"))]
unsafe {
let value = if cfg!(feature = "nfc-pins-as-gpio") { 0 } else { 1 };
let res = uicr_write_masked(consts::UICR_NFCPINS, value, 1);
needs_reset |= res == WriteResult::Written;
if res == WriteResult::Failed {
// with nfc-pins-as-gpio, this can never fail because we're writing all zero bits.
#[cfg(not(feature = "nfc-pins-as-gpio"))]
warn!(
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
);
}
}
if needs_reset {
cortex_m::peripheral::SCB::sys_reset();
}
let r = unsafe { &*pac::CLOCK::ptr() };
// Start HFCLK.
@ -259,6 +488,41 @@ pub fn init(config: config::Config) -> Peripherals {
r.tasks_lfclkstart.write(|w| unsafe { w.bits(1) });
while r.events_lfclkstarted.read().bits() == 0 {}
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
{
// Setup DCDCs.
let pwr = unsafe { &*pac::POWER::ptr() };
#[cfg(feature = "nrf52840")]
if config.dcdc.reg0 {
pwr.dcdcen0.write(|w| w.dcdcen().set_bit());
}
if config.dcdc.reg1 {
pwr.dcdcen.write(|w| w.dcdcen().set_bit());
}
}
#[cfg(feature = "_nrf9160")]
{
// Setup DCDC.
let reg = unsafe { &*pac::REGULATORS::ptr() };
if config.dcdc.regmain {
reg.dcdcen.write(|w| w.dcdcen().set_bit());
}
}
#[cfg(feature = "_nrf5340-app")]
{
// Setup DCDC.
let reg = unsafe { &*pac::REGULATORS::ptr() };
if config.dcdc.regh {
reg.vregh.dcdcen.write(|w| w.dcdcen().set_bit());
}
if config.dcdc.regmain {
reg.vregmain.dcdcen.write(|w| w.dcdcen().set_bit());
}
if config.dcdc.regradio {
reg.vregradio.dcdcen.write(|w| w.dcdcen().set_bit());
}
}
// Init GPIOTE
#[cfg(feature = "gpiote")]
gpiote::init(config.gpiote_interrupt_priority);
@ -267,5 +531,12 @@ pub fn init(config: config::Config) -> Peripherals {
#[cfg(feature = "_time-driver")]
time_driver::init(config.time_interrupt_priority);
// Disable UARTE (enabled by default for some reason)
#[cfg(feature = "_nrf9160")]
unsafe {
(*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled());
(*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled());
}
peripherals
}

View File

@ -1,4 +1,4 @@
//! Non-Volatile Memory Controller (NVMC) module.
//! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver.
use core::{ptr, slice};
@ -10,8 +10,12 @@ use embedded_storage::nor_flash::{
use crate::peripherals::NVMC;
use crate::{pac, Peripheral};
#[cfg(not(feature = "_nrf5340-net"))]
/// Erase size of NVMC flash in bytes.
pub const PAGE_SIZE: usize = 4096;
#[cfg(feature = "_nrf5340-net")]
/// Erase size of NVMC flash in bytes.
pub const PAGE_SIZE: usize = 2048;
/// Size of NVMC flash in bytes.
pub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE;
@ -20,7 +24,7 @@ pub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// Opration using a location not in flash.
/// Operation using a location not in flash.
OutOfBounds,
/// Unaligned operation or using unaligned buffers.
Unaligned,
@ -55,6 +59,51 @@ impl<'d> Nvmc<'d> {
let p = Self::regs();
while p.ready.read().ready().is_busy() {}
}
#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))]
fn wait_ready_write(&mut self) {
self.wait_ready();
}
#[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))]
fn wait_ready_write(&mut self) {
let p = Self::regs();
while p.readynext.read().readynext().is_busy() {}
}
#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))]
fn erase_page(&mut self, page_addr: u32) {
Self::regs().erasepage().write(|w| unsafe { w.bits(page_addr) });
}
#[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))]
fn erase_page(&mut self, page_addr: u32) {
let first_page_word = page_addr as *mut u32;
unsafe {
first_page_word.write_volatile(0xFFFF_FFFF);
}
}
fn enable_erase(&self) {
#[cfg(not(feature = "_ns"))]
Self::regs().config.write(|w| w.wen().een());
#[cfg(feature = "_ns")]
Self::regs().configns.write(|w| w.wen().een());
}
fn enable_read(&self) {
#[cfg(not(feature = "_ns"))]
Self::regs().config.write(|w| w.wen().ren());
#[cfg(feature = "_ns")]
Self::regs().configns.write(|w| w.wen().ren());
}
fn enable_write(&self) {
#[cfg(not(feature = "_ns"))]
Self::regs().config.write(|w| w.wen().wen());
#[cfg(feature = "_ns")]
Self::regs().configns.write(|w| w.wen().wen());
}
}
impl<'d> MultiwriteNorFlash for Nvmc<'d> {}
@ -93,17 +142,15 @@ impl<'d> NorFlash for Nvmc<'d> {
return Err(Error::Unaligned);
}
let p = Self::regs();
p.config.write(|w| w.wen().een());
self.enable_erase();
self.wait_ready();
for page in (from..to).step_by(PAGE_SIZE) {
p.erasepage().write(|w| unsafe { w.bits(page) });
for page_addr in (from..to).step_by(PAGE_SIZE) {
self.erase_page(page_addr);
self.wait_ready();
}
p.config.reset();
self.enable_read();
self.wait_ready();
Ok(())
@ -117,9 +164,7 @@ impl<'d> NorFlash for Nvmc<'d> {
return Err(Error::Unaligned);
}
let p = Self::regs();
p.config.write(|w| w.wen().wen());
self.enable_write();
self.wait_ready();
unsafe {
@ -129,11 +174,11 @@ impl<'d> NorFlash for Nvmc<'d> {
for i in 0..words {
let w = ptr::read_unaligned(p_src.add(i));
ptr::write_volatile(p_dst.add(i), w);
self.wait_ready();
self.wait_ready_write();
}
}
p.config.reset();
self.enable_read();
self.wait_ready();
Ok(())

View File

@ -1,66 +1,71 @@
//! Pulse Density Modulation (PDM) mirophone driver.
#![macro_use]
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_sync::waitqueue::AtomicWaker;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn;
use pac::{pdm, PDM};
use pdm::mode::{EDGE_A, OPERATION_A};
pub use pdm::pdmclkctrl::FREQ_A as Frequency;
pub use pdm::ratio::RATIO_A as Ratio;
use fixed::types::I7F1;
use crate::interrupt::InterruptExt;
use crate::gpio::Pin as GpioPin;
use crate::{interrupt, pac, peripherals, Peripheral};
use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
if r.events_end.read().bits() != 0 {
r.intenclr.write(|w| w.end().clear());
}
if r.events_started.read().bits() != 0 {
r.intenclr.write(|w| w.started().clear());
}
if r.events_stopped.read().bits() != 0 {
r.intenclr.write(|w| w.stopped().clear());
}
T::state().waker.wake();
}
}
/// PDM microphone interface
pub struct Pdm<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
}
/// PDM error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {}
/// One-shot and continuous PDM.
pub struct Pdm<'d> {
_p: PeripheralRef<'d, peripherals::PDM>,
pub enum Error {
/// Buffer is too long.
BufferTooLong,
/// Buffer is empty
BufferZeroLength,
/// PDM is not running
NotRunning,
/// PDM is already running
AlreadyRunning,
}
static WAKER: AtomicWaker = AtomicWaker::new();
/// Used to configure the PDM peripheral.
///
/// See the `Default` impl for suitable default values.
#[non_exhaustive]
pub struct Config {
/// Clock frequency
pub frequency: Frequency,
/// Clock ratio
pub ratio: Ratio,
/// Channels
pub channels: Channels,
/// Edge to sample on
pub left_edge: Edge,
/// Gain left in dB
pub gain_left: I7F1,
/// Gain right in dB
pub gain_right: I7F1,
}
impl Default for Config {
/// Default configuration for single channel sampling.
fn default() -> Self {
Self {
frequency: Frequency::DEFAULT,
ratio: Ratio::RATIO80,
channels: Channels::Stereo,
left_edge: Edge::FallingEdge,
gain_left: I7F1::ZERO,
gain_right: I7F1::ZERO,
}
}
}
static DUMMY_BUFFER: [i16; 1] = [0; 1];
/// The state of a continuously running sampler. While it reflects
/// the progress of a sampler, it also signals what should be done
@ -68,69 +73,66 @@ impl Default for Config {
/// can then tear down its infrastructure.
#[derive(PartialEq)]
pub enum SamplerState {
/// The sampler processed the samples and is ready for more.
Sampled,
/// The sampler is done processing samples.
Stopped,
}
impl<'d> Pdm<'d> {
impl<'d, T: Instance> Pdm<'d, T> {
/// Create PDM driver
pub fn new(
pdm: impl Peripheral<P = peripherals::PDM> + 'd,
irq: impl Peripheral<P = interrupt::PDM> + 'd,
data: impl Peripheral<P = impl GpioPin> + 'd,
clock: impl Peripheral<P = impl GpioPin> + 'd,
pdm: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(pdm, irq, data, clock);
into_ref!(pdm, clk, din);
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
}
let r = unsafe { &*PDM::ptr() };
fn new_inner(
pdm: PeripheralRef<'d, T>,
clk: PeripheralRef<'d, AnyPin>,
din: PeripheralRef<'d, AnyPin>,
config: Config,
) -> Self {
into_ref!(pdm);
let Config { frequency, ratio, channels, left_edge, gain_left, gain_right } = config;
let r = T::regs();
// Configure channels
r.enable.write(|w| w.enable().enabled());
r.pdmclkctrl.write(|w| w.freq().variant(frequency));
r.ratio.write(|w| w.ratio().variant(ratio));
// setup gpio pins
din.conf().write(|w| w.input().set_bit());
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
clk.set_low();
clk.conf().write(|w| w.dir().output());
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
// configure
r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
r.ratio.write(|w| w.ratio().variant(config.ratio));
r.mode.write(|w| {
w.operation().variant(channels.into());
w.edge().variant(left_edge.into());
w.operation().variant(config.operation_mode.into());
w.edge().variant(config.edge.into());
w
});
Self::_set_gain(r, gain_left, gain_right);
r.psel.din.write(|w| unsafe { w.bits(data.psel_bits()) });
r.psel.clk.write(|w| unsafe { w.bits(clock.psel_bits()) });
Self::_set_gain(r, config.gain_left, config.gain_right);
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
// IRQ
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: pdm }
r.enable.write(|w| w.enable().set_bit());
Self { _peri: pdm }
}
fn on_interrupt(_ctx: *mut ()) {
let r = Self::regs();
if r.events_end.read().bits() != 0 {
r.intenclr.write(|w| w.end().clear());
WAKER.wake();
}
if r.events_started.read().bits() != 0 {
r.intenclr.write(|w| w.started().clear());
WAKER.wake();
}
if r.events_stopped.read().bits() != 0 {
r.intenclr.write(|w| w.stopped().clear());
WAKER.wake();
}
}
fn _set_gain(r: &pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
let gain_left = gain_left.saturating_add(I7F1::from_bits(40)).saturating_to_num::<u8>().clamp(0, 0x50);
let gain_right = gain_right.saturating_add(I7F1::from_bits(40)).saturating_to_num::<u8>().clamp(0, 0x50);
@ -138,81 +140,111 @@ impl<'d> Pdm<'d> {
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
}
/// Adjust the gain of the PDM microphone on the fly
pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
Self::_set_gain(Self::regs(), gain_left, gain_right)
Self::_set_gain(T::regs(), gain_left, gain_right)
}
fn regs() -> &'static pdm::RegisterBlock {
unsafe { &*PDM::ptr() }
/// Start sampling microphon data into a dummy buffer
/// Usefull to start the microphon and keep it active between recording samples
pub async fn start(&mut self) {
let r = T::regs();
// start dummy sampling because microphon needs some setup time
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
r.tasks_start.write(|w| unsafe { w.bits(1) });
}
/// One shot sampling. If the PDM is configured for multiple channels, the samples will be interleaved.
/// The first samples from the PDM peripheral and microphone usually contain garbage data, so the discard parameter sets the number of complete buffers to discard before returning.
pub async fn sample<const N: usize>(&mut self, mut discard: usize, buf: &mut [i16; N]) {
let r = Self::regs();
/// Stop sampling microphon data inta a dummy buffer
pub async fn stop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.events_started.reset();
}
// Set up the DMA
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(buf.as_mut_ptr() as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
/// Sample data into the given buffer.
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
// Reset and enable the events
r.events_end.reset();
r.events_stopped.reset();
r.intenset.write(|w| {
w.end().set();
w.stopped().set();
w
let r = T::regs();
if r.events_started.read().bits() == 0 {
return Err(Error::NotRunning);
}
let drop = OnDrop::new(move || {
r.intenclr.write(|w| w.end().clear());
r.events_stopped.reset();
// reset to dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
while r.events_stopped.read().bits() == 0 {}
});
// Don't reorder the start event before the previous writes. Hopefully self
// wouldn't happen anyway.
// setup user buffer
let ptr = buffer.as_ptr();
let len = buffer.len();
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
// wait till the current sample is finished and the user buffer sample is started
Self::wait_for_sample().await;
// reset the buffer back to the dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
// wait till the user buffer is sampled
Self::wait_for_sample().await;
drop.defuse();
Ok(())
}
async fn wait_for_sample() {
let r = T::regs();
r.events_end.reset();
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
r.tasks_start.write(|w| w.tasks_start().set_bit());
let ondrop = OnDrop::new(|| {
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
// N.B. It would be better if this were async, but Drop only support sync code.
while r.events_stopped.read().bits() != 0 {}
});
// Wait for 'end' event.
poll_fn(|cx| {
let r = Self::regs();
WAKER.register(cx.waker());
T::state().waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
compiler_fence(Ordering::SeqCst);
// END means the whole buffer has been received.
r.events_end.reset();
r.intenset.write(|w| w.end().set());
if discard > 0 {
discard -= 1;
} else {
// Note that the beginning of the buffer might be overwritten before the task fully stops :(
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
}
}
if r.events_stopped.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
ondrop.defuse();
compiler_fence(Ordering::SeqCst);
}
/// Continuous sampling with double buffers.
///
/// A TIMER and two PPI peripherals are passed in so that precise sampling
/// can be attained. The sampling interval is expressed by selecting a
/// timer clock frequency to use along with a counter threshold to be reached.
/// For example, 1KHz can be achieved using a frequency of 1MHz and a counter
/// threshold of 1000.
///
/// A sampler closure is provided that receives the buffer of samples, noting
/// that the size of this buffer can be less than the original buffer's size.
/// A command is return from the closure that indicates whether the sampling
@ -226,10 +258,14 @@ impl<'d> Pdm<'d> {
&mut self,
bufs: &mut [[i16; N]; 2],
mut sampler: S,
) where
) -> Result<(), Error> where
S: FnMut(&[i16; N]) -> SamplerState,
{
let r = Self::regs();
let r = T::regs();
if r.events_started.read().bits() != 0 {
return Err(Error::AlreadyRunning);
}
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
@ -255,7 +291,7 @@ impl<'d> Pdm<'d> {
let mut done = false;
let ondrop = OnDrop::new(|| {
let drop = OnDrop::new(|| {
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
// N.B. It would be better if this were async, but Drop only support sync code.
while r.events_stopped.read().bits() != 0 {}
@ -263,9 +299,9 @@ impl<'d> Pdm<'d> {
// Wait for events and complete when the sampler indicates it has had enough.
poll_fn(|cx| {
let r = Self::regs();
let r = T::regs();
WAKER.register(cx.waker());
T::state().waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
compiler_fence(Ordering::SeqCst);
@ -301,43 +337,130 @@ impl<'d> Pdm<'d> {
Poll::Pending
})
.await;
ondrop.defuse();
drop.defuse();
Ok(())
}
}
impl<'d> Drop for Pdm<'d> {
fn drop(&mut self) {
let r = Self::regs();
r.enable.write(|w| w.enable().disabled());
/// PDM microphone driver Config
pub struct Config {
/// Use stero or mono operation
pub operation_mode: OperationMode,
/// On which edge the left channel should be samples
pub edge: Edge,
/// Clock frequency
pub frequency: Frequency,
/// Clock ratio
pub ratio: Ratio,
/// Gain left in dB
pub gain_left: I7F1,
/// Gain right in dB
pub gain_right: I7F1,
}
impl Default for Config {
fn default() -> Self {
Self {
operation_mode: OperationMode::Mono,
edge: Edge::LeftFalling,
frequency: Frequency::DEFAULT,
ratio: Ratio::RATIO80,
gain_left: I7F1::ZERO,
gain_right: I7F1::ZERO,
}
}
}
#[derive(Clone, Copy, PartialEq)]
/// PDM operation mode.
#[derive(PartialEq)]
pub enum OperationMode {
/// Mono (1 channel)
Mono,
/// Stereo (2 channels)
Stereo,
}
impl From<OperationMode> for OPERATION_A {
fn from(mode: OperationMode) -> Self {
match mode {
OperationMode::Mono => OPERATION_A::MONO,
OperationMode::Stereo => OPERATION_A::STEREO,
}
}
}
/// PDM edge polarity
#[derive(PartialEq)]
pub enum Edge {
FallingEdge,
RisingEdge,
/// Left edge is rising
LeftRising,
/// Left edge is falling
LeftFalling,
}
impl From<Edge> for EDGE_A {
fn from(edge: Edge) -> Self {
match edge {
Edge::FallingEdge => EDGE_A::LEFTFALLING,
Edge::RisingEdge => EDGE_A::LEFTRISING,
Edge::LeftRising => EDGE_A::LEFT_RISING,
Edge::LeftFalling => EDGE_A::LEFT_FALLING,
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum Channels {
Stereo,
Mono,
impl<'d, T: Instance> Drop for Pdm<'d, T> {
fn drop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.enable.write(|w| w.enable().disabled());
r.psel.din.reset();
r.psel.clk.reset();
}
}
impl From<Channels> for OPERATION_A {
fn from(ch: Channels) -> Self {
match ch {
Channels::Stereo => OPERATION_A::STEREO,
Channels::Mono => OPERATION_A::MONO,
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
fn state() -> &'static State;
}
}
/// PDM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_pdm {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::pdm::sealed::Instance for peripherals::$type {
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::pdm::sealed::State {
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
&STATE
}
}
impl crate::pdm::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -6,18 +6,20 @@ use crate::{pac, Peripheral};
const DPPI_ENABLE_BIT: u32 = 0x8000_0000;
const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;
fn regs() -> &'static pac::dppic::RegisterBlock {
pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
unsafe { &*pac::DPPIC::ptr() }
}
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
/// Configure PPI channel to trigger `task` on `event`.
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task])
}
}
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task1, task2])
}
}
@ -25,10 +27,11 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
{
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
pub fn new_many_to_many(
ch: impl Peripheral<P = C> + 'd,
events: [Event; EVENT_COUNT],
tasks: [Task; TASK_COUNT],
events: [Event<'d>; EVENT_COUNT],
tasks: [Task<'d>; TASK_COUNT],
) -> Self {
into_ref!(ch);

View File

@ -1,6 +1,6 @@
#![macro_use]
//! HAL interface for the PPI and DPPI peripheral.
//! Programmable Peripheral Interconnect (PPI/DPPI) driver.
//!
//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability
//! between peripherals through their events and tasks. There are fixed PPI channels and fully
@ -15,24 +15,107 @@
//! many tasks and events, but any single task or event can only be coupled with one channel.
//!
use core::marker::PhantomData;
use core::ptr::NonNull;
use embassy_hal_common::{impl_peripheral, PeripheralRef};
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use crate::{peripherals, Peripheral};
#[cfg(feature = "_dppi")]
mod dppi;
#[cfg(feature = "_ppi")]
mod ppi;
#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
mod _version;
pub(crate) use _version::*;
/// An instance of the Programmable peripheral interconnect on nRF devices.
/// PPI channel driver.
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
ch: PeripheralRef<'d, C>,
#[cfg(feature = "_dppi")]
events: [Event; EVENT_COUNT],
events: [Event<'d>; EVENT_COUNT],
#[cfg(feature = "_dppi")]
tasks: [Task; TASK_COUNT],
tasks: [Task<'d>; TASK_COUNT],
}
/// PPI channel group driver.
pub struct PpiGroup<'d, G: Group> {
g: PeripheralRef<'d, G>,
}
impl<'d, G: Group> PpiGroup<'d, G> {
/// Create a new PPI group driver.
///
/// The group is initialized as containing no channels.
pub fn new(g: impl Peripheral<P = G> + 'd) -> Self {
into_ref!(g);
let r = regs();
let n = g.number();
r.chg[n].write(|w| unsafe { w.bits(0) });
Self { g }
}
/// Add a PPI channel to this group.
///
/// If the channel is already in the group, this is a no-op.
pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
&mut self,
ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
) {
let r = regs();
let ng = self.g.number();
let nc = ch.ch.number();
r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) });
}
/// Remove a PPI channel from this group.
///
/// If the channel is already not in the group, this is a no-op.
pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
&mut self,
ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
) {
let r = regs();
let ng = self.g.number();
let nc = ch.ch.number();
r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) });
}
/// Enable all the channels in this group.
pub fn enable_all(&mut self) {
let n = self.g.number();
regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) });
}
/// Disable all the channels in this group.
pub fn disable_all(&mut self) {
let n = self.g.number();
regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) });
}
/// Get a reference to the "enable all" task.
///
/// When triggered, it will enable all the channels in this group.
pub fn task_enable_all(&self) -> Task<'d> {
let n = self.g.number();
Task::from_reg(&regs().tasks_chg[n].en)
}
/// Get a reference to the "disable all" task.
///
/// When triggered, it will disable all the channels in this group.
pub fn task_disable_all(&self) -> Task<'d> {
let n = self.g.number();
Task::from_reg(&regs().tasks_chg[n].dis)
}
}
impl<'d, G: Group> Drop for PpiGroup<'d, G> {
fn drop(&mut self) {
let r = regs();
let n = self.g.number();
r.chg[n].write(|w| unsafe { w.bits(0) });
}
}
#[cfg(feature = "_dppi")]
@ -43,20 +126,28 @@ const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
/// a published event.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Task(NonNull<u32>);
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
impl Task {
impl<'d> Task<'d> {
/// Create a new `Task` from a task register pointer
///
/// # Safety
///
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
Self(ptr)
Self(ptr, PhantomData)
}
/// Triggers this task.
pub fn trigger(&mut self) {
unsafe { self.0.as_ptr().write_volatile(1) };
}
pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
Self(
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
PhantomData,
)
}
/// Address of subscription register for this task.
@ -69,26 +160,39 @@ impl Task {
/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Task {}
unsafe impl Send for Task<'_> {}
/// Represents an event that a peripheral can publish.
///
/// An event can be set to publish on a PPI channel when the event happens.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Event(NonNull<u32>);
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
impl Event {
impl<'d> Event<'d> {
/// Create a new `Event` from an event register pointer
///
/// # Safety
///
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
Self(ptr)
Self(ptr, PhantomData)
}
pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
Self(
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
PhantomData,
)
}
/// Describes whether this Event is currently in a triggered state.
pub fn is_triggered(&self) -> bool {
unsafe { self.0.as_ptr().read_volatile() == 1 }
}
/// Clear the current register's triggered state, reverting it to 0.
pub fn clear(&mut self) {
unsafe { self.0.as_ptr().write_volatile(0) };
}
/// Address of publish register for this event.
@ -101,7 +205,7 @@ impl Event {
/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Event {}
unsafe impl Send for Event<'_> {}
// ======================
// traits
@ -112,7 +216,7 @@ pub(crate) mod sealed {
}
/// Interface for PPI channels.
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized {
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized + 'static {
/// Returns the number of the channel
fn number(&self) -> usize;
}
@ -130,7 +234,7 @@ pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
}
/// Interface for a group of PPI channels.
pub trait Group: sealed::Group + Sized {
pub trait Group: sealed::Group + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
/// Returns the number of the group.
fn number(&self) -> usize;
/// Convert into a type erased group.
@ -248,6 +352,12 @@ macro_rules! impl_group {
$number
}
}
impl From<peripherals::$type> for crate::ppi::AnyGroup {
fn from(val: peripherals::$type) -> Self {
crate::ppi::Group::degrade(val)
}
}
};
}

View File

@ -3,18 +3,18 @@ use embassy_hal_common::into_ref;
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
use crate::{pac, Peripheral};
impl Task {
impl<'d> Task<'d> {
fn reg_val(&self) -> u32 {
self.0.as_ptr() as _
}
}
impl Event {
impl<'d> Event<'d> {
fn reg_val(&self) -> u32 {
self.0.as_ptr() as _
}
}
fn regs() -> &'static pac::ppi::RegisterBlock {
pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock {
unsafe { &*pac::PPI::ptr() }
}
@ -34,7 +34,7 @@ impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
into_ref!(ch);
let r = regs();
@ -48,8 +48,8 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
into_ref!(ch);
let r = regs();

View File

@ -1,3 +1,5 @@
//! Pulse Width Modulation (PWM) driver.
#![macro_use]
use core::sync::atomic::{compiler_fence, Ordering};
@ -6,10 +8,9 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::Interrupt;
use crate::ppi::{Event, Task};
use crate::util::slice_in_ram_or;
use crate::{pac, Peripheral};
use crate::{interrupt, pac, Peripheral};
/// SimplePwm is the traditional pwm interface you're probably used to, allowing
/// to simply set a duty cycle across up to four channels.
@ -32,6 +33,7 @@ pub struct SequencePwm<'d, T: Instance> {
ch3: Option<PeripheralRef<'d, AnyPin>>,
}
/// PWM error
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
@ -41,7 +43,7 @@ pub enum Error {
/// Min Sequence count is 1
SequenceTimesAtLeastOne,
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
DMABufferNotInDataMemory,
BufferNotInRAM,
}
const MAX_SEQUENCE_LEN: usize = 32767;
@ -179,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Stopped` event endpoint for PPI.
#[inline(always)]
pub fn event_stopped(&self) -> Event {
pub fn event_stopped(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_stopped)
@ -187,7 +189,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `LoopsDone` event endpoint for PPI.
#[inline(always)]
pub fn event_loops_done(&self) -> Event {
pub fn event_loops_done(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_loopsdone)
@ -195,7 +197,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
#[inline(always)]
pub fn event_pwm_period_end(&self) -> Event {
pub fn event_pwm_period_end(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_pwmperiodend)
@ -203,7 +205,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq0 End` event endpoint for PPI.
#[inline(always)]
pub fn event_seq_end(&self) -> Event {
pub fn event_seq_end(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqend[0])
@ -211,7 +213,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq1 End` event endpoint for PPI.
#[inline(always)]
pub fn event_seq1_end(&self) -> Event {
pub fn event_seq1_end(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqend[1])
@ -219,7 +221,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq0 Started` event endpoint for PPI.
#[inline(always)]
pub fn event_seq0_started(&self) -> Event {
pub fn event_seq0_started(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqstarted[0])
@ -227,7 +229,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq1 Started` event endpoint for PPI.
#[inline(always)]
pub fn event_seq1_started(&self) -> Event {
pub fn event_seq1_started(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqstarted[1])
@ -238,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_start_seq0(&self) -> Task {
pub unsafe fn task_start_seq0(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_seqstart[0])
@ -249,7 +251,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_start_seq1(&self) -> Task {
pub unsafe fn task_start_seq1(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_seqstart[1])
@ -260,7 +262,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_next_step(&self) -> Task {
pub unsafe fn task_next_step(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_nextstep)
@ -271,7 +273,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_stop(&self) -> Task {
pub unsafe fn task_stop(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_stop)
@ -358,6 +360,7 @@ pub struct Sequence<'s> {
}
impl<'s> Sequence<'s> {
/// Create a new `Sequence`
pub fn new(words: &'s [u16], config: SequenceConfig) -> Self {
Self { words, config }
}
@ -367,7 +370,7 @@ impl<'s> Sequence<'s> {
/// Takes at one sequence along with its configuration.
#[non_exhaustive]
pub struct SingleSequencer<'d, 's, T: Instance> {
pub sequencer: Sequencer<'d, 's, T>,
sequencer: Sequencer<'d, 's, T>,
}
impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> {
@ -428,8 +431,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> {
let sequence0 = &self.sequence0;
let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0);
slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?;
slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?;
slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?;
slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?;
if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN {
return Err(Error::SequenceTooLong);
@ -536,13 +539,21 @@ pub enum SequenceMode {
/// PWM Base clock is system clock (16MHz) divided by prescaler
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Prescaler {
/// Divide by 1
Div1,
/// Divide by 2
Div2,
/// Divide by 4
Div4,
/// Divide by 8
Div8,
/// Divide by 16
Div16,
/// Divide by 32
Div32,
/// Divide by 64
Div64,
/// Divide by 128
Div128,
}
@ -828,8 +839,10 @@ pub(crate) mod sealed {
}
}
/// PWM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
type Interrupt: Interrupt;
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_pwm {
@ -840,7 +853,7 @@ macro_rules! impl_pwm {
}
}
impl crate::pwm::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -1,28 +1,35 @@
//! Quadrature decoder interface
//! Quadrature decoder (QDEC) driver.
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::InterruptExt;
use crate::peripherals::QDEC;
use crate::{interrupt, pac, Peripheral};
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
/// Quadrature decoder
pub struct Qdec<'d> {
_p: PeripheralRef<'d, QDEC>,
/// Quadrature decoder driver.
pub struct Qdec<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
/// QDEC config
#[non_exhaustive]
pub struct Config {
/// Number of samples
pub num_samples: NumSamples,
/// Sample period
pub period: SamplePeriod,
/// Set LED output pin polarity
pub led_polarity: LedPolarity,
/// Enable/disable input debounce filters
pub debounce: bool,
/// Time period the LED is switched ON prior to sampling (0..511 us).
pub led_pre_usecs: u16,
}
@ -38,42 +45,52 @@ impl Default for Config {
}
}
static WAKER: AtomicWaker = AtomicWaker::new();
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<'d> Qdec<'d> {
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
T::regs().intenclr.write(|w| w.reportrdy().clear());
T::state().waker.wake();
}
}
impl<'d, T: Instance> Qdec<'d, T> {
/// Create a new QDEC.
pub fn new(
qdec: impl Peripheral<P = QDEC> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
qdec: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(a, b);
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config)
into_ref!(qdec, a, b);
Self::new_inner(qdec, a.map_into(), b.map_into(), None, config)
}
/// Create a new QDEC, with a pin for LED output.
pub fn new_with_led(
qdec: impl Peripheral<P = QDEC> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
qdec: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd,
led: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(a, b, led);
Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config)
into_ref!(qdec, a, b, led);
Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config)
}
fn new_inner(
p: impl Peripheral<P = QDEC> + 'd,
irq: impl Peripheral<P = interrupt::QDEC> + 'd,
p: PeripheralRef<'d, T>,
a: PeripheralRef<'d, AnyPin>,
b: PeripheralRef<'d, AnyPin>,
led: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
into_ref!(p, irq);
let r = Self::regs();
let r = T::regs();
// Select pins.
a.conf().write(|w| w.input().connect().pull().pullup());
@ -116,20 +133,15 @@ impl<'d> Qdec<'d> {
SamplePeriod::_131ms => w.sampleper()._131ms(),
});
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
// Enable peripheral
r.enable.write(|w| w.enable().set_bit());
// Start sampling
unsafe { r.tasks_start.write(|w| w.bits(1)) };
irq.disable();
irq.set_handler(|_| {
let r = Self::regs();
r.intenclr.write(|w| w.reportrdy().clear());
WAKER.wake();
});
irq.enable();
Self { _p: p }
}
@ -141,18 +153,27 @@ impl<'d> Qdec<'d> {
/// # Example
///
/// ```no_run
/// let irq = interrupt::take!(QDEC);
/// use embassy_nrf::qdec::{self, Qdec};
/// use embassy_nrf::{bind_interrupts, peripherals};
///
/// bind_interrupts!(struct Irqs {
/// QDEC => qdec::InterruptHandler<peripherals::QDEC>;
/// });
///
/// # async {
/// # let p: embassy_nrf::Peripherals = todo!();
/// let config = qdec::Config::default();
/// let mut q = Qdec::new(p.QDEC, p.P0_31, p.P0_30, config);
/// let mut q = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config);
/// let delta = q.read().await;
/// # };
/// ```
pub async fn read(&mut self) -> i16 {
let t = Self::regs();
let t = T::regs();
t.intenset.write(|w| w.reportrdy().set());
unsafe { t.tasks_readclracc.write(|w| w.bits(1)) };
let value = poll_fn(|cx| {
WAKER.register(cx.waker());
T::state().waker.register(cx.waker());
if t.events_reportrdy.read().bits() == 0 {
return Poll::Pending;
} else {
@ -164,42 +185,108 @@ impl<'d> Qdec<'d> {
.await;
value
}
fn regs() -> &'static pac::qdec::RegisterBlock {
unsafe { &*pac::QDEC::ptr() }
}
}
/// Sample period
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum SamplePeriod {
/// 128 us
_128us,
/// 256 us
_256us,
/// 512 us
_512us,
/// 1024 us
_1024us,
/// 2048 us
_2048us,
/// 4096 us
_4096us,
/// 8192 us
_8192us,
/// 16384 us
_16384us,
/// 32 ms
_32ms,
/// 65 ms
_65ms,
/// 131 ms
_131ms,
}
/// Number of samples taken.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum NumSamples {
/// 10 samples
_10smpl,
/// 40 samples
_40smpl,
/// 80 samples
_80smpl,
/// 120 samples
_120smpl,
/// 160 samples
_160smpl,
/// 200 samples
_200smpl,
/// 240 samples
_240smpl,
/// 280 samples
_280smpl,
/// 1 sample
_1smpl,
}
/// LED polarity
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum LedPolarity {
/// Active high (a high output turns on the LED).
ActiveHigh,
/// Active low (a low output turns on the LED).
ActiveLow,
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::qdec::RegisterBlock;
fn state() -> &'static State;
}
}
/// qdec peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_qdec {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::qdec::sealed::Instance for peripherals::$type {
fn regs() -> &'static crate::pac::qdec::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::qdec::sealed::State {
static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new();
&STATE
}
}
impl crate::qdec::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -1,20 +1,25 @@
//! Quad Serial Peripheral Interface (QSPI) flash driver.
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::ptr;
use core::task::Poll;
use embassy_hal_common::drop::DropBomb;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn;
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
use crate::gpio::{self, Pin as GpioPin};
use crate::interrupt::{Interrupt, InterruptExt};
use crate::interrupt::typelevel::Interrupt;
pub use crate::pac::qspi::ifconfig0::{
ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode,
};
pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode;
use crate::{pac, Peripheral};
use crate::{interrupt, Peripheral};
/// Deep power-down config.
pub struct DeepPowerDownConfig {
/// Time required for entering DPM, in units of 16us
pub enter_time: u16,
@ -22,38 +27,65 @@ pub struct DeepPowerDownConfig {
pub exit_time: u16,
}
/// QSPI bus frequency.
pub enum Frequency {
/// 32 Mhz
M32 = 0,
/// 16 Mhz
M16 = 1,
/// 10.7 Mhz
M10_7 = 2,
/// 8 Mhz
M8 = 3,
/// 6.4 Mhz
M6_4 = 4,
/// 5.3 Mhz
M5_3 = 5,
/// 4.6 Mhz
M4_6 = 6,
/// 4 Mhz
M4 = 7,
/// 3.6 Mhz
M3_6 = 8,
/// 3.2 Mhz
M3_2 = 9,
/// 2.9 Mhz
M2_9 = 10,
/// 2.7 Mhz
M2_7 = 11,
/// 2.5 Mhz
M2_5 = 12,
/// 2.3 Mhz
M2_3 = 13,
/// 2.1 Mhz
M2_1 = 14,
/// 2 Mhz
M2 = 15,
}
/// QSPI config.
#[non_exhaustive]
pub struct Config {
/// XIP offset.
pub xip_offset: u32,
/// Opcode used for read operations.
pub read_opcode: ReadOpcode,
/// Opcode used for write operations.
pub write_opcode: WriteOpcode,
/// Page size for write operations.
pub write_page_size: WritePageSize,
/// Configuration for deep power down. If None, deep power down is disabled.
pub deep_power_down: Option<DeepPowerDownConfig>,
/// QSPI bus frequency.
pub frequency: Frequency,
/// Value is specified in number of 16 MHz periods (62.5 ns)
pub sck_delay: u8,
/// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3)
pub spi_mode: SpiMode,
/// Addressing mode (24-bit or 32-bit)
pub address_mode: AddressMode,
/// Flash memory capacity in bytes. This is the value reported by the `embedded-storage` traits.
pub capacity: u32,
}
impl Default for Config {
@ -68,27 +100,50 @@ impl Default for Config {
sck_delay: 80,
spi_mode: SpiMode::MODE0,
address_mode: AddressMode::_24BIT,
capacity: 0,
}
}
}
/// Error
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Operation address was out of bounds.
OutOfBounds,
// TODO add "not in data memory" error and check for it
}
pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> {
irq: PeripheralRef<'d, T::Interrupt>,
dpm_enabled: bool,
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_ready.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.ready().clear());
}
}
}
/// QSPI flash driver.
pub struct Qspi<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
dpm_enabled: bool,
capacity: u32,
}
impl<'d, T: Instance> Qspi<'d, T> {
/// Create a new QSPI driver.
pub fn new(
_qspi: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
qspi: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
csn: impl Peripheral<P = impl GpioPin> + 'd,
io0: impl Peripheral<P = impl GpioPin> + 'd,
@ -96,30 +151,31 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
io2: impl Peripheral<P = impl GpioPin> + 'd,
io3: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Qspi<'d, T, FLASH_SIZE> {
into_ref!(irq, sck, csn, io0, io1, io2, io3);
) -> Self {
into_ref!(qspi, sck, csn, io0, io1, io2, io3);
let r = T::regs();
sck.set_high();
csn.set_high();
io0.set_high();
io1.set_high();
io2.set_high();
io3.set_high();
sck.conf().write(|w| w.dir().output().drive().h0h1());
csn.conf().write(|w| w.dir().output().drive().h0h1());
io0.conf().write(|w| w.dir().output().drive().h0h1());
io1.conf().write(|w| w.dir().output().drive().h0h1());
io2.conf().write(|w| w.dir().output().drive().h0h1());
io3.conf().write(|w| w.dir().output().drive().h0h1());
macro_rules! config_pin {
($pin:ident) => {
$pin.set_high();
$pin.conf().write(|w| {
w.dir().output();
w.drive().h0h1();
#[cfg(feature = "_nrf5340-s")]
w.mcusel().peripheral();
w
});
r.psel.$pin.write(|w| unsafe { w.bits($pin.psel_bits()) });
};
}
r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
r.psel.csn.write(|w| unsafe { w.bits(csn.psel_bits()) });
r.psel.io0.write(|w| unsafe { w.bits(io0.psel_bits()) });
r.psel.io1.write(|w| unsafe { w.bits(io1.psel_bits()) });
r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) });
r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) });
config_pin!(sck);
config_pin!(csn);
config_pin!(io0);
config_pin!(io1);
config_pin!(io2);
config_pin!(io3);
r.ifconfig0.write(|w| {
w.addrmode().variant(config.address_mode);
@ -151,16 +207,16 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
w
});
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
// Enable it
r.enable.write(|w| w.enable().enabled());
let mut res = Self {
let res = Self {
_peri: qspi,
dpm_enabled: config.deep_power_down.is_some(),
irq,
capacity: config.capacity,
};
r.events_ready.reset();
@ -168,23 +224,14 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
r.tasks_activate.write(|w| w.tasks_activate().bit(true));
res.blocking_wait_ready();
Self::blocking_wait_ready();
res
}
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let s = T::state();
if r.events_ready.read().bits() != 0 {
s.ready_waker.wake();
r.intenclr.write(|w| w.ready().clear());
}
}
/// Do a custom QSPI instruction.
pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> {
let bomb = DropBomb::new();
let ondrop = OnDrop::new(Self::blocking_wait_ready);
let len = core::cmp::max(req.len(), resp.len()) as u8;
self.custom_instruction_start(opcode, req, len)?;
@ -193,16 +240,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
self.custom_instruction_finish(resp)?;
bomb.defuse();
ondrop.defuse();
Ok(())
}
/// Do a custom QSPI instruction, blocking version.
pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> {
let len = core::cmp::max(req.len(), resp.len()) as u8;
self.custom_instruction_start(opcode, req, len)?;
self.blocking_wait_ready();
Self::blocking_wait_ready();
self.custom_instruction_finish(resp)?;
@ -269,7 +317,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.ready_waker.register(cx.waker());
s.waker.register(cx.waker());
if r.events_ready.read().bits() != 0 {
return Poll::Ready(());
}
@ -278,7 +326,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
.await
}
fn blocking_wait_ready(&mut self) {
fn blocking_wait_ready() {
loop {
let r = T::regs();
if r.events_ready.read().bits() != 0 {
@ -287,17 +335,15 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
}
}
fn start_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
// TODO: Return these as errors instead.
assert_eq!(data.as_ptr() as u32 % 4, 0);
assert_eq!(data.len() as u32 % 4, 0);
assert_eq!(address as u32 % 4, 0);
if address > FLASH_SIZE {
return Err(Error::OutOfBounds);
}
assert_eq!(address % 4, 0);
let r = T::regs();
r.read.src.write(|w| unsafe { w.src().bits(address as u32) });
r.read.src.write(|w| unsafe { w.src().bits(address) });
r.read.dst.write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) });
r.read.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) });
@ -308,18 +354,15 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
fn start_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
// TODO: Return these as errors instead.
assert_eq!(data.as_ptr() as u32 % 4, 0);
assert_eq!(data.len() as u32 % 4, 0);
assert_eq!(address as u32 % 4, 0);
if address > FLASH_SIZE {
return Err(Error::OutOfBounds);
}
assert_eq!(address % 4, 0);
let r = T::regs();
r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) });
r.write.dst.write(|w| unsafe { w.dst().bits(address as u32) });
r.write.dst.write(|w| unsafe { w.dst().bits(address) });
r.write.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) });
r.events_ready.reset();
@ -329,14 +372,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
fn start_erase(&mut self, address: usize) -> Result<(), Error> {
assert_eq!(address as u32 % 4096, 0);
if address > FLASH_SIZE {
return Err(Error::OutOfBounds);
}
fn start_erase(&mut self, address: u32) -> Result<(), Error> {
// TODO: Return these as errors instead.
assert_eq!(address % 4096, 0);
let r = T::regs();
r.erase.ptr.write(|w| unsafe { w.ptr().bits(address as u32) });
r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) });
r.erase.len.write(|w| w.len()._4kb());
r.events_ready.reset();
@ -346,59 +387,122 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
let bomb = DropBomb::new();
/// Raw QSPI read.
///
/// The difference with `read` is that this does not do bounds checks
/// against the flash capacity. It is intended for use when QSPI is used as
/// a raw bus, not with flash memory.
pub async fn read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
let ondrop = OnDrop::new(Self::blocking_wait_ready);
self.start_read(address, data)?;
self.wait_ready().await;
bomb.defuse();
ondrop.defuse();
Ok(())
}
pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
let bomb = DropBomb::new();
/// Raw QSPI write.
///
/// The difference with `write` is that this does not do bounds checks
/// against the flash capacity. It is intended for use when QSPI is used as
/// a raw bus, not with flash memory.
pub async fn write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
let ondrop = OnDrop::new(Self::blocking_wait_ready);
self.start_write(address, data)?;
self.wait_ready().await;
bomb.defuse();
ondrop.defuse();
Ok(())
}
pub async fn erase(&mut self, address: usize) -> Result<(), Error> {
let bomb = DropBomb::new();
self.start_erase(address)?;
self.wait_ready().await;
bomb.defuse();
Ok(())
}
pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
/// Raw QSPI read, blocking version.
///
/// The difference with `blocking_read` is that this does not do bounds checks
/// against the flash capacity. It is intended for use when QSPI is used as
/// a raw bus, not with flash memory.
pub fn blocking_read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
self.start_read(address, data)?;
self.blocking_wait_ready();
Self::blocking_wait_ready();
Ok(())
}
pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
/// Raw QSPI write, blocking version.
///
/// The difference with `blocking_write` is that this does not do bounds checks
/// against the flash capacity. It is intended for use when QSPI is used as
/// a raw bus, not with flash memory.
pub fn blocking_write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
self.start_write(address, data)?;
self.blocking_wait_ready();
Self::blocking_wait_ready();
Ok(())
}
pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> {
/// Read data from the flash memory.
pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
self.bounds_check(address, data.len())?;
self.read_raw(address, data).await
}
/// Write data to the flash memory.
pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
self.bounds_check(address, data.len())?;
self.write_raw(address, data).await
}
/// Erase a sector on the flash memory.
pub async fn erase(&mut self, address: u32) -> Result<(), Error> {
if address >= self.capacity {
return Err(Error::OutOfBounds);
}
let ondrop = OnDrop::new(Self::blocking_wait_ready);
self.start_erase(address)?;
self.blocking_wait_ready();
self.wait_ready().await;
ondrop.defuse();
Ok(())
}
/// Read data from the flash memory, blocking version.
pub fn blocking_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
self.bounds_check(address, data.len())?;
self.blocking_read_raw(address, data)
}
/// Write data to the flash memory, blocking version.
pub fn blocking_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
self.bounds_check(address, data.len())?;
self.blocking_write_raw(address, data)
}
/// Erase a sector on the flash memory, blocking version.
pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> {
if address >= self.capacity {
return Err(Error::OutOfBounds);
}
self.start_erase(address)?;
Self::blocking_wait_ready();
Ok(())
}
fn bounds_check(&self, address: u32, len: usize) -> Result<(), Error> {
let len_u32: u32 = len.try_into().map_err(|_| Error::OutOfBounds)?;
let end_address = address.checked_add(len_u32).ok_or(Error::OutOfBounds)?;
if end_address > self.capacity {
return Err(Error::OutOfBounds);
}
Ok(())
}
}
impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> {
impl<'d, T: Instance> Drop for Qspi<'d, T> {
fn drop(&mut self) {
let r = T::regs();
@ -428,8 +532,6 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE>
r.enable.write(|w| w.enable().disabled());
self.irq.disable();
// Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN,
// leaving it floating, the flash chip might read it as zero which would cause it to
// spuriously exit DPM.
@ -443,9 +545,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE>
}
}
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Qspi<'d, T, FLASH_SIZE> {
impl<'d, T: Instance> ErrorType for Qspi<'d, T> {
type Error = Error;
}
@ -455,72 +555,66 @@ impl NorFlashError for Error {
}
}
impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> {
impl<'d, T: Instance> ReadNorFlash for Qspi<'d, T> {
const READ_SIZE: usize = 4;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(offset as usize, bytes)?;
self.blocking_read(offset, bytes)?;
Ok(())
}
fn capacity(&self) -> usize {
FLASH_SIZE
self.capacity as usize
}
}
impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SIZE> {
impl<'d, T: Instance> NorFlash for Qspi<'d, T> {
const WRITE_SIZE: usize = 4;
const ERASE_SIZE: usize = 4096;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
for address in (from as usize..to as usize).step_by(<Self as NorFlash>::ERASE_SIZE) {
for address in (from..to).step_by(<Self as NorFlash>::ERASE_SIZE) {
self.blocking_erase(address)?;
}
Ok(())
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(offset as usize, bytes)?;
self.blocking_write(offset, bytes)?;
Ok(())
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "nightly")]
{
use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash};
use core::future::Future;
#[cfg(feature = "nightly")]
mod _eh1 {
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> {
const WRITE_SIZE: usize = <Self as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Self as NorFlash>::ERASE_SIZE;
use super::*;
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
async move { self.write(offset as usize, data).await }
}
impl<'d, T: Instance> AsyncNorFlash for Qspi<'d, T> {
const WRITE_SIZE: usize = <Self as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Self as NorFlash>::ERASE_SIZE;
type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
async move {
for address in (from as usize..to as usize).step_by(<Self as AsyncNorFlash>::ERASE_SIZE) {
self.erase(address).await?
}
Ok(())
}
}
async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
self.write(offset, data).await
}
impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> {
const READ_SIZE: usize = 4;
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
async move { self.read(address as usize, data).await }
async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
for address in (from..to).step_by(<Self as AsyncNorFlash>::ERASE_SIZE) {
self.erase(address).await?
}
Ok(())
}
}
fn capacity(&self) -> usize {
FLASH_SIZE
}
impl<'d, T: Instance> AsyncReadNorFlash for Qspi<'d, T> {
const READ_SIZE: usize = 4;
async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.read(address, data).await
}
fn capacity(&self) -> usize {
self.capacity as usize
}
}
}
@ -528,33 +622,35 @@ cfg_if::cfg_if! {
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
/// Peripheral static state
pub struct State {
pub ready_waker: AtomicWaker,
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
ready_waker: AtomicWaker::new(),
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static pac::qspi::RegisterBlock;
fn regs() -> &'static crate::pac::qspi::RegisterBlock;
fn state() -> &'static State;
}
}
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
type Interrupt: Interrupt;
/// QSPI peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_qspi {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::qspi::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::qspi::RegisterBlock {
fn regs() -> &'static crate::pac::qspi::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::qspi::sealed::State {
@ -563,7 +659,7 @@ macro_rules! impl_qspi {
}
}
impl crate::qspi::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -1,3 +1,9 @@
//! Random Number Generator (RNG) driver.
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use core::task::Poll;
@ -5,77 +11,37 @@ use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::interrupt::InterruptExt;
use crate::peripherals::RNG;
use crate::{interrupt, pac, Peripheral};
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
impl RNG {
fn regs() -> &'static pac::rng::RegisterBlock {
unsafe { &*pac::RNG::ptr() }
}
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
static STATE: State = State {
ptr: AtomicPtr::new(ptr::null_mut()),
end: AtomicPtr::new(ptr::null_mut()),
waker: AtomicWaker::new(),
};
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let s = T::state();
let r = T::regs();
struct State {
ptr: AtomicPtr<u8>,
end: AtomicPtr<u8>,
waker: AtomicWaker,
}
/// A wrapper around an nRF RNG peripheral.
///
/// It has a non-blocking API, and a blocking api through `rand`.
pub struct Rng<'d> {
irq: PeripheralRef<'d, interrupt::RNG>,
}
impl<'d> Rng<'d> {
/// Creates a new RNG driver from the `RNG` peripheral and interrupt.
///
/// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,
/// e.g. using `mem::forget`.
///
/// The synchronous API is safe.
pub fn new(_rng: impl Peripheral<P = RNG> + 'd, irq: impl Peripheral<P = interrupt::RNG> + 'd) -> Self {
into_ref!(irq);
let this = Self { irq };
this.stop();
this.disable_irq();
this.irq.set_handler(Self::on_interrupt);
this.irq.unpend();
this.irq.enable();
this
}
fn on_interrupt(_: *mut ()) {
// Clear the event.
RNG::regs().events_valrdy.reset();
r.events_valrdy.reset();
// Mutate the slice within a critical section,
// so that the future isn't dropped in between us loading the pointer and actually dereferencing it.
let (ptr, end) = critical_section::with(|_| {
let ptr = STATE.ptr.load(Ordering::Relaxed);
let ptr = s.ptr.load(Ordering::Relaxed);
// We need to make sure we haven't already filled the whole slice,
// in case the interrupt fired again before the executor got back to the future.
let end = STATE.end.load(Ordering::Relaxed);
let end = s.end.load(Ordering::Relaxed);
if !ptr.is_null() && ptr != end {
// If the future was dropped, the pointer would have been set to null,
// so we're still good to mutate the slice.
// The safety contract of `Rng::new` means that the future can't have been dropped
// without calling its destructor.
unsafe {
*ptr = RNG::regs().value.read().value().bits();
*ptr = r.value.read().value().bits();
}
}
(ptr, end)
@ -88,15 +54,15 @@ impl<'d> Rng<'d> {
}
let new_ptr = unsafe { ptr.add(1) };
match STATE
match s
.ptr
.compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed)
{
Ok(_) => {
let end = STATE.end.load(Ordering::Relaxed);
let end = s.end.load(Ordering::Relaxed);
// It doesn't matter if `end` was changed under our feet, because then this will just be false.
if new_ptr == end {
STATE.waker.wake();
s.waker.wake();
}
}
Err(_) => {
@ -105,21 +71,53 @@ impl<'d> Rng<'d> {
}
}
}
}
/// A wrapper around an nRF RNG peripheral.
///
/// It has a non-blocking API, and a blocking api through `rand`.
pub struct Rng<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Rng<'d, T> {
/// Creates a new RNG driver from the `RNG` peripheral and interrupt.
///
/// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,
/// e.g. using `mem::forget`.
///
/// The synchronous API is safe.
pub fn new(
rng: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
into_ref!(rng);
let this = Self { _peri: rng };
this.stop();
this.disable_irq();
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
this
}
fn stop(&self) {
RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) })
T::regs().tasks_stop.write(|w| unsafe { w.bits(1) })
}
fn start(&self) {
RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) })
T::regs().tasks_start.write(|w| unsafe { w.bits(1) })
}
fn enable_irq(&self) {
RNG::regs().intenset.write(|w| w.valrdy().set());
T::regs().intenset.write(|w| w.valrdy().set());
}
fn disable_irq(&self) {
RNG::regs().intenclr.write(|w| w.valrdy().clear());
T::regs().intenclr.write(|w| w.valrdy().clear());
}
/// Enable or disable the RNG's bias correction.
@ -128,20 +126,23 @@ impl<'d> Rng<'d> {
/// However, this makes the generation of numbers slower.
///
/// Defaults to disabled.
pub fn bias_correction(&self, enable: bool) {
RNG::regs().config.write(|w| w.dercen().bit(enable))
pub fn set_bias_correction(&self, enable: bool) {
T::regs().config.write(|w| w.dercen().bit(enable))
}
/// Fill the buffer with random bytes.
pub async fn fill_bytes(&mut self, dest: &mut [u8]) {
if dest.len() == 0 {
return; // Nothing to fill
}
let s = T::state();
let range = dest.as_mut_ptr_range();
// Even if we've preempted the interrupt, it can't preempt us again,
// so we don't need to worry about the order we write these in.
STATE.ptr.store(range.start, Ordering::Relaxed);
STATE.end.store(range.end, Ordering::Relaxed);
s.ptr.store(range.start, Ordering::Relaxed);
s.end.store(range.end, Ordering::Relaxed);
self.enable_irq();
self.start();
@ -151,16 +152,16 @@ impl<'d> Rng<'d> {
self.disable_irq();
// The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here.
STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed);
STATE.end.store(ptr::null_mut(), Ordering::Relaxed);
s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
s.end.store(ptr::null_mut(), Ordering::Relaxed);
});
poll_fn(|cx| {
STATE.waker.register(cx.waker());
s.waker.register(cx.waker());
// The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`.
let end = STATE.end.load(Ordering::Relaxed);
let ptr = STATE.ptr.load(Ordering::Relaxed);
let end = s.end.load(Ordering::Relaxed);
let ptr = s.ptr.load(Ordering::Relaxed);
if ptr == end {
// We're done.
@ -175,11 +176,12 @@ impl<'d> Rng<'d> {
drop(on_drop);
}
/// Fill the buffer with random bytes, blocking version.
pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {
self.start();
for byte in dest.iter_mut() {
let regs = RNG::regs();
let regs = T::regs();
while regs.events_valrdy.read().bits() == 0 {}
regs.events_valrdy.reset();
*byte = regs.value.read().value().bits();
@ -189,13 +191,16 @@ impl<'d> Rng<'d> {
}
}
impl<'d> Drop for Rng<'d> {
impl<'d, T: Instance> Drop for Rng<'d, T> {
fn drop(&mut self) {
self.irq.disable()
self.stop();
let s = T::state();
s.ptr.store(ptr::null_mut(), Ordering::Relaxed);
s.end.store(ptr::null_mut(), Ordering::Relaxed);
}
}
impl<'d> rand_core::RngCore for Rng<'d> {
impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> {
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.blocking_fill_bytes(dest);
}
@ -219,4 +224,53 @@ impl<'d> rand_core::RngCore for Rng<'d> {
}
}
impl<'d> rand_core::CryptoRng for Rng<'d> {}
impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {}
pub(crate) mod sealed {
use super::*;
/// Peripheral static state
pub struct State {
pub ptr: AtomicPtr<u8>,
pub end: AtomicPtr<u8>,
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
ptr: AtomicPtr::new(ptr::null_mut()),
end: AtomicPtr::new(ptr::null_mut()),
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::rng::RegisterBlock;
fn state() -> &'static State;
}
}
/// RNG peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_rng {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::rng::sealed::Instance for peripherals::$type {
fn regs() -> &'static crate::pac::rng::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::rng::sealed::State {
static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new();
&STATE
}
}
impl crate::rng::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -1,11 +1,14 @@
//! Successive Approximation Analog-to-Digital Converter (SAADC) driver.
#![macro_use]
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use pac::{saadc, SAADC};
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
// We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same
@ -19,14 +22,36 @@ use crate::ppi::{ConfigurableChannel, Event, Ppi, Task};
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
use crate::{interrupt, pac, peripherals, Peripheral};
/// SAADC error
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {}
/// One-shot and continuous SAADC.
pub struct Saadc<'d, const N: usize> {
_p: PeripheralRef<'d, peripherals::SAADC>,
/// Interrupt handler.
pub struct InterruptHandler {
_private: (),
}
impl interrupt::typelevel::Handler<interrupt::typelevel::SAADC> for InterruptHandler {
unsafe fn on_interrupt() {
let r = unsafe { &*SAADC::ptr() };
if r.events_calibratedone.read().bits() != 0 {
r.intenclr.write(|w| w.calibratedone().clear());
WAKER.wake();
}
if r.events_end.read().bits() != 0 {
r.intenclr.write(|w| w.end().clear());
WAKER.wake();
}
if r.events_started.read().bits() != 0 {
r.intenclr.write(|w| w.started().clear());
WAKER.wake();
}
}
}
static WAKER: AtomicWaker = AtomicWaker::new();
@ -101,24 +126,29 @@ impl<'d> ChannelConfig<'d> {
}
}
/// The state of a continuously running sampler. While it reflects
/// the progress of a sampler, it also signals what should be done
/// next. For example, if the sampler has stopped then the Saadc implementation
/// can then tear down its infrastructure.
/// Value returned by the SAADC callback, deciding what happens next.
#[derive(PartialEq)]
pub enum SamplerState {
Sampled,
Stopped,
pub enum CallbackResult {
/// The SAADC should keep sampling and calling the callback.
Continue,
/// The SAADC should stop sampling, and return.
Stop,
}
/// One-shot and continuous SAADC.
pub struct Saadc<'d, const N: usize> {
_p: PeripheralRef<'d, peripherals::SAADC>,
}
impl<'d, const N: usize> Saadc<'d, N> {
/// Create a new SAADC driver.
pub fn new(
saadc: impl Peripheral<P = peripherals::SAADC> + 'd,
irq: impl Peripheral<P = interrupt::SAADC> + 'd,
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SAADC, InterruptHandler> + 'd,
config: Config,
channel_configs: [ChannelConfig; N],
) -> Self {
into_ref!(saadc, irq);
into_ref!(saadc);
let r = unsafe { &*SAADC::ptr() };
@ -159,32 +189,12 @@ impl<'d, const N: usize> Saadc<'d, N> {
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
interrupt::SAADC.unpend();
unsafe { interrupt::SAADC.enable() };
Self { _p: saadc }
}
fn on_interrupt(_ctx: *mut ()) {
let r = Self::regs();
if r.events_calibratedone.read().bits() != 0 {
r.intenclr.write(|w| w.calibratedone().clear());
WAKER.wake();
}
if r.events_end.read().bits() != 0 {
r.intenclr.write(|w| w.end().clear());
WAKER.wake();
}
if r.events_started.read().bits() != 0 {
r.intenclr.write(|w| w.started().clear());
WAKER.wake();
}
}
fn regs() -> &'static saadc::RegisterBlock {
unsafe { &*SAADC::ptr() }
}
@ -219,7 +229,13 @@ impl<'d, const N: usize> Saadc<'d, N> {
}
/// One shot sampling. The buffer must be the same size as the number of channels configured.
/// The sampling is stopped prior to returning in order to reduce power consumption (power
/// consumption remains higher if sampling is not stopped explicitly). Cancellation will
/// also cause the sampling to be stopped.
pub async fn sample(&mut self, buf: &mut [i16; N]) {
// In case the future is dropped, stop the task and wait for it to end.
let on_drop = OnDrop::new(Self::stop_sampling_immediately);
let r = Self::regs();
// Set up the DMA
@ -251,6 +267,8 @@ impl<'d, const N: usize> Saadc<'d, N> {
Poll::Pending
})
.await;
drop(on_drop);
}
/// Continuous sampling with double buffers.
@ -270,7 +288,13 @@ impl<'d, const N: usize> Saadc<'d, N> {
/// taken to acquire the samples into a single buffer. You should measure the
/// time taken by the callback and set the sample buffer size accordingly.
/// Exceeding this time can lead to samples becoming dropped.
pub async fn run_task_sampler<S, T: TimerInstance, const N0: usize>(
///
/// The sampling is stopped prior to returning in order to reduce power consumption (power
/// consumption remains higher if sampling is not stopped explicitly), and to
/// free the buffers from being used by the peripheral. Cancellation will
/// also cause the sampling to be stopped.
pub async fn run_task_sampler<F, T: TimerInstance, const N0: usize>(
&mut self,
timer: &mut T,
ppi_ch1: &mut impl ConfigurableChannel,
@ -278,9 +302,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
frequency: Frequency,
sample_counter: u32,
bufs: &mut [[[i16; N]; N0]; 2],
sampler: S,
callback: F,
) where
S: FnMut(&[[i16; N]]) -> SamplerState,
F: FnMut(&[[i16; N]]) -> CallbackResult,
{
let r = Self::regs();
@ -291,12 +315,14 @@ impl<'d, const N: usize> Saadc<'d, N> {
Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_end), Task::from_reg(&r.tasks_start));
start_ppi.enable();
let mut timer = Timer::new(timer);
let timer = Timer::new(timer);
timer.set_frequency(frequency);
timer.cc(0).write(sample_counter);
timer.cc(0).short_compare_clear();
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer.cc(0).event_compare(), Task::from_reg(&r.tasks_sample));
let timer_cc = timer.cc(0);
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample));
timer.start();
@ -306,21 +332,24 @@ impl<'d, const N: usize> Saadc<'d, N> {
|| {
sample_ppi.enable();
},
sampler,
callback,
)
.await;
}
async fn run_sampler<I, S, const N0: usize>(
async fn run_sampler<I, F, const N0: usize>(
&mut self,
bufs: &mut [[[i16; N]; N0]; 2],
sample_rate_divisor: Option<u16>,
mut init: I,
mut sampler: S,
mut callback: F,
) where
I: FnMut(),
S: FnMut(&[[i16; N]]) -> SamplerState,
F: FnMut(&[[i16; N]]) -> CallbackResult,
{
// In case the future is dropped, stop the task and wait for it to end.
let on_drop = OnDrop::new(Self::stop_sampling_immediately);
let r = Self::regs();
// Establish mode and sample rate
@ -366,7 +395,7 @@ impl<'d, const N: usize> Saadc<'d, N> {
let mut current_buffer = 0;
// Wait for events and complete when the sampler indicates it has had enough.
poll_fn(|cx| {
let r = poll_fn(|cx| {
let r = Self::regs();
WAKER.register(cx.waker());
@ -377,12 +406,15 @@ impl<'d, const N: usize> Saadc<'d, N> {
r.events_end.reset();
r.intenset.write(|w| w.end().set());
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
let next_buffer = 1 - current_buffer;
current_buffer = next_buffer;
} else {
return Poll::Ready(());
};
match callback(&bufs[current_buffer]) {
CallbackResult::Continue => {
let next_buffer = 1 - current_buffer;
current_buffer = next_buffer;
}
CallbackResult::Stop => {
return Poll::Ready(());
}
}
}
if r.events_started.read().bits() != 0 {
@ -403,6 +435,23 @@ impl<'d, const N: usize> Saadc<'d, N> {
Poll::Pending
})
.await;
drop(on_drop);
r
}
// Stop sampling and wait for it to stop in a blocking fashion
fn stop_sampling_immediately() {
let r = Self::regs();
compiler_fence(Ordering::SeqCst);
r.events_stopped.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
while r.events_stopped.read().bits() == 0 {}
r.events_stopped.reset();
}
}
@ -423,7 +472,7 @@ impl<'d> Saadc<'d, 1> {
sample_rate_divisor: u16,
sampler: S,
) where
S: FnMut(&[[i16; 1]]) -> SamplerState,
S: FnMut(&[[i16; 1]]) -> CallbackResult,
{
self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await;
}
@ -623,6 +672,10 @@ pub(crate) mod sealed {
/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.
pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized + 'static {
/// Convert this SAADC input to a type-erased `AnyInput`.
///
/// This allows using several inputs in situations that might require
/// them to be the same type, like putting them in an array.
fn degrade_saadc(self) -> AnyInput {
AnyInput {
channel: self.channel(),
@ -630,6 +683,10 @@ pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized +
}
}
/// A type-erased SAADC input.
///
/// This allows using several inputs in situations that might require
/// them to be the same type, like putting them in an array.
pub struct AnyInput {
channel: InputChannel,
}

View File

@ -1,42 +1,50 @@
//! Serial Peripheral Instance in master mode (SPIM) driver.
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use futures::future::poll_fn;
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::{Interrupt, InterruptExt};
use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{pac, Peripheral};
use crate::{interrupt, pac, Peripheral};
/// SPIM error
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// TX buffer was too long.
TxBufferTooLong,
/// RX buffer was too long.
RxBufferTooLong,
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
DMABufferNotInDataMemory,
}
/// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
pub struct Spim<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
BufferNotInRAM,
}
/// SPIM configuration.
#[non_exhaustive]
pub struct Config {
/// Frequency
pub frequency: Frequency,
/// SPI mode
pub mode: Mode,
/// Overread character.
///
/// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer,
/// this byte will be transmitted in the MOSI line for the left-over bytes.
pub orc: u8,
}
@ -50,10 +58,33 @@ impl Default for Config {
}
}
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_end.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.end().clear());
}
}
}
/// SPIM driver.
pub struct Spim<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Spim<'d, T> {
/// Create a new SPIM driver.
pub fn new(
spim: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd,
@ -62,7 +93,6 @@ impl<'d, T: Instance> Spim<'d, T> {
into_ref!(sck, miso, mosi);
Self::new_inner(
spim,
irq,
sck.map_into(),
Some(miso.map_into()),
Some(mosi.map_into()),
@ -70,37 +100,38 @@ impl<'d, T: Instance> Spim<'d, T> {
)
}
/// Create a new SPIM driver, capable of TX only (MOSI only).
pub fn new_txonly(
spim: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(sck, mosi);
Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config)
Self::new_inner(spim, sck.map_into(), None, Some(mosi.map_into()), config)
}
/// Create a new SPIM driver, capable of RX only (MISO only).
pub fn new_rxonly(
spim: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(sck, miso);
Self::new_inner(spim, irq, sck.map_into(), Some(miso.map_into()), None, config)
Self::new_inner(spim, sck.map_into(), Some(miso.map_into()), None, config)
}
fn new_inner(
spim: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
sck: PeripheralRef<'d, AnyPin>,
miso: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
into_ref!(spim, irq);
into_ref!(spim);
let r = T::regs();
@ -176,25 +207,14 @@ impl<'d, T: Instance> Spim<'d, T> {
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: spim }
}
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let s = T::state();
if r.events_end.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.end().clear());
}
}
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?;
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
// NOTE: RAM slice check for rx is not necessary, as a mutable
// slice can only be built from data located in RAM.
@ -236,7 +256,7 @@ impl<'d, T: Instance> Spim<'d, T> {
fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
match self.blocking_inner_from_ram(rx, tx) {
Ok(_) => Ok(()),
Err(Error::DMABufferNotInDataMemory) => {
Err(Error::BufferNotInRAM) => {
trace!("Copying SPIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
@ -268,7 +288,7 @@ impl<'d, T: Instance> Spim<'d, T> {
async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
match self.async_inner_from_ram(rx, tx).await {
Ok(_) => Ok(()),
Err(Error::DMABufferNotInDataMemory) => {
Err(Error::BufferNotInRAM) => {
trace!("Copying SPIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
@ -385,8 +405,10 @@ pub(crate) mod sealed {
}
}
/// SPIM peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
type Interrupt: Interrupt;
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_spim {
@ -401,7 +423,7 @@ macro_rules! impl_spim {
}
}
impl crate::spim::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
@ -437,7 +459,7 @@ mod eh1 {
match *self {
Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
Self::DMABufferNotInDataMemory => embedded_hal_1::spi::ErrorKind::Other,
Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,
}
}
}
@ -446,25 +468,19 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spim<'d, T> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_transfer(words, &[])
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite<u8> for Spim<'d, T> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus<u8> for Spim<'d, T> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -475,49 +491,30 @@ mod eh1 {
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
use core::future::Future;
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
use super::*;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
async fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(words)
}
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.read(words).await
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(data)
}
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
self.write(data).await
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
self.transfer(rx, tx).await
}
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
self.transfer(rx, tx)
}
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn transfer_in_place<'a>(
&'a mut self,
words: &'a mut [u8],
) -> Self::TransferInPlaceFuture<'a> {
self.transfer_in_place(words)
}
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.transfer_in_place(words).await
}
}
}

550
embassy-nrf/src/spis.rs Normal file
View File

@ -0,0 +1,550 @@
//! Serial Peripheral Instance in slave mode (SPIS) driver.
#![macro_use]
use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{interrupt, pac, Peripheral};
/// SPIS error
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// TX buffer was too long.
TxBufferTooLong,
/// RX buffer was too long.
RxBufferTooLong,
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
BufferNotInRAM,
}
/// SPIS configuration.
#[non_exhaustive]
pub struct Config {
/// SPI mode
pub mode: Mode,
/// Overread character.
///
/// If the master keeps clocking the bus after all the bytes in the TX buffer have
/// already been transmitted, this byte will be constantly transmitted in the MISO line.
pub orc: u8,
/// Default byte.
///
/// This is the byte clocked out in the MISO line for ignored transactions (if the master
/// sets CSN low while the semaphore is owned by the firmware)
pub def: u8,
/// Automatically make the firmware side acquire the semaphore on transfer end.
pub auto_acquire: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
mode: MODE_0,
orc: 0x00,
def: 0x00,
auto_acquire: true,
}
}
}
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_end.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.end().clear());
}
if r.events_acquired.read().bits() != 0 {
s.waker.wake();
r.intenclr.write(|w| w.acquired().clear());
}
}
}
/// SPIS driver.
pub struct Spis<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Spis<'d, T> {
/// Create a new SPIS driver.
pub fn new(
spis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(cs, sck, miso, mosi);
Self::new_inner(
spis,
cs.map_into(),
sck.map_into(),
Some(miso.map_into()),
Some(mosi.map_into()),
config,
)
}
/// Create a new SPIS driver, capable of TX only (MISO only).
pub fn new_txonly(
spis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(cs, sck, miso);
Self::new_inner(spis, cs.map_into(), sck.map_into(), Some(miso.map_into()), None, config)
}
/// Create a new SPIS driver, capable of RX only (MOSI only).
pub fn new_rxonly(
spis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(cs, sck, mosi);
Self::new_inner(spis, cs.map_into(), sck.map_into(), None, Some(mosi.map_into()), config)
}
fn new_inner(
spis: impl Peripheral<P = T> + 'd,
cs: PeripheralRef<'d, AnyPin>,
sck: PeripheralRef<'d, AnyPin>,
miso: Option<PeripheralRef<'d, AnyPin>>,
mosi: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
compiler_fence(Ordering::SeqCst);
into_ref!(spis, cs, sck);
let r = T::regs();
// Configure pins.
sck.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
cs.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) });
if let Some(mosi) = &mosi {
mosi.conf().write(|w| w.input().connect().drive().h0h1());
r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) });
}
if let Some(miso) = &miso {
miso.conf().write(|w| w.dir().output().drive().h0h1());
r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) });
}
// Enable SPIS instance.
r.enable.write(|w| w.enable().enabled());
// Configure mode.
let mode = config.mode;
r.config.write(|w| {
match mode {
MODE_0 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().leading();
}
MODE_1 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().trailing();
}
MODE_2 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().leading();
}
MODE_3 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().trailing();
}
}
w
});
// Set over-read character.
let orc = config.orc;
r.orc.write(|w| unsafe { w.orc().bits(orc) });
// Set default character.
let def = config.def;
r.def.write(|w| unsafe { w.def().bits(def) });
// Configure auto-acquire on 'transfer end' event.
if config.auto_acquire {
r.shorts.write(|w| w.end_acquire().bit(true));
}
// Disable all events interrupts.
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: spis }
}
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
slice_in_ram_or(tx, Error::BufferNotInRAM)?;
// NOTE: RAM slice check for rx is not necessary, as a mutable
// slice can only be built from data located in RAM.
compiler_fence(Ordering::SeqCst);
let r = T::regs();
// Set up the DMA write.
let (ptr, len) = slice_ptr_parts(tx);
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
// Set up the DMA read.
let (ptr, len) = slice_ptr_parts_mut(rx);
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
// Reset end event.
r.events_end.reset();
// Release the semaphore.
r.tasks_release.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
compiler_fence(Ordering::SeqCst);
let r = T::regs();
// Acquire semaphore.
if r.semstat.read().bits() != 1 {
r.events_acquired.reset();
r.tasks_acquire.write(|w| unsafe { w.bits(1) });
// Wait until CPU has acquired the semaphore.
while r.semstat.read().bits() != 1 {}
}
self.prepare(rx, tx)?;
// Wait for 'end' event.
while r.events_end.read().bits() == 0 {}
let n_rx = r.rxd.amount.read().bits() as usize;
let n_tx = r.txd.amount.read().bits() as usize;
compiler_fence(Ordering::SeqCst);
Ok((n_rx, n_tx))
}
fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
match self.blocking_inner_from_ram(rx, tx) {
Ok(n) => Ok(n),
Err(Error::BufferNotInRAM) => {
trace!("Copying SPIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.blocking_inner_from_ram(rx, tx_ram_buf)
}
Err(error) => Err(error),
}
}
async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {
let r = T::regs();
let s = T::state();
// Clear status register.
r.status.write(|w| w.overflow().clear().overread().clear());
// Acquire semaphore.
if r.semstat.read().bits() != 1 {
// Reset and enable the acquire event.
r.events_acquired.reset();
r.intenset.write(|w| w.acquired().set());
// Request acquiring the SPIS semaphore.
r.tasks_acquire.write(|w| unsafe { w.bits(1) });
// Wait until CPU has acquired the semaphore.
poll_fn(|cx| {
s.waker.register(cx.waker());
if r.events_acquired.read().bits() == 1 {
r.events_acquired.reset();
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
self.prepare(rx, tx)?;
// Wait for 'end' event.
r.intenset.write(|w| w.end().set());
poll_fn(|cx| {
s.waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
r.events_end.reset();
return Poll::Ready(());
}
Poll::Pending
})
.await;
let n_rx = r.rxd.amount.read().bits() as usize;
let n_tx = r.txd.amount.read().bits() as usize;
compiler_fence(Ordering::SeqCst);
Ok((n_rx, n_tx))
}
async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
match self.async_inner_from_ram(rx, tx).await {
Ok(n) => Ok(n),
Err(Error::BufferNotInRAM) => {
trace!("Copying SPIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.async_inner_from_ram(rx, tx_ram_buf).await
}
Err(error) => Err(error),
}
}
/// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted.
/// Returns number of bytes read.
pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.blocking_inner(data, &[]).map(|n| n.0)
}
/// Simultaneously sends and receives data. Blocks until the transmission is completed.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.blocking_inner(read, write)
}
/// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.blocking_inner_from_ram(read, write)
}
/// Simultaneously sends and receives data.
/// Places the received data into the same buffer and blocks until the transmission is completed.
/// Returns number of bytes transferred.
pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.blocking_inner_from_ram(data, data).map(|n| n.0)
}
/// Sends data, discarding any received data. Blocks until the transmission is completed.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes written.
pub fn blocking_write(&mut self, data: &[u8]) -> Result<usize, Error> {
self.blocking_inner(&mut [], data).map(|n| n.1)
}
/// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes written.
pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
self.blocking_inner_from_ram(&mut [], data).map(|n| n.1)
}
/// Reads data from the SPI bus without sending anything.
/// Returns number of bytes read.
pub async fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.async_inner(data, &[]).await.map(|n| n.0)
}
/// Simultaneously sends and receives data.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.async_inner(read, write).await
}
/// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes transferred `(n_rx, n_tx)`.
pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {
self.async_inner_from_ram(read, write).await
}
/// Simultaneously sends and receives data. Places the received data into the same buffer.
/// Returns number of bytes transferred.
pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.async_inner_from_ram(data, data).await.map(|n| n.0)
}
/// Sends data, discarding any received data.
/// If necessary, the write buffer will be copied into RAM (see struct description for detail).
/// Returns number of bytes written.
pub async fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
self.async_inner(&mut [], data).await.map(|n| n.1)
}
/// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
/// Returns number of bytes written.
pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {
self.async_inner_from_ram(&mut [], data).await.map(|n| n.1)
}
/// Checks if last transaction overread.
pub fn is_overread(&mut self) -> bool {
T::regs().status.read().overread().is_present()
}
/// Checks if last transaction overflowed.
pub fn is_overflow(&mut self) -> bool {
T::regs().status.read().overflow().is_present()
}
}
impl<'d, T: Instance> Drop for Spis<'d, T> {
fn drop(&mut self) {
trace!("spis drop");
// Disable
let r = T::regs();
r.enable.write(|w| w.enable().disabled());
gpio::deconfigure_pin(r.psel.sck.read().bits());
gpio::deconfigure_pin(r.psel.csn.read().bits());
gpio::deconfigure_pin(r.psel.miso.read().bits());
gpio::deconfigure_pin(r.psel.mosi.read().bits());
trace!("spis drop: done");
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static pac::spis0::RegisterBlock;
fn state() -> &'static State;
}
}
/// SPIS peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_spis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::spis::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::spis0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::spis::sealed::State {
static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new();
&STATE
}
}
impl crate::spis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
// ====================
impl<'d, T: Instance> SetConfig for Spis<'d, T> {
type Config = Config;
fn set_config(&mut self, config: &Self::Config) {
let r = T::regs();
// Configure mode.
let mode = config.mode;
r.config.write(|w| {
match mode {
MODE_0 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().leading();
}
MODE_1 => {
w.order().msb_first();
w.cpol().active_high();
w.cpha().trailing();
}
MODE_2 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().leading();
}
MODE_3 => {
w.order().msb_first();
w.cpol().active_low();
w.cpha().trailing();
}
}
w
});
// Set over-read character.
let orc = config.orc;
r.orc.write(|w| unsafe { w.orc().bits(orc) });
// Set default character.
let def = config.def;
r.def.write(|w| unsafe { w.def().bits(def) });
// Configure auto-acquire on 'transfer end' event.
let auto_acquire = config.auto_acquire;
r.shorts.write(|w| w.end_acquire().bit(auto_acquire));
}
}

View File

@ -1,37 +1,50 @@
//! Temperature sensor interface.
//! Builtin temperature sensor driver.
use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::I30F2;
use futures::future::poll_fn;
use crate::interrupt::InterruptExt;
use crate::peripherals::TEMP;
use crate::{interrupt, pac, Peripheral};
/// Integrated temperature sensor.
/// Interrupt handler.
pub struct InterruptHandler {
_private: (),
}
impl interrupt::typelevel::Handler<interrupt::typelevel::TEMP> for InterruptHandler {
unsafe fn on_interrupt() {
let r = unsafe { &*pac::TEMP::PTR };
r.intenclr.write(|w| w.datardy().clear());
WAKER.wake();
}
}
/// Builtin temperature sensor driver.
pub struct Temp<'d> {
_irq: PeripheralRef<'d, interrupt::TEMP>,
_peri: PeripheralRef<'d, TEMP>,
}
static WAKER: AtomicWaker = AtomicWaker::new();
impl<'d> Temp<'d> {
pub fn new(_t: impl Peripheral<P = TEMP> + 'd, irq: impl Peripheral<P = interrupt::TEMP> + 'd) -> Self {
into_ref!(_t, irq);
/// Create a new temperature sensor driver.
pub fn new(
_peri: impl Peripheral<P = TEMP> + 'd,
_irq: impl interrupt::typelevel::Binding<interrupt::typelevel::TEMP, InterruptHandler> + 'd,
) -> Self {
into_ref!(_peri);
// Enable interrupt that signals temperature values
irq.disable();
irq.set_handler(|_| {
let t = Self::regs();
t.intenclr.write(|w| w.datardy().clear());
WAKER.wake();
});
irq.enable();
Self { _irq: irq }
interrupt::TEMP.unpend();
unsafe { interrupt::TEMP.enable() };
Self { _peri }
}
/// Perform an asynchronous temperature measurement. The returned future
@ -42,8 +55,19 @@ impl<'d> Temp<'d> {
/// # Example
///
/// ```no_run
/// let mut t = Temp::new(p.TEMP, interrupt::take!(TEMP));
/// use embassy_nrf::{bind_interrupts, temp};
/// use embassy_nrf::temp::Temp;
/// use embassy_time::{Duration, Timer};
///
/// bind_interrupts!(struct Irqs {
/// TEMP => temp::InterruptHandler;
/// });
///
/// # async {
/// # let p: embassy_nrf::Peripherals = todo!();
/// let mut t = Temp::new(p.TEMP, Irqs);
/// let v: u16 = t.read().await.to_num::<u16>();
/// # };
/// ```
pub async fn read(&mut self) -> I30F2 {
// In case the future is dropped, stop the task and reset events.

View File

@ -7,7 +7,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use embassy_time::driver::{AlarmHandle, Driver};
use crate::interrupt::{Interrupt, InterruptExt};
use crate::interrupt::InterruptExt;
use crate::{interrupt, pac};
fn rtc() -> &'static pac::rtc0::RegisterBlock {
@ -67,7 +67,7 @@ fn compare_n(n: usize) -> u32 {
1 << (n + 16)
}
#[cfg(tests)]
#[cfg(test)]
mod test {
use super::*;
@ -142,9 +142,8 @@ impl RtcDriver {
// Wait for clear
while r.counter.read().bits() != 0 {}
let irq = unsafe { interrupt::RTC1::steal() };
irq.set_priority(irq_prio);
irq.enable();
interrupt::RTC1.set_priority(irq_prio);
unsafe { interrupt::RTC1.enable() };
}
fn on_interrupt(&self) {
@ -243,22 +242,25 @@ impl Driver for RtcDriver {
})
}
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
critical_section::with(|cs| {
let n = alarm.id() as _;
let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
let t = self.now();
// If alarm timestamp has passed, trigger it instantly.
if timestamp <= t {
self.trigger_alarm(n, cs);
return;
}
let r = rtc();
let t = self.now();
if timestamp <= t {
// If alarm timestamp has passed the alarm will not fire.
// Disarm the alarm and return `false` to indicate that.
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
alarm.timestamp.set(u64::MAX);
return false;
}
// If it hasn't triggered yet, setup it in the compare channel.
// Write the CC value regardless of whether we're going to enable it now or not.
@ -287,10 +289,13 @@ impl Driver for RtcDriver {
// It will be setup later by `next_period`.
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
}
true
})
}
}
#[cfg(feature = "rt")]
#[interrupt]
fn RTC1() {
DRIVER.on_interrupt()

View File

@ -1,14 +1,13 @@
//! Timer driver.
//!
//! Important note! This driver is very low level. For most time-related use cases, like
//! "sleep for X seconds", "do something every X seconds", or measuring time, you should
//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead!
#![macro_use]
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use futures::future::poll_fn;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::ppi::{Event, Task};
use crate::{pac, Peripheral};
@ -20,17 +19,19 @@ pub(crate) mod sealed {
/// The number of CC registers this instance has.
const CCS: usize;
fn regs() -> &'static pac::timer0::RegisterBlock;
/// Storage for the waker for CC register `n`.
fn waker(n: usize) -> &'static AtomicWaker;
}
pub trait ExtendedInstance {}
pub trait TimerType {}
}
/// Basic Timer instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
type Interrupt: Interrupt;
/// Interrupt for this peripheral.
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
/// Extended timer instance.
pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {}
macro_rules! impl_timer {
@ -40,15 +41,9 @@ macro_rules! impl_timer {
fn regs() -> &'static pac::timer0::RegisterBlock {
unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) }
}
fn waker(n: usize) -> &'static ::embassy_sync::waitqueue::AtomicWaker {
use ::embassy_sync::waitqueue::AtomicWaker;
const NEW_AW: AtomicWaker = AtomicWaker::new();
static WAKERS: [AtomicWaker; $ccs] = [NEW_AW; $ccs];
&WAKERS[n]
}
}
impl crate::timer::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
($type:ident, $pac_type:ident, $irq:ident) => {
@ -61,85 +56,77 @@ macro_rules! impl_timer {
};
}
/// Timer frequency
#[repr(u8)]
pub enum Frequency {
// I'd prefer not to prefix these with `F`, but Rust identifiers can't start with digits.
/// 16MHz
F16MHz = 0,
/// 8MHz
F8MHz = 1,
/// 4MHz
F4MHz = 2,
/// 2MHz
F2MHz = 3,
/// 1MHz
F1MHz = 4,
/// 500kHz
F500kHz = 5,
/// 250kHz
F250kHz = 6,
/// 125kHz
F125kHz = 7,
/// 62500Hz
F62500Hz = 8,
/// 31250Hz
F31250Hz = 9,
}
/// nRF Timer driver.
///
/// The timer has an internal counter, which is incremented for every tick of the timer.
/// The counter is 32-bit, so it wraps back to 0 at 4294967296.
/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32.
///
/// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter
/// or trigger an event when the counter reaches a certain value.
pub trait TimerType: sealed::TimerType {}
pub enum Awaitable {}
pub enum NotAwaitable {}
impl sealed::TimerType for Awaitable {}
impl sealed::TimerType for NotAwaitable {}
impl TimerType for Awaitable {}
impl TimerType for NotAwaitable {}
pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> {
/// Timer driver.
pub struct Timer<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
_i: PhantomData<I>,
}
impl<'d, T: Instance> Timer<'d, T, Awaitable> {
pub fn new_awaitable(timer: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd) -> Self {
into_ref!(irq);
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
Self::new_irqless(timer)
}
}
impl<'d, T: Instance> Timer<'d, T, NotAwaitable> {
/// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
impl<'d, T: Instance> Timer<'d, T> {
/// Create a new `Timer` driver.
///
/// This can be useful for triggering tasks via PPI
/// `Uarte` uses this internally.
pub fn new(timer: impl Peripheral<P = T> + 'd) -> Self {
Self::new_irqless(timer)
Self::new_inner(timer, false)
}
}
impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
/// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work.
/// Create a new `Timer` driver in counter mode.
///
/// This is used by the public constructors.
fn new_irqless(timer: impl Peripheral<P = T> + 'd) -> Self {
/// This can be useful for triggering tasks via PPI
/// `Uarte` uses this internally.
pub fn new_counter(timer: impl Peripheral<P = T> + 'd) -> Self {
Self::new_inner(timer, true)
}
fn new_inner(timer: impl Peripheral<P = T> + 'd, is_counter: bool) -> Self {
into_ref!(timer);
let regs = T::regs();
let mut this = Self {
_p: timer,
_i: PhantomData,
};
let this = Self { _p: timer };
// Stop the timer before doing anything else,
// since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.
this.stop();
// Set the instance to timer mode.
regs.mode.write(|w| w.mode().timer());
if is_counter {
regs.mode.write(|w| w.mode().low_power_counter());
} else {
regs.mode.write(|w| w.mode().timer());
}
// Make the counter's max value as high as possible.
// TODO: is there a reason someone would want to set this lower?
@ -181,24 +168,32 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
/// Returns the START task, for use with PPI.
///
/// When triggered, this task starts the timer.
pub fn task_start(&self) -> Task {
pub fn task_start(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_start)
}
/// Returns the STOP task, for use with PPI.
///
/// When triggered, this task stops the timer.
pub fn task_stop(&self) -> Task {
pub fn task_stop(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_stop)
}
/// Returns the CLEAR task, for use with PPI.
///
/// When triggered, this task resets the timer's counter to 0.
pub fn task_clear(&self) -> Task {
pub fn task_clear(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_clear)
}
/// Returns the COUNT task, for use with PPI.
///
/// When triggered, this task increments the timer's counter by 1.
/// Only works in counter mode.
pub fn task_count(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_count)
}
/// Change the timer's frequency.
///
/// This will stop the timer if it isn't already stopped,
@ -213,31 +208,17 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
.write(|w| unsafe { w.prescaler().bits(frequency as u8) })
}
fn on_interrupt(_: *mut ()) {
let regs = T::regs();
for n in 0..T::CCS {
if regs.events_compare[n].read().bits() != 0 {
// Clear the interrupt, otherwise the interrupt will be repeatedly raised as soon as the interrupt handler exits.
// We can't clear the event, because it's used to poll whether the future is done or still pending.
regs.intenclr
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + n))) });
T::waker(n).wake();
}
}
}
/// Returns this timer's `n`th CC register.
///
/// # Panics
/// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer).
pub fn cc(&mut self, n: usize) -> Cc<T, I> {
pub fn cc(&self, n: usize) -> Cc<'d, T> {
if n >= T::CCS {
panic!("Cannot get CC register {} of timer with {} CC registers.", n, T::CCS);
}
Cc {
n,
_p: self._p.reborrow(),
_i: PhantomData,
_p: unsafe { self._p.clone_unchecked() },
}
}
}
@ -249,49 +230,12 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> {
///
/// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register.
/// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register
pub struct Cc<'d, T: Instance, I: TimerType = NotAwaitable> {
pub struct Cc<'d, T: Instance> {
n: usize,
_p: PeripheralRef<'d, T>,
_i: PhantomData<I>,
}
impl<'d, T: Instance> Cc<'d, T, Awaitable> {
/// Wait until the timer's counter reaches the value stored in this register.
///
/// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`.
pub async fn wait(&mut self) {
let regs = T::regs();
// Enable the interrupt for this CC's COMPARE event.
regs.intenset
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) });
// Disable the interrupt if the future is dropped.
let on_drop = OnDrop::new(|| {
regs.intenclr
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) });
});
poll_fn(|cx| {
T::waker(self.n).register(cx.waker());
if regs.events_compare[self.n].read().bits() != 0 {
// Reset the register for next time
regs.events_compare[self.n].reset();
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
// The interrupt was already disabled in the interrupt handler, so there's no need to disable it again.
on_drop.defuse();
}
}
impl<'d, T: Instance> Cc<'d, T, NotAwaitable> {}
impl<'d, T: Instance, I: TimerType> Cc<'d, T, I> {
impl<'d, T: Instance> Cc<'d, T> {
/// Get the current value stored in the register.
pub fn read(&self) -> u32 {
T::regs().cc[self.n].read().cc().bits()
@ -314,14 +258,14 @@ impl<'d, T: Instance, I: TimerType> Cc<'d, T, I> {
/// Returns this CC register's CAPTURE task, for use with PPI.
///
/// When triggered, this task will capture the current value of the timer's counter in this register.
pub fn task_capture(&self) -> Task {
pub fn task_capture(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_capture)
}
/// Returns this CC register's COMPARE event, for use with PPI.
///
/// This event will fire when the timer's counter reaches the value in this CC register.
pub fn event_compare(&self) -> Event {
pub fn event_compare(&self) -> Event<'d> {
Event::from_reg(&T::regs().events_compare[self.n])
}

View File

@ -1,12 +1,9 @@
//! I2C-compatible Two Wire Interface in master mode (TWIM) driver.
#![macro_use]
//! HAL interface to the TWIM peripheral.
//!
//! See product specification:
//!
//! - nRF52832: Section 33
//! - nRF52840: Section 6.31
use core::future::Future;
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
@ -16,30 +13,46 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
use futures::future::poll_fn;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin;
use crate::interrupt::{Interrupt, InterruptExt};
use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram, slice_in_ram_or};
use crate::{gpio, pac, Peripheral};
use crate::{gpio, interrupt, pac, Peripheral};
/// TWI frequency
#[derive(Clone, Copy)]
pub enum Frequency {
#[doc = "26738688: 100 kbps"]
/// 100 kbps
K100 = 26738688,
#[doc = "67108864: 250 kbps"]
/// 250 kbps
K250 = 67108864,
#[doc = "104857600: 400 kbps"]
/// 400 kbps
K400 = 104857600,
}
/// TWIM config.
#[non_exhaustive]
pub struct Config {
/// Frequency
pub frequency: Frequency,
/// Enable high drive for the SDA line.
pub sda_high_drive: bool,
/// Enable internal pullup for the SDA line.
///
/// Note that using external pullups is recommended for I2C, and
/// most boards already have them.
pub sda_pullup: bool,
/// Enable high drive for the SCL line.
pub scl_high_drive: bool,
/// Enable internal pullup for the SCL line.
///
/// Note that using external pullups is recommended for I2C, and
/// most boards already have them.
pub scl_pullup: bool,
}
@ -55,37 +68,67 @@ impl Default for Config {
}
}
/// TWI error.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// TX buffer was too long.
TxBufferTooLong,
/// RX buffer was too long.
RxBufferTooLong,
/// Data transmit failed.
Transmit,
/// Data reception failed.
Receive,
DMABufferNotInDataMemory,
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
BufferNotInRAM,
/// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly.
AddressNack,
/// Didn't receive an ACK bit after a data byte.
DataNack,
/// Overrun error.
Overrun,
/// Timeout error.
Timeout,
}
/// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_stopped.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.stopped().clear());
}
if r.events_error.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.error().clear());
}
}
}
/// TWI driver.
pub struct Twim<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Twim<'d, T> {
/// Create a new TWI driver.
pub fn new(
twim: impl Peripheral<P = T> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sda: impl Peripheral<P = impl GpioPin> + 'd,
scl: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(twim, irq, sda, scl);
into_ref!(twim, sda, scl);
let r = T::regs();
@ -131,30 +174,15 @@ impl<'d, T: Instance> Twim<'d, T> {
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: twim }
}
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let s = T::state();
if r.events_stopped.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.stopped().clear());
}
if r.events_error.read().bits() != 0 {
s.end_waker.wake();
r.intenclr.write(|w| w.error().clear());
}
}
/// Set TX buffer, checking that it is in RAM and has suitable length.
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?;
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::TxBufferTooLong);
@ -234,7 +262,7 @@ impl<'d, T: Instance> Twim<'d, T> {
return Err(Error::DataNack);
}
if err.overrun().is_received() {
return Err(Error::DataNack);
return Err(Error::Overrun);
}
Ok(())
}
@ -308,7 +336,7 @@ impl<'d, T: Instance> Twim<'d, T> {
return Poll::Ready(());
}
// stop if an error occured
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
@ -436,7 +464,7 @@ impl<'d, T: Instance> Twim<'d, T> {
) -> Result<(), Error> {
match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) {
Ok(_) => Ok(()),
Err(Error::DMABufferNotInDataMemory) => {
Err(Error::BufferNotInRAM) => {
trace!("Copying TWIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
@ -449,7 +477,7 @@ impl<'d, T: Instance> Twim<'d, T> {
fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
match self.setup_write_from_ram(address, wr_buffer, inten) {
Ok(_) => Ok(()),
Err(Error::DMABufferNotInDataMemory) => {
Err(Error::BufferNotInRAM) => {
trace!("Copying TWIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
@ -613,6 +641,10 @@ impl<'d, T: Instance> Twim<'d, T> {
// ===========================================
/// Read from an I2C slave.
///
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
self.setup_read(address, buffer, true)?;
self.async_wait().await;
@ -622,6 +654,10 @@ impl<'d, T: Instance> Twim<'d, T> {
Ok(())
}
/// Write to an I2C slave.
///
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {
self.setup_write(address, buffer, true)?;
self.async_wait().await;
@ -641,6 +677,11 @@ impl<'d, T: Instance> Twim<'d, T> {
Ok(())
}
/// Write data to an I2C slave, then read data from the slave without
/// triggering a stop condition between the two.
///
/// The buffers must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> {
self.setup_write_read(address, wr_buffer, rd_buffer, true)?;
self.async_wait().await;
@ -706,8 +747,10 @@ pub(crate) mod sealed {
}
}
/// TWIM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
type Interrupt: Interrupt;
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_twim {
@ -722,7 +765,7 @@ macro_rules! impl_twim {
}
}
impl crate::twim::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
@ -777,7 +820,7 @@ mod eh1 {
Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,
Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,
Self::Receive => embedded_hal_1::i2c::ErrorKind::Other,
Self::DMABufferNotInDataMemory => embedded_hal_1::i2c::ErrorKind::Other,
Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other,
Self::AddressNack => {
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
}
@ -794,7 +837,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for Twim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
@ -803,20 +846,6 @@ mod eh1 {
self.blocking_write(address, buffer)
}
fn write_iter<B>(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>,
{
todo!();
}
fn write_iter_read<B>(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error>
where
B: IntoIterator<Item = u8>,
{
todo!();
}
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_write_read(address, wr_buffer, rd_buffer)
}
@ -824,57 +853,36 @@ mod eh1 {
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
{
todo!();
}
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use super::*;
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
self.read(address, read).await
}
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(address, buffer)
}
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
self.write(address, write).await
}
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
self.write_read(address, write, read).await
}
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(address, bytes)
}
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write_read<'a>(
&'a mut self,
address: u8,
wr_buffer: &'a [u8],
rd_buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
self.write_read(address, wr_buffer, rd_buffer)
}
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a, 'b: 'a;
fn transaction<'a, 'b>(
&'a mut self,
address: u8,
operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
let _ = address;
let _ = operations;
async move { todo!() }
}
async fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
let _ = address;
let _ = operations;
todo!()
}
}
}

799
embassy-nrf/src/twis.rs Normal file
View File

@ -0,0 +1,799 @@
//! I2C-compatible Two Wire Interface in slave mode (TWIM) driver.
#![macro_use]
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin;
use crate::interrupt::typelevel::Interrupt;
use crate::util::slice_in_ram_or;
use crate::{gpio, interrupt, pac, Peripheral};
/// TWIS config.
#[non_exhaustive]
pub struct Config {
/// First address
pub address0: u8,
/// Second address, optional.
pub address1: Option<u8>,
/// Overread character.
///
/// If the master keeps clocking the bus after all the bytes in the TX buffer have
/// already been transmitted, this byte will be constantly transmitted.
pub orc: u8,
/// Enable high drive for the SDA line.
pub sda_high_drive: bool,
/// Enable internal pullup for the SDA line.
///
/// Note that using external pullups is recommended for I2C, and
/// most boards already have them.
pub sda_pullup: bool,
/// Enable high drive for the SCL line.
pub scl_high_drive: bool,
/// Enable internal pullup for the SCL line.
///
/// Note that using external pullups is recommended for I2C, and
/// most boards already have them.
pub scl_pullup: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
address0: 0x55,
address1: None,
orc: 0x00,
scl_high_drive: false,
sda_pullup: false,
sda_high_drive: false,
scl_pullup: false,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum Status {
Read,
Write,
}
/// TWIS error.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// TX buffer was too long.
TxBufferTooLong,
/// RX buffer was too long.
RxBufferTooLong,
/// Didn't receive an ACK bit after a data byte.
DataNack,
/// Bus error.
Bus,
/// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
BufferNotInRAM,
/// Overflow
Overflow,
/// Overread
OverRead,
/// Timeout
Timeout,
}
/// Received command
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Command {
/// Read
Read,
/// Write+read
WriteRead(usize),
/// Write
Write(usize),
}
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 {
s.waker.wake();
r.intenclr.modify(|_r, w| w.read().clear().write().clear());
}
if r.events_stopped.read().bits() != 0 {
s.waker.wake();
r.intenclr.modify(|_r, w| w.stopped().clear());
}
if r.events_error.read().bits() != 0 {
s.waker.wake();
r.intenclr.modify(|_r, w| w.error().clear());
}
}
}
/// TWIS driver.
pub struct Twis<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Twis<'d, T> {
/// Create a new TWIS driver.
pub fn new(
twis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sda: impl Peripheral<P = impl GpioPin> + 'd,
scl: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(twis, sda, scl);
let r = T::regs();
// Configure pins
sda.conf().write(|w| {
w.dir().input();
w.input().connect();
if config.sda_high_drive {
w.drive().h0d1();
} else {
w.drive().s0d1();
}
if config.sda_pullup {
w.pull().pullup();
}
w
});
scl.conf().write(|w| {
w.dir().input();
w.input().connect();
if config.scl_high_drive {
w.drive().h0d1();
} else {
w.drive().s0d1();
}
if config.scl_pullup {
w.pull().pullup();
}
w
});
// Select pins.
r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) });
r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) });
// Enable TWIS instance.
r.enable.write(|w| w.enable().enabled());
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
// Set address
r.address[0].write(|w| unsafe { w.address().bits(config.address0) });
r.config.write(|w| w.address0().enabled());
if let Some(address1) = config.address1 {
r.address[1].write(|w| unsafe { w.address().bits(address1) });
r.config.modify(|_r, w| w.address1().enabled());
}
// Set over-read character
r.orc.write(|w| unsafe { w.orc().bits(config.orc) });
// Generate suspend on read event
r.shorts.write(|w| w.read_suspend().enabled());
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: twis }
}
/// Set TX buffer, checking that it is in RAM and has suitable length.
unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::TxBufferTooLong);
}
let r = T::regs();
r.txd.ptr.write(|w|
// We're giving the register a pointer to the stack. Since we're
// waiting for the I2C transaction to end before this stack pointer
// becomes invalid, there's nothing wrong here.
//
// The PTR field is a full 32 bits wide and accepts the full range
// of values.
w.ptr().bits(buffer.as_ptr() as u32));
r.txd.maxcnt.write(|w|
// We're giving it the length of the buffer, so no danger of
// accessing invalid memory. We have verified that the length of the
// buffer fits in an `u8`, so the cast to `u8` is also fine.
//
// The MAXCNT field is 8 bits wide and accepts the full range of
// values.
w.maxcnt().bits(buffer.len() as _));
Ok(())
}
/// Set RX buffer, checking that it has suitable length.
unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
// NOTE: RAM slice check is not necessary, as a mutable
// slice can only be built from data located in RAM.
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::RxBufferTooLong);
}
let r = T::regs();
r.rxd.ptr.write(|w|
// We're giving the register a pointer to the stack. Since we're
// waiting for the I2C transaction to end before this stack pointer
// becomes invalid, there's nothing wrong here.
//
// The PTR field is a full 32 bits wide and accepts the full range
// of values.
w.ptr().bits(buffer.as_mut_ptr() as u32));
r.rxd.maxcnt.write(|w|
// We're giving it the length of the buffer, so no danger of
// accessing invalid memory. We have verified that the length of the
// buffer fits in an `u8`, so the cast to the type of maxcnt
// is also fine.
//
// Note that that nrf52840 maxcnt is a wider
// type than a u8, so we use a `_` cast rather than a `u8` cast.
// The MAXCNT field is thus at least 8 bits wide and accepts the
// full range of values that fit in a `u8`.
w.maxcnt().bits(buffer.len() as _));
Ok(())
}
fn clear_errorsrc(&mut self) {
let r = T::regs();
r.errorsrc
.write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true));
}
/// Returns matched address for latest command.
pub fn address_match(&self) -> u8 {
let r = T::regs();
r.address[r.match_.read().bits() as usize].read().address().bits()
}
/// Returns the index of the address matched in the latest command.
pub fn address_match_index(&self) -> usize {
T::regs().match_.read().bits() as _
}
/// Wait for read, write, stop or error
fn blocking_listen_wait(&mut self) -> Result<Status, Error> {
let r = T::regs();
loop {
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
while r.events_stopped.read().bits() == 0 {}
return Err(Error::Overflow);
}
if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return Err(Error::Bus);
}
if r.events_read.read().bits() != 0 {
r.events_read.reset();
return Ok(Status::Read);
}
if r.events_write.read().bits() != 0 {
r.events_write.reset();
return Ok(Status::Write);
}
}
}
/// Wait for stop, repeated start or error
fn blocking_listen_wait_end(&mut self, status: Status) -> Result<Command, Error> {
let r = T::regs();
loop {
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Overflow);
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return match status {
Status::Read => Ok(Command::Read),
Status::Write => {
let n = r.rxd.amount.read().bits() as usize;
Ok(Command::Write(n))
}
};
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
let n = r.rxd.amount.read().bits() as usize;
return Ok(Command::WriteRead(n));
}
}
}
/// Wait for stop or error
fn blocking_wait(&mut self) -> Result<usize, Error> {
let r = T::regs();
loop {
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
let errorsrc = r.errorsrc.read();
if errorsrc.overread().is_detected() {
return Err(Error::OverRead);
} else if errorsrc.dnack().is_received() {
return Err(Error::DataNack);
} else {
return Err(Error::Bus);
}
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
let n = r.txd.amount.read().bits() as usize;
return Ok(n);
}
}
}
/// Wait for stop or error with timeout
#[cfg(feature = "time")]
fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<usize, Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
loop {
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
let errorsrc = r.errorsrc.read();
if errorsrc.overread().is_detected() {
return Err(Error::OverRead);
} else if errorsrc.dnack().is_received() {
return Err(Error::DataNack);
} else {
return Err(Error::Bus);
}
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
let n = r.txd.amount.read().bits() as usize;
return Ok(n);
} else if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
}
/// Wait for read, write, stop or error with timeout
#[cfg(feature = "time")]
fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result<Status, Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
loop {
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
while r.events_stopped.read().bits() == 0 {}
return Err(Error::Overflow);
}
if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return Err(Error::Bus);
}
if r.events_read.read().bits() != 0 {
r.events_read.reset();
return Ok(Status::Read);
}
if r.events_write.read().bits() != 0 {
r.events_write.reset();
return Ok(Status::Write);
}
if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
}
/// Wait for stop, repeated start or error with timeout
#[cfg(feature = "time")]
fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result<Command, Error> {
let r = T::regs();
let deadline = Instant::now() + timeout;
loop {
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Overflow);
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return match status {
Status::Read => Ok(Command::Read),
Status::Write => {
let n = r.rxd.amount.read().bits() as usize;
Ok(Command::Write(n))
}
};
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
let n = r.rxd.amount.read().bits() as usize;
return Ok(Command::WriteRead(n));
} else if Instant::now() > deadline {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Err(Error::Timeout);
}
}
}
/// Wait for stop or error
fn async_wait(&mut self) -> impl Future<Output = Result<usize, Error>> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.waker.register(cx.waker());
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
let errorsrc = r.errorsrc.read();
if errorsrc.overread().is_detected() {
return Poll::Ready(Err(Error::OverRead));
} else if errorsrc.dnack().is_received() {
return Poll::Ready(Err(Error::DataNack));
} else {
return Poll::Ready(Err(Error::Bus));
}
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
let n = r.txd.amount.read().bits() as usize;
return Poll::Ready(Ok(n));
}
Poll::Pending
})
}
/// Wait for read or write
fn async_listen_wait(&mut self) -> impl Future<Output = Result<Status, Error>> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.waker.register(cx.waker());
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Poll::Ready(Err(Error::Overflow));
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
return Poll::Ready(Ok(Status::Read));
} else if r.events_write.read().bits() != 0 {
r.events_write.reset();
return Poll::Ready(Ok(Status::Write));
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return Poll::Ready(Err(Error::Bus));
}
Poll::Pending
})
}
/// Wait for stop, repeated start or error
fn async_listen_wait_end(&mut self, status: Status) -> impl Future<Output = Result<Command, Error>> {
poll_fn(move |cx| {
let r = T::regs();
let s = T::state();
s.waker.register(cx.waker());
// stop if an error occurred
if r.events_error.read().bits() != 0 {
r.events_error.reset();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
return Poll::Ready(Err(Error::Overflow));
} else if r.events_stopped.read().bits() != 0 {
r.events_stopped.reset();
return match status {
Status::Read => Poll::Ready(Ok(Command::Read)),
Status::Write => {
let n = r.rxd.amount.read().bits() as usize;
Poll::Ready(Ok(Command::Write(n)))
}
};
} else if r.events_read.read().bits() != 0 {
r.events_read.reset();
let n = r.rxd.amount.read().bits() as usize;
return Poll::Ready(Ok(Command::WriteRead(n)));
}
Poll::Pending
})
}
fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
// Set up the DMA write.
unsafe { self.set_tx_buffer(buffer)? };
// Clear events
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset.write(|w| w.stopped().set().error().set());
} else {
r.intenclr.write(|w| w.stopped().clear().error().clear());
}
// Start write operation.
r.tasks_preparetx.write(|w| unsafe { w.bits(1) });
r.tasks_resume.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {
match self.setup_respond_from_ram(wr_buffer, inten) {
Ok(_) => Ok(()),
Err(Error::BufferNotInRAM) => {
trace!("Copying TWIS tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];
tx_ram_buf.copy_from_slice(wr_buffer);
self.setup_respond_from_ram(&tx_ram_buf, inten)
}
Err(error) => Err(error),
}
}
fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
// Set up the DMA read.
unsafe { self.set_rx_buffer(buffer)? };
// Clear events
r.events_read.reset();
r.events_write.reset();
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset
.write(|w| w.stopped().set().error().set().read().set().write().set());
} else {
r.intenclr
.write(|w| w.stopped().clear().error().clear().read().clear().write().clear());
}
// Start read operation.
r.tasks_preparerx.write(|w| unsafe { w.bits(1) });
Ok(())
}
fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> {
let r = T::regs();
compiler_fence(SeqCst);
// Clear events
r.events_read.reset();
r.events_write.reset();
r.events_stopped.reset();
r.events_error.reset();
self.clear_errorsrc();
if inten {
r.intenset.write(|w| w.stopped().set().error().set().read().set());
} else {
r.intenclr.write(|w| w.stopped().clear().error().clear().read().clear());
}
Ok(())
}
/// Wait for commands from an I2C master.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
self.setup_listen(buffer, false)?;
let status = self.blocking_listen_wait()?;
if status == Status::Write {
self.setup_listen_end(false)?;
let command = self.blocking_listen_wait_end(status)?;
return Ok(command);
}
Ok(Command::Read)
}
/// Respond to an I2C master READ command.
/// Returns the number of bytes written.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond(buffer, false)?;
self.blocking_wait()
}
/// Same as [`blocking_respond_to_read`](Twis::blocking_respond_to_read) but will fail instead of copying data into RAM.
/// Consult the module level documentation to learn more.
pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, false)?;
self.blocking_wait()
}
// ===========================================
/// Wait for commands from an I2C master, with timeout.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
#[cfg(feature = "time")]
pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result<Command, Error> {
self.setup_listen(buffer, false)?;
let status = self.blocking_listen_wait_timeout(timeout)?;
if status == Status::Write {
self.setup_listen_end(false)?;
let command = self.blocking_listen_wait_end_timeout(status, timeout)?;
return Ok(command);
}
Ok(Command::Read)
}
/// Respond to an I2C master READ command with timeout.
/// Returns the number of bytes written.
/// See [`blocking_respond_to_read`].
#[cfg(feature = "time")]
pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<usize, Error> {
self.setup_respond(buffer, false)?;
self.blocking_wait_timeout(timeout)
}
/// Same as [`blocking_respond_to_read_timeout`](Twis::blocking_respond_to_read_timeout) but will fail instead of copying data into RAM.
/// Consult the module level documentation to learn more.
#[cfg(feature = "time")]
pub fn blocking_respond_to_read_from_ram_timeout(
&mut self,
buffer: &[u8],
timeout: Duration,
) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, false)?;
self.blocking_wait_timeout(timeout)
}
// ===========================================
/// Wait asynchronously for commands from an I2C master.
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
/// To know which one of the addresses were matched, call `address_match` or `address_match_index`
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
self.setup_listen(buffer, true)?;
let status = self.async_listen_wait().await?;
if status == Status::Write {
self.setup_listen_end(true)?;
let command = self.async_listen_wait_end(status).await?;
return Ok(command);
}
Ok(Command::Read)
}
/// Respond to an I2C master READ command, asynchronously.
/// Returns the number of bytes written.
/// The buffer must have a length of at most 255 bytes on the nRF52832
/// and at most 65535 bytes on the nRF52840.
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond(buffer, true)?;
self.async_wait().await
}
/// Same as [`respond_to_read`](Twis::respond_to_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.
pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {
self.setup_respond_from_ram(buffer, true)?;
self.async_wait().await
}
}
impl<'a, T: Instance> Drop for Twis<'a, T> {
fn drop(&mut self) {
trace!("twis drop");
// TODO: check for abort
// disable!
let r = T::regs();
r.enable.write(|w| w.enable().disabled());
gpio::deconfigure_pin(r.psel.sda.read().bits());
gpio::deconfigure_pin(r.psel.scl.read().bits());
trace!("twis drop: done");
}
}
pub(crate) mod sealed {
use super::*;
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static pac::twis0::RegisterBlock;
fn state() -> &'static State;
}
}
/// TWIS peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_twis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::twis::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::twis0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::twis::sealed::State {
static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new();
&STATE
}
}
impl crate::twis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,26 @@
//! Universal Serial Bus (USB) driver.
#![macro_use]
pub mod vbus_detect;
use core::future::poll_fn;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
use core::task::Poll;
use cortex_m::peripheral::NVIC;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
pub use embassy_usb;
use embassy_usb::driver::{self, EndpointError, Event, Unsupported};
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
use futures::future::poll_fn;
use futures::Future;
use embassy_usb_driver as driver;
use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported};
use pac::usbd::RegisterBlock;
use crate::interrupt::{Interrupt, InterruptExt};
use self::vbus_detect::VbusDetect;
use crate::interrupt::typelevel::Interrupt;
use crate::util::slice_in_ram;
use crate::{pac, Peripheral};
use crate::{interrupt, pac, Peripheral};
const NEW_AW: AtomicWaker = AtomicWaker::new();
static BUS_WAKER: AtomicWaker = NEW_AW;
@ -26,161 +29,13 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
/// There are multiple ways to detect USB power. The behavior
/// here provides a hook into determining whether it is.
pub trait UsbSupply {
fn is_usb_detected(&self) -> bool;
type UsbPowerReadyFuture<'a>: Future<Output = Result<(), ()>> + 'a
where
Self: 'a;
fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_>;
/// Interrupt handler.
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
pub struct Driver<'d, T: Instance, P: UsbSupply> {
_p: PeripheralRef<'d, T>,
alloc_in: Allocator,
alloc_out: Allocator,
usb_supply: P,
}
/// Uses the POWER peripheral to detect when power is available
/// for USB. Unsuitable for usage with the nRF softdevice.
#[cfg(not(feature = "_nrf5340-app"))]
pub struct PowerUsb {
_private: (),
}
/// Can be used to signal that power is available. Particularly suited for
/// use with the nRF softdevice.
pub struct SignalledSupply {
usb_detected: AtomicBool,
power_ready: AtomicBool,
}
static POWER_WAKER: AtomicWaker = NEW_AW;
#[cfg(not(feature = "_nrf5340-app"))]
impl PowerUsb {
pub fn new(power_irq: impl Interrupt) -> Self {
let regs = unsafe { &*pac::POWER::ptr() };
power_irq.set_handler(Self::on_interrupt);
power_irq.unpend();
power_irq.enable();
regs.intenset
.write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set());
Self { _private: () }
}
#[cfg(not(feature = "_nrf5340-app"))]
fn on_interrupt(_: *mut ()) {
let regs = unsafe { &*pac::POWER::ptr() };
if regs.events_usbdetected.read().bits() != 0 {
regs.events_usbdetected.reset();
BUS_WAKER.wake();
}
if regs.events_usbremoved.read().bits() != 0 {
regs.events_usbremoved.reset();
BUS_WAKER.wake();
POWER_WAKER.wake();
}
if regs.events_usbpwrrdy.read().bits() != 0 {
regs.events_usbpwrrdy.reset();
POWER_WAKER.wake();
}
}
}
#[cfg(not(feature = "_nrf5340-app"))]
impl UsbSupply for PowerUsb {
fn is_usb_detected(&self) -> bool {
let regs = unsafe { &*pac::POWER::ptr() };
regs.usbregstatus.read().vbusdetect().is_vbus_present()
}
type UsbPowerReadyFuture<'a> = impl Future<Output = Result<(), ()>> + 'a where Self: 'a;
fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> {
poll_fn(move |cx| {
POWER_WAKER.register(cx.waker());
let regs = unsafe { &*pac::POWER::ptr() };
if regs.usbregstatus.read().outputrdy().is_ready() {
Poll::Ready(Ok(()))
} else if !self.is_usb_detected() {
Poll::Ready(Err(()))
} else {
Poll::Pending
}
})
}
}
impl SignalledSupply {
pub fn new(usb_detected: bool, power_ready: bool) -> Self {
BUS_WAKER.wake();
Self {
usb_detected: AtomicBool::new(usb_detected),
power_ready: AtomicBool::new(power_ready),
}
}
pub fn detected(&self, detected: bool) {
self.usb_detected.store(detected, Ordering::Relaxed);
self.power_ready.store(false, Ordering::Relaxed);
BUS_WAKER.wake();
POWER_WAKER.wake();
}
pub fn ready(&self) {
self.power_ready.store(true, Ordering::Relaxed);
POWER_WAKER.wake();
}
}
impl UsbSupply for &SignalledSupply {
fn is_usb_detected(&self) -> bool {
self.usb_detected.load(Ordering::Relaxed)
}
type UsbPowerReadyFuture<'a> = impl Future<Output = Result<(), ()>> + 'a where Self: 'a;
fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> {
poll_fn(move |cx| {
POWER_WAKER.register(cx.waker());
if self.power_ready.load(Ordering::Relaxed) {
Poll::Ready(Ok(()))
} else if !self.usb_detected.load(Ordering::Relaxed) {
Poll::Ready(Err(()))
} else {
Poll::Pending
}
})
}
}
impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> {
pub fn new(usb: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd, usb_supply: P) -> Self {
into_ref!(usb, irq);
irq.set_handler(Self::on_interrupt);
irq.unpend();
irq.enable();
Self {
_p: usb,
alloc_in: Allocator::new(),
alloc_out: Allocator::new(),
usb_supply,
}
}
fn on_interrupt(_: *mut ()) {
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let regs = T::regs();
if regs.events_usbreset.read().bits() != 0 {
@ -231,25 +86,54 @@ impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> {
}
}
impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> {
/// USB driver.
pub struct Driver<'d, T: Instance, V: VbusDetect> {
_p: PeripheralRef<'d, T>,
alloc_in: Allocator,
alloc_out: Allocator,
vbus_detect: V,
}
impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> {
/// Create a new USB driver.
pub fn new(
usb: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
vbus_detect: V,
) -> Self {
into_ref!(usb);
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self {
_p: usb,
alloc_in: Allocator::new(),
alloc_out: Allocator::new(),
vbus_detect,
}
}
}
impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V> {
type EndpointOut = Endpoint<'d, T, Out>;
type EndpointIn = Endpoint<'d, T, In>;
type ControlPipe = ControlPipe<'d, T>;
type Bus = Bus<'d, T, P>;
type Bus = Bus<'d, T, V>;
fn alloc_endpoint_in(
&mut self,
ep_type: EndpointType,
packet_size: u16,
interval: u8,
interval_ms: u8,
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
let index = self.alloc_in.allocate(ep_type)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
let ep_addr = EndpointAddress::from_parts(index, Direction::In);
Ok(Endpoint::new(EndpointInfo {
addr: ep_addr,
ep_type,
max_packet_size: packet_size,
interval,
interval_ms,
}))
}
@ -257,24 +141,24 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
&mut self,
ep_type: EndpointType,
packet_size: u16,
interval: u8,
interval_ms: u8,
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
let index = self.alloc_out.allocate(ep_type)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
Ok(Endpoint::new(EndpointInfo {
addr: ep_addr,
ep_type,
max_packet_size: packet_size,
interval,
interval_ms,
}))
}
fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
(
Bus {
_p: unsafe { self._p.clone_unchecked() },
power_available: false,
usb_supply: self.usb_supply,
vbus_detect: self.vbus_detect,
},
ControlPipe {
_p: self._p,
@ -284,68 +168,60 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
}
}
pub struct Bus<'d, T: Instance, P: UsbSupply> {
/// USB bus.
pub struct Bus<'d, T: Instance, V: VbusDetect> {
_p: PeripheralRef<'d, T>,
power_available: bool,
usb_supply: P,
vbus_detect: V,
}
impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a;
type RemoteWakeupFuture<'a> = impl Future<Output = Result<(), Unsupported>> + 'a where Self: 'a;
impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> {
async fn enable(&mut self) {
let regs = T::regs();
fn enable(&mut self) -> Self::EnableFuture<'_> {
async move {
let regs = T::regs();
errata::pre_enable();
errata::pre_enable();
regs.enable.write(|w| w.enable().enabled());
regs.enable.write(|w| w.enable().enabled());
// Wait until the peripheral is ready.
regs.intenset.write(|w| w.usbevent().set_bit());
poll_fn(|cx| {
BUS_WAKER.register(cx.waker());
if regs.eventcause.read().ready().is_ready() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear.
errata::post_enable();
unsafe { NVIC::unmask(pac::Interrupt::USBD) };
regs.intenset.write(|w| {
w.usbreset().set_bit();
w.usbevent().set_bit();
w.epdata().set_bit();
w
});
if self.usb_supply.wait_power_ready().await.is_ok() {
// Enable the USB pullup, allowing enumeration.
regs.usbpullup.write(|w| w.connect().enabled());
trace!("enabled");
// Wait until the peripheral is ready.
regs.intenset.write(|w| w.usbevent().set_bit());
poll_fn(|cx| {
BUS_WAKER.register(cx.waker());
if regs.eventcause.read().ready().is_ready() {
Poll::Ready(())
} else {
trace!("usb power not ready due to usb removal");
Poll::Pending
}
})
.await;
regs.eventcause.write(|w| w.ready().clear_bit_by_one());
errata::post_enable();
unsafe { NVIC::unmask(pac::Interrupt::USBD) };
regs.intenset.write(|w| {
w.usbreset().set_bit();
w.usbevent().set_bit();
w.epdata().set_bit();
w
});
if self.vbus_detect.wait_power_ready().await.is_ok() {
// Enable the USB pullup, allowing enumeration.
regs.usbpullup.write(|w| w.connect().enabled());
trace!("enabled");
} else {
trace!("usb power not ready due to usb removal");
}
}
fn disable(&mut self) -> Self::DisableFuture<'_> {
async move {
let regs = T::regs();
regs.enable.write(|x| x.enable().disabled());
}
async fn disable(&mut self) {
let regs = T::regs();
regs.enable.write(|x| x.enable().disabled());
}
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> {
async fn poll(&mut self) -> Event {
poll_fn(move |cx| {
BUS_WAKER.register(cx.waker());
let regs = T::regs();
@ -369,28 +245,28 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
let r = regs.eventcause.read();
if r.isooutcrc().bit() {
regs.eventcause.write(|w| w.isooutcrc().set_bit());
regs.eventcause.write(|w| w.isooutcrc().detected());
trace!("USB event: isooutcrc");
}
if r.usbwuallowed().bit() {
regs.eventcause.write(|w| w.usbwuallowed().set_bit());
regs.eventcause.write(|w| w.usbwuallowed().allowed());
trace!("USB event: usbwuallowed");
}
if r.suspend().bit() {
regs.eventcause.write(|w| w.suspend().set_bit());
regs.eventcause.write(|w| w.suspend().detected());
regs.lowpower.write(|w| w.lowpower().low_power());
return Poll::Ready(Event::Suspend);
}
if r.resume().bit() {
regs.eventcause.write(|w| w.resume().set_bit());
regs.eventcause.write(|w| w.resume().detected());
return Poll::Ready(Event::Resume);
}
if r.ready().bit() {
regs.eventcause.write(|w| w.ready().set_bit());
regs.eventcause.write(|w| w.ready().ready());
trace!("USB event: ready");
}
if self.usb_supply.is_usb_detected() != self.power_available {
if self.vbus_detect.is_usb_detected() != self.power_available {
self.power_available = !self.power_available;
if self.power_available {
trace!("Power event: available");
@ -403,11 +279,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
Poll::Pending
})
}
#[inline]
fn set_address(&mut self, _addr: u8) {
// Nothing to do, the peripheral handles this.
.await
}
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
@ -429,8 +301,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
let regs = T::regs();
let i = ep_addr.index();
match ep_addr.direction() {
UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(),
UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(),
Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(),
Direction::In => regs.halted.epin[i].read().getstatus().is_halted(),
}
}
@ -443,7 +315,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled);
match ep_addr.direction() {
UsbDirection::In => {
Direction::In => {
let mut was_enabled = false;
regs.epinen.modify(|r, w| {
let mut bits = r.bits();
@ -467,7 +339,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
In::waker(i).wake();
}
UsbDirection::Out => {
Direction::Out => {
regs.epouten.modify(|r, w| {
let mut bits = r.bits();
if enabled {
@ -495,46 +367,47 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
}
#[inline]
fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> {
async move {
let regs = T::regs();
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
let regs = T::regs();
if regs.lowpower.read().lowpower().is_low_power() {
errata::pre_wakeup();
if regs.lowpower.read().lowpower().is_low_power() {
errata::pre_wakeup();
regs.lowpower.write(|w| w.lowpower().force_normal());
regs.lowpower.write(|w| w.lowpower().force_normal());
poll_fn(|cx| {
BUS_WAKER.register(cx.waker());
let regs = T::regs();
let r = regs.eventcause.read();
poll_fn(|cx| {
BUS_WAKER.register(cx.waker());
let regs = T::regs();
let r = regs.eventcause.read();
if regs.events_usbreset.read().bits() != 0 {
Poll::Ready(())
} else if r.resume().bit() {
Poll::Ready(())
} else if r.usbwuallowed().bit() {
regs.eventcause.write(|w| w.usbwuallowed().set_bit());
if regs.events_usbreset.read().bits() != 0 {
Poll::Ready(())
} else if r.resume().bit() {
Poll::Ready(())
} else if r.usbwuallowed().bit() {
regs.eventcause.write(|w| w.usbwuallowed().allowed());
regs.dpdmvalue.write(|w| w.state().resume());
regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit());
regs.dpdmvalue.write(|w| w.state().resume());
regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit());
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
errata::post_wakeup();
}
Ok(())
errata::post_wakeup();
}
Ok(())
}
}
/// Type-level marker for OUT endpoints.
pub enum Out {}
/// Type-level marker for IN endpoints.
pub enum In {}
trait EndpointDir {
@ -577,6 +450,7 @@ impl EndpointDir for Out {
}
}
/// USB endpoint.
pub struct Endpoint<'d, T: Instance, Dir> {
_phantom: PhantomData<(&'d mut T, Dir)>,
info: EndpointInfo,
@ -596,9 +470,7 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir
&self.info
}
type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
async fn wait_enabled(&mut self) {
let i = self.info.addr.index();
assert!(i != 0);
@ -610,6 +482,7 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir
Poll::Pending
}
})
.await
}
}
@ -714,173 +587,155 @@ unsafe fn write_dma<T: Instance>(i: usize, buf: &[u8]) {
}
impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
type ReadFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
let i = self.info.addr.index();
assert!(i != 0);
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
async move {
let i = self.info.addr.index();
assert!(i != 0);
self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?;
self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?;
unsafe { read_dma::<T>(i, buf) }
}
unsafe { read_dma::<T>(i, buf) }
}
}
impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
type WriteFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {
let i = self.info.addr.index();
assert!(i != 0);
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
async move {
let i = self.info.addr.index();
assert!(i != 0);
self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?;
self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?;
unsafe { write_dma::<T>(i, buf) }
unsafe { write_dma::<T>(i, buf) }
Ok(())
}
Ok(())
}
}
/// USB control pipe.
pub struct ControlPipe<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
max_packet_size: u16,
}
impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
type SetupFuture<'a> = impl Future<Output = [u8;8]> + 'a where Self: 'a;
type DataOutFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
type DataInFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
type AcceptFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
type RejectFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
fn max_packet_size(&self) -> usize {
usize::from(self.max_packet_size)
}
fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> {
async move {
async fn setup(&mut self) -> [u8; 8] {
let regs = T::regs();
// Reset shorts
regs.shorts.write(|w| w);
// Wait for SETUP packet
regs.intenset.write(|w| w.ep0setup().set());
poll_fn(|cx| {
EP0_WAKER.register(cx.waker());
let regs = T::regs();
if regs.events_ep0setup.read().bits() != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
// Reset shorts
regs.shorts.write(|w| w);
regs.events_ep0setup.reset();
// Wait for SETUP packet
regs.intenset.write(|w| w.ep0setup().set());
poll_fn(|cx| {
EP0_WAKER.register(cx.waker());
let regs = T::regs();
if regs.events_ep0setup.read().bits() != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
let mut buf = [0; 8];
buf[0] = regs.bmrequesttype.read().bits() as u8;
buf[1] = regs.brequest.read().brequest().bits();
buf[2] = regs.wvaluel.read().wvaluel().bits();
buf[3] = regs.wvalueh.read().wvalueh().bits();
buf[4] = regs.windexl.read().windexl().bits();
buf[5] = regs.windexh.read().windexh().bits();
buf[6] = regs.wlengthl.read().wlengthl().bits();
buf[7] = regs.wlengthh.read().wlengthh().bits();
regs.events_ep0setup.reset();
let mut buf = [0; 8];
buf[0] = regs.bmrequesttype.read().bits() as u8;
buf[1] = regs.brequest.read().brequest().bits();
buf[2] = regs.wvaluel.read().wvaluel().bits();
buf[3] = regs.wvalueh.read().wvalueh().bits();
buf[4] = regs.windexl.read().windexl().bits();
buf[5] = regs.windexh.read().windexh().bits();
buf[6] = regs.wlengthl.read().wlengthl().bits();
buf[7] = regs.wlengthh.read().wlengthh().bits();
buf
}
buf
}
fn data_out<'a>(&'a mut self, buf: &'a mut [u8], _first: bool, _last: bool) -> Self::DataOutFuture<'a> {
async move {
async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result<usize, EndpointError> {
let regs = T::regs();
regs.events_ep0datadone.reset();
// This starts a RX on EP0. events_ep0datadone notifies when done.
regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit());
// Wait until ready
regs.intenset.write(|w| {
w.usbreset().set();
w.ep0setup().set();
w.ep0datadone().set()
});
poll_fn(|cx| {
EP0_WAKER.register(cx.waker());
let regs = T::regs();
if regs.events_ep0datadone.read().bits() != 0 {
Poll::Ready(Ok(()))
} else if regs.events_usbreset.read().bits() != 0 {
trace!("aborted control data_out: usb reset");
Poll::Ready(Err(EndpointError::Disabled))
} else if regs.events_ep0setup.read().bits() != 0 {
trace!("aborted control data_out: received another SETUP");
Poll::Ready(Err(EndpointError::Disabled))
} else {
Poll::Pending
}
})
.await?;
regs.events_ep0datadone.reset();
// This starts a RX on EP0. events_ep0datadone notifies when done.
regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit());
// Wait until ready
regs.intenset.write(|w| {
w.usbreset().set();
w.ep0setup().set();
w.ep0datadone().set()
});
poll_fn(|cx| {
EP0_WAKER.register(cx.waker());
let regs = T::regs();
if regs.events_ep0datadone.read().bits() != 0 {
Poll::Ready(Ok(()))
} else if regs.events_usbreset.read().bits() != 0 {
trace!("aborted control data_out: usb reset");
Poll::Ready(Err(EndpointError::Disabled))
} else if regs.events_ep0setup.read().bits() != 0 {
trace!("aborted control data_out: received another SETUP");
Poll::Ready(Err(EndpointError::Disabled))
} else {
Poll::Pending
}
})
.await?;
unsafe { read_dma::<T>(0, buf) }
}
unsafe { read_dma::<T>(0, buf) }
}
fn data_in<'a>(&'a mut self, buf: &'a [u8], _first: bool, last: bool) -> Self::DataInFuture<'a> {
async move {
async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> {
let regs = T::regs();
regs.events_ep0datadone.reset();
regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last));
// This starts a TX on EP0. events_ep0datadone notifies when done.
unsafe { write_dma::<T>(0, buf) }
regs.intenset.write(|w| {
w.usbreset().set();
w.ep0setup().set();
w.ep0datadone().set()
});
poll_fn(|cx| {
cx.waker().wake_by_ref();
EP0_WAKER.register(cx.waker());
let regs = T::regs();
regs.events_ep0datadone.reset();
regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last));
// This starts a TX on EP0. events_ep0datadone notifies when done.
unsafe { write_dma::<T>(0, buf) }
regs.intenset.write(|w| {
w.usbreset().set();
w.ep0setup().set();
w.ep0datadone().set()
});
poll_fn(|cx| {
cx.waker().wake_by_ref();
EP0_WAKER.register(cx.waker());
let regs = T::regs();
if regs.events_ep0datadone.read().bits() != 0 {
Poll::Ready(Ok(()))
} else if regs.events_usbreset.read().bits() != 0 {
trace!("aborted control data_in: usb reset");
Poll::Ready(Err(EndpointError::Disabled))
} else if regs.events_ep0setup.read().bits() != 0 {
trace!("aborted control data_in: received another SETUP");
Poll::Ready(Err(EndpointError::Disabled))
} else {
Poll::Pending
}
})
.await
}
if regs.events_ep0datadone.read().bits() != 0 {
Poll::Ready(Ok(()))
} else if regs.events_usbreset.read().bits() != 0 {
trace!("aborted control data_in: usb reset");
Poll::Ready(Err(EndpointError::Disabled))
} else if regs.events_ep0setup.read().bits() != 0 {
trace!("aborted control data_in: received another SETUP");
Poll::Ready(Err(EndpointError::Disabled))
} else {
Poll::Pending
}
})
.await
}
fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> {
async move {
let regs = T::regs();
regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true));
}
async fn accept(&mut self) {
let regs = T::regs();
regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true));
}
fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> {
async move {
let regs = T::regs();
regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true));
}
async fn reject(&mut self) {
let regs = T::regs();
regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true));
}
async fn accept_set_address(&mut self, _addr: u8) {
self.accept().await;
// Nothing to do, the peripheral handles this.
}
}
@ -946,8 +801,10 @@ pub(crate) mod sealed {
}
}
/// USB peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
type Interrupt: Interrupt;
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_usb {
@ -958,7 +815,7 @@ macro_rules! impl_usb {
}
}
impl crate::usb::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq;
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -0,0 +1,177 @@
//! Trait and implementations for performing VBUS detection.
use core::future::poll_fn;
use core::sync::atomic::{AtomicBool, Ordering};
use core::task::Poll;
use embassy_sync::waitqueue::AtomicWaker;
use super::BUS_WAKER;
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, pac};
/// Trait for detecting USB VBUS power.
///
/// There are multiple ways to detect USB power. The behavior
/// here provides a hook into determining whether it is.
pub trait VbusDetect {
/// Report whether power is detected.
///
/// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the
/// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral.
fn is_usb_detected(&self) -> bool;
/// Wait until USB power is ready.
///
/// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the
/// `USBPWRRDY` event from the `POWER` peripheral.
async fn wait_power_ready(&mut self) -> Result<(), ()>;
}
#[cfg(not(feature = "_nrf5340"))]
type UsbRegIrq = interrupt::typelevel::POWER_CLOCK;
#[cfg(feature = "_nrf5340")]
type UsbRegIrq = interrupt::typelevel::USBREGULATOR;
#[cfg(not(feature = "_nrf5340"))]
type UsbRegPeri = pac::POWER;
#[cfg(feature = "_nrf5340")]
type UsbRegPeri = pac::USBREGULATOR;
/// Interrupt handler.
pub struct InterruptHandler {
_private: (),
}
impl interrupt::typelevel::Handler<UsbRegIrq> for InterruptHandler {
unsafe fn on_interrupt() {
let regs = unsafe { &*UsbRegPeri::ptr() };
if regs.events_usbdetected.read().bits() != 0 {
regs.events_usbdetected.reset();
BUS_WAKER.wake();
}
if regs.events_usbremoved.read().bits() != 0 {
regs.events_usbremoved.reset();
BUS_WAKER.wake();
POWER_WAKER.wake();
}
if regs.events_usbpwrrdy.read().bits() != 0 {
regs.events_usbpwrrdy.reset();
POWER_WAKER.wake();
}
}
}
/// [`VbusDetect`] implementation using the native hardware POWER peripheral.
///
/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces
/// to POWER. In that case, use [`VbusDetectSignal`].
pub struct HardwareVbusDetect {
_private: (),
}
static POWER_WAKER: AtomicWaker = AtomicWaker::new();
impl HardwareVbusDetect {
/// Create a new `VbusDetectNative`.
pub fn new(_irq: impl interrupt::typelevel::Binding<UsbRegIrq, InterruptHandler> + 'static) -> Self {
let regs = unsafe { &*UsbRegPeri::ptr() };
UsbRegIrq::unpend();
unsafe { UsbRegIrq::enable() };
regs.intenset
.write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set());
Self { _private: () }
}
}
impl VbusDetect for HardwareVbusDetect {
fn is_usb_detected(&self) -> bool {
let regs = unsafe { &*UsbRegPeri::ptr() };
regs.usbregstatus.read().vbusdetect().is_vbus_present()
}
async fn wait_power_ready(&mut self) -> Result<(), ()> {
poll_fn(move |cx| {
POWER_WAKER.register(cx.waker());
let regs = unsafe { &*UsbRegPeri::ptr() };
if regs.usbregstatus.read().outputrdy().is_ready() {
Poll::Ready(Ok(()))
} else if !self.is_usb_detected() {
Poll::Ready(Err(()))
} else {
Poll::Pending
}
})
.await
}
}
/// Software-backed [`VbusDetect`] implementation.
///
/// This implementation does not interact with the hardware, it allows user code
/// to notify the power events by calling functions instead.
///
/// This is suitable for use with the nRF softdevice, by calling the functions
/// when the softdevice reports power-related events.
pub struct SoftwareVbusDetect {
usb_detected: AtomicBool,
power_ready: AtomicBool,
}
impl SoftwareVbusDetect {
/// Create a new `SoftwareVbusDetect`.
pub fn new(usb_detected: bool, power_ready: bool) -> Self {
BUS_WAKER.wake();
Self {
usb_detected: AtomicBool::new(usb_detected),
power_ready: AtomicBool::new(power_ready),
}
}
/// Report whether power was detected.
///
/// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral.
pub fn detected(&self, detected: bool) {
self.usb_detected.store(detected, Ordering::Relaxed);
self.power_ready.store(false, Ordering::Relaxed);
BUS_WAKER.wake();
POWER_WAKER.wake();
}
/// Report when USB power is ready.
///
/// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral.
pub fn ready(&self) {
self.power_ready.store(true, Ordering::Relaxed);
POWER_WAKER.wake();
}
}
impl VbusDetect for &SoftwareVbusDetect {
fn is_usb_detected(&self) -> bool {
self.usb_detected.load(Ordering::Relaxed)
}
async fn wait_power_ready(&mut self) -> Result<(), ()> {
poll_fn(move |cx| {
POWER_WAKER.register(cx.waker());
if self.power_ready.load(Ordering::Relaxed) {
Poll::Ready(Ok(()))
} else if !self.usb_detected.load(Ordering::Relaxed) {
Poll::Ready(Err(()))
} else {
Poll::Pending
}
})
.await
}
}

View File

@ -1,4 +1,4 @@
//! HAL interface to the WDT peripheral.
//! Watchdog Timer (WDT) driver.
//!
//! This HAL implements a basic watchdog timer with 1..=8 handles.
//! Once the watchdog has been started, it cannot be stopped.
@ -8,6 +8,7 @@ use crate::peripherals;
const MIN_TICKS: u32 = 15;
/// WDT configuration.
#[non_exhaustive]
pub struct Config {
/// Number of 32768 Hz ticks in each watchdog period.
@ -23,6 +24,30 @@ pub struct Config {
pub run_during_debug_halt: bool,
}
impl Config {
/// Create a config structure from the current configuration of the WDT
/// peripheral.
pub fn try_new(_wdt: &peripherals::WDT) -> Option<Self> {
let r = unsafe { &*WDT::ptr() };
#[cfg(not(feature = "_nrf9160"))]
let runstatus = r.runstatus.read().runstatus().bit();
#[cfg(feature = "_nrf9160")]
let runstatus = r.runstatus.read().runstatuswdt().bit();
if runstatus {
let config = r.config.read();
Some(Self {
timeout_ticks: r.crv.read().bits(),
run_during_sleep: config.sleep().bit(),
run_during_debug_halt: config.halt().bit(),
})
} else {
None
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
@ -33,13 +58,13 @@ impl Default for Config {
}
}
/// An interface to the Watchdog.
/// Watchdog driver.
pub struct Watchdog {
_private: (),
}
impl Watchdog {
/// Try to create a new watchdog instance from the peripheral.
/// Try to create a new watchdog driver.
///
/// This function will return an error if the watchdog is already active
/// with a `config` different to the requested one, or a different number of
@ -131,6 +156,7 @@ impl Watchdog {
}
}
/// Watchdog handle.
pub struct WatchdogHandle {
index: u8,
}