Merge remote-tracking branch 'origin/main' into nrf-pdm
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
1194
embassy-nrf/src/i2s.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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(®s().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(®s().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)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
550
embassy-nrf/src/spis.rs
Normal 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));
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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])
|
||||
}
|
||||
|
||||
|
@ -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
799
embassy-nrf/src/twis.rs
Normal 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
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
177
embassy-nrf/src/usb/vbus_detect.rs
Normal file
177
embassy-nrf/src/usb/vbus_detect.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
Reference in New Issue
Block a user