diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-05-26 23:52:15 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-05-26 23:52:15 +0200 |
| commit | b515170e0a1f5a5c79401cd6508b9638232302b3 (patch) | |
| tree | 083769e7b4751b3ebc36adfba2e155afc5d9e714 | |
| parent | a126e17fb2d5544f1506aec860cc1b218008e9fd (diff) | |
| parent | 565c606ff8b216689df6551808ee57038b8939cd (diff) | |
Merge pull request #208 from embassy-rs/deconfigure-pins
nRF lowpower improvements
| -rw-r--r-- | embassy-nrf-examples/src/bin/qspi.rs | 2 | ||||
| -rw-r--r-- | embassy-nrf-examples/src/bin/qspi_lowpower.rs | 81 | ||||
| -rw-r--r-- | embassy-nrf-examples/src/bin/twim.rs | 35 | ||||
| -rw-r--r-- | embassy-nrf-examples/src/bin/twim_lowpower.rs | 54 | ||||
| -rw-r--r-- | embassy-nrf/src/gpio.rs | 13 | ||||
| -rw-r--r-- | embassy-nrf/src/qspi.rs | 140 | ||||
| -rw-r--r-- | embassy-nrf/src/spim.rs | 19 | ||||
| -rw-r--r-- | embassy-nrf/src/twim.rs | 19 | ||||
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 9 |
9 files changed, 310 insertions, 62 deletions
diff --git a/embassy-nrf-examples/src/bin/qspi.rs b/embassy-nrf-examples/src/bin/qspi.rs index 14f215187..1f33192e9 100644 --- a/embassy-nrf-examples/src/bin/qspi.rs +++ b/embassy-nrf-examples/src/bin/qspi.rs | |||
| @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 33 | 33 | ||
| 34 | let config = qspi::Config::default(); | 34 | let config = qspi::Config::default(); |
| 35 | let irq = interrupt::take!(QSPI); | 35 | let irq = interrupt::take!(QSPI); |
| 36 | let mut q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config); | 36 | let mut q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config).await; |
| 37 | 37 | ||
| 38 | let mut id = [1; 3]; | 38 | let mut id = [1; 3]; |
| 39 | q.custom_instruction(0x9F, &[], &mut id).await.unwrap(); | 39 | q.custom_instruction(0x9F, &[], &mut id).await.unwrap(); |
diff --git a/embassy-nrf-examples/src/bin/qspi_lowpower.rs b/embassy-nrf-examples/src/bin/qspi_lowpower.rs new file mode 100644 index 000000000..9bbc87caa --- /dev/null +++ b/embassy-nrf-examples/src/bin/qspi_lowpower.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(min_type_alias_impl_trait)] | ||
| 4 | #![feature(impl_trait_in_bindings)] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | #![allow(incomplete_features)] | ||
| 7 | |||
| 8 | #[path = "../example_common.rs"] | ||
| 9 | mod example_common; | ||
| 10 | |||
| 11 | use core::mem; | ||
| 12 | use defmt::panic; | ||
| 13 | use embassy::executor::Spawner; | ||
| 14 | use embassy::time::{Duration, Timer}; | ||
| 15 | use embassy::traits::flash::Flash; | ||
| 16 | use embassy_nrf::Peripherals; | ||
| 17 | use embassy_nrf::{interrupt, qspi}; | ||
| 18 | use example_common::*; | ||
| 19 | |||
| 20 | // Workaround for alignment requirements. | ||
| 21 | // Nicer API will probably come in the future. | ||
| 22 | #[repr(C, align(4))] | ||
| 23 | struct AlignedBuf([u8; 64]); | ||
| 24 | |||
| 25 | #[embassy::main] | ||
| 26 | async fn main(_spawner: Spawner, mut p: Peripherals) { | ||
| 27 | let mut irq = interrupt::take!(QSPI); | ||
| 28 | |||
| 29 | loop { | ||
| 30 | let mut config = qspi::Config::default(); | ||
| 31 | config.deep_power_down = Some(qspi::DeepPowerDownConfig { | ||
| 32 | enter_time: 3, // tDP = 30uS | ||
| 33 | exit_time: 3, // tRDP = 35uS | ||
| 34 | }); | ||
| 35 | |||
| 36 | let mut q = qspi::Qspi::new( | ||
| 37 | &mut p.QSPI, | ||
| 38 | &mut irq, | ||
| 39 | &mut p.P0_19, | ||
| 40 | &mut p.P0_17, | ||
| 41 | &mut p.P0_20, | ||
| 42 | &mut p.P0_21, | ||
| 43 | &mut p.P0_22, | ||
| 44 | &mut p.P0_23, | ||
| 45 | config, | ||
| 46 | ) | ||
| 47 | .await; | ||
| 48 | |||
| 49 | let mut id = [1; 3]; | ||
| 50 | q.custom_instruction(0x9F, &[], &mut id).await.unwrap(); | ||
| 51 | info!("id: {}", id); | ||
| 52 | |||
| 53 | // Read status register | ||
| 54 | let mut status = [4; 1]; | ||
| 55 | q.custom_instruction(0x05, &[], &mut status).await.unwrap(); | ||
| 56 | |||
| 57 | info!("status: {:?}", status[0]); | ||
| 58 | |||
| 59 | if status[0] & 0x40 == 0 { | ||
| 60 | status[0] |= 0x40; | ||
| 61 | |||
| 62 | q.custom_instruction(0x01, &status, &mut []).await.unwrap(); | ||
| 63 | |||
| 64 | info!("enabled quad in status"); | ||
| 65 | } | ||
| 66 | |||
| 67 | let mut buf = AlignedBuf([0u8; 64]); | ||
| 68 | |||
| 69 | info!("reading..."); | ||
| 70 | q.read(0, &mut buf.0).await.unwrap(); | ||
| 71 | info!("read: {=[u8]:x}", buf.0); | ||
| 72 | |||
| 73 | // Drop the QSPI instance. This disables the peripehral and deconfigures the pins. | ||
| 74 | // This clears the borrow on the singletons, so they can now be used again. | ||
| 75 | mem::drop(q); | ||
| 76 | |||
| 77 | // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do. | ||
| 78 | // During this sleep, the nRF chip should only use ~3uA | ||
| 79 | Timer::after(Duration::from_secs(1)).await; | ||
| 80 | } | ||
| 81 | } | ||
diff --git a/embassy-nrf-examples/src/bin/twim.rs b/embassy-nrf-examples/src/bin/twim.rs new file mode 100644 index 000000000..537cea160 --- /dev/null +++ b/embassy-nrf-examples/src/bin/twim.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | //! Example on how to read a 24C/24LC i2c eeprom. | ||
| 2 | //! | ||
| 3 | //! Connect SDA to P0.03, SCL to P0.04 | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(min_type_alias_impl_trait)] | ||
| 8 | #![feature(impl_trait_in_bindings)] | ||
| 9 | #![feature(type_alias_impl_trait)] | ||
| 10 | #![allow(incomplete_features)] | ||
| 11 | |||
| 12 | #[path = "../example_common.rs"] | ||
| 13 | mod example_common; | ||
| 14 | |||
| 15 | use defmt::{panic, *}; | ||
| 16 | use embassy::executor::Spawner; | ||
| 17 | use embassy_nrf::twim::{self, Twim}; | ||
| 18 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 19 | |||
| 20 | const ADDRESS: u8 = 0x50; | ||
| 21 | |||
| 22 | #[embassy::main] | ||
| 23 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 24 | info!("Initializing TWI..."); | ||
| 25 | let config = twim::Config::default(); | ||
| 26 | let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 27 | let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); | ||
| 28 | |||
| 29 | info!("Reading..."); | ||
| 30 | |||
| 31 | let mut buf = [0u8; 16]; | ||
| 32 | twi.write_then_read(ADDRESS, &mut [0x00], &mut buf).unwrap(); | ||
| 33 | |||
| 34 | info!("Read: {=[u8]:x}", buf); | ||
| 35 | } | ||
diff --git a/embassy-nrf-examples/src/bin/twim_lowpower.rs b/embassy-nrf-examples/src/bin/twim_lowpower.rs new file mode 100644 index 000000000..1cd66a18e --- /dev/null +++ b/embassy-nrf-examples/src/bin/twim_lowpower.rs | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | //! Example on how to read a 24C/24LC i2c eeprom with low power consumption. | ||
| 2 | //! The eeprom is read every 1 second, while ensuring lowest possible power while | ||
| 3 | //! sleeping between reads. | ||
| 4 | //! | ||
| 5 | //! Connect SDA to P0.03, SCL to P0.04 | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | #![feature(min_type_alias_impl_trait)] | ||
| 10 | #![feature(impl_trait_in_bindings)] | ||
| 11 | #![feature(type_alias_impl_trait)] | ||
| 12 | #![allow(incomplete_features)] | ||
| 13 | |||
| 14 | #[path = "../example_common.rs"] | ||
| 15 | mod example_common; | ||
| 16 | |||
| 17 | use core::mem; | ||
| 18 | |||
| 19 | use defmt::{panic, *}; | ||
| 20 | use embassy::executor::Spawner; | ||
| 21 | use embassy::time::{Duration, Timer}; | ||
| 22 | use embassy_nrf::twim::{self, Twim}; | ||
| 23 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 24 | |||
| 25 | const ADDRESS: u8 = 0x50; | ||
| 26 | |||
| 27 | #[embassy::main] | ||
| 28 | async fn main(_spawner: Spawner, mut p: Peripherals) { | ||
| 29 | info!("Started!"); | ||
| 30 | let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 31 | |||
| 32 | loop { | ||
| 33 | info!("Initializing TWI..."); | ||
| 34 | let config = twim::Config::default(); | ||
| 35 | |||
| 36 | // Create the TWIM instance with borrowed singletons, so they're not consumed. | ||
| 37 | let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); | ||
| 38 | |||
| 39 | info!("Reading..."); | ||
| 40 | |||
| 41 | let mut buf = [0u8; 16]; | ||
| 42 | twi.write_then_read(ADDRESS, &mut [0x00], &mut buf).unwrap(); | ||
| 43 | |||
| 44 | info!("Read: {=[u8]:x}", buf); | ||
| 45 | |||
| 46 | // Drop the TWIM instance. This disables the peripehral and deconfigures the pins. | ||
| 47 | // This clears the borrow on the singletons, so they can now be used again. | ||
| 48 | mem::drop(twi); | ||
| 49 | |||
| 50 | // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do. | ||
| 51 | // During this sleep, the nRF chip should only use ~3uA | ||
| 52 | Timer::after(Duration::from_secs(1)).await; | ||
| 53 | } | ||
| 54 | } | ||
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 3ae160ca8..b02e77874 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs | |||
| @@ -12,6 +12,8 @@ use gpio::pin_cnf::DRIVE_A; | |||
| 12 | use crate::pac; | 12 | use crate::pac; |
| 13 | use crate::pac::p0 as gpio; | 13 | use crate::pac::p0 as gpio; |
| 14 | 14 | ||
| 15 | use self::sealed::Pin as _; | ||
| 16 | |||
| 15 | /// A GPIO port with up to 32 pins. | 17 | /// A GPIO port with up to 32 pins. |
| 16 | #[derive(Debug, Eq, PartialEq)] | 18 | #[derive(Debug, Eq, PartialEq)] |
| 17 | pub enum Port { | 19 | pub enum Port { |
| @@ -487,6 +489,17 @@ impl OptionalPin for NoPin { | |||
| 487 | 489 | ||
| 488 | // ==================== | 490 | // ==================== |
| 489 | 491 | ||
| 492 | pub(crate) fn deconfigure_pin(psel_bits: u32) { | ||
| 493 | if psel_bits & 0x8000_0000 != 0 { | ||
| 494 | return; | ||
| 495 | } | ||
| 496 | unsafe { | ||
| 497 | AnyPin::steal(psel_bits as _).conf().reset(); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | // ==================== | ||
| 502 | |||
| 490 | macro_rules! impl_pin { | 503 | macro_rules! impl_pin { |
| 491 | ($type:ident, $port_num:expr, $pin_num:expr) => { | 504 | ($type:ident, $port_num:expr, $pin_num:expr) => { |
| 492 | impl crate::gpio::Pin for peripherals::$type {} | 505 | impl crate::gpio::Pin for peripherals::$type {} |
diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index e5d21ceaf..6cabe1168 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | use core::future::Future; | 3 | use core::future::Future; |
| 4 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::ptr; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | use embassy::interrupt::{Interrupt, InterruptExt}; | 7 | use embassy::interrupt::{Interrupt, InterruptExt}; |
| 7 | use embassy::traits::flash::{Error, Flash}; | 8 | use embassy::traits::flash::{Error, Flash}; |
| @@ -10,7 +11,8 @@ use embassy_extras::unborrow; | |||
| 10 | use futures::future::poll_fn; | 11 | use futures::future::poll_fn; |
| 11 | 12 | ||
| 12 | use crate::fmt::{assert, assert_eq, *}; | 13 | use crate::fmt::{assert, assert_eq, *}; |
| 13 | use crate::gpio::Pin as GpioPin; | 14 | use crate::gpio::sealed::Pin as _; |
| 15 | use crate::gpio::{self, Pin as GpioPin}; | ||
| 14 | use crate::pac; | 16 | use crate::pac; |
| 15 | 17 | ||
| 16 | pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; | 18 | pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; |
| @@ -29,7 +31,9 @@ pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode; | |||
| 29 | // - set gpio in high drive | 31 | // - set gpio in high drive |
| 30 | 32 | ||
| 31 | pub struct DeepPowerDownConfig { | 33 | pub struct DeepPowerDownConfig { |
| 34 | /// Time required for entering DPM, in units of 16us | ||
| 32 | pub enter_time: u16, | 35 | pub enter_time: u16, |
| 36 | /// Time required for exiting DPM, in units of 16us | ||
| 33 | pub exit_time: u16, | 37 | pub exit_time: u16, |
| 34 | } | 38 | } |
| 35 | 39 | ||
| @@ -55,11 +59,12 @@ impl Default for Config { | |||
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | pub struct Qspi<'d, T: Instance> { | 61 | pub struct Qspi<'d, T: Instance> { |
| 62 | dpm_enabled: bool, | ||
| 58 | phantom: PhantomData<&'d mut T>, | 63 | phantom: PhantomData<&'d mut T>, |
| 59 | } | 64 | } |
| 60 | 65 | ||
| 61 | impl<'d, T: Instance> Qspi<'d, T> { | 66 | impl<'d, T: Instance> Qspi<'d, T> { |
| 62 | pub fn new( | 67 | pub async fn new( |
| 63 | _qspi: impl Unborrow<Target = T> + 'd, | 68 | _qspi: impl Unborrow<Target = T> + 'd, |
| 64 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | 69 | irq: impl Unborrow<Target = T::Interrupt> + 'd, |
| 65 | sck: impl Unborrow<Target = impl GpioPin> + 'd, | 70 | sck: impl Unborrow<Target = impl GpioPin> + 'd, |
| @@ -69,20 +74,21 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 69 | io2: impl Unborrow<Target = impl GpioPin> + 'd, | 74 | io2: impl Unborrow<Target = impl GpioPin> + 'd, |
| 70 | io3: impl Unborrow<Target = impl GpioPin> + 'd, | 75 | io3: impl Unborrow<Target = impl GpioPin> + 'd, |
| 71 | config: Config, | 76 | config: Config, |
| 72 | ) -> Self { | 77 | ) -> Qspi<'d, T> { |
| 73 | unborrow!(irq, sck, csn, io0, io1, io2, io3); | 78 | unborrow!(irq, sck, csn, io0, io1, io2, io3); |
| 74 | 79 | ||
| 75 | let r = T::regs(); | 80 | let r = T::regs(); |
| 76 | 81 | ||
| 77 | for cnf in &[ | 82 | let sck = sck.degrade(); |
| 78 | sck.conf(), | 83 | let csn = csn.degrade(); |
| 79 | csn.conf(), | 84 | let io0 = io0.degrade(); |
| 80 | io0.conf(), | 85 | let io1 = io1.degrade(); |
| 81 | io1.conf(), | 86 | let io2 = io2.degrade(); |
| 82 | io2.conf(), | 87 | let io3 = io3.degrade(); |
| 83 | io3.conf(), | 88 | |
| 84 | ] { | 89 | for pin in [&sck, &csn, &io0, &io1, &io2, &io3] { |
| 85 | cnf.write(|w| w.dir().output().drive().h0h1()); | 90 | pin.set_high(); |
| 91 | pin.conf().write(|w| w.dir().output().drive().h0h1()); | ||
| 86 | } | 92 | } |
| 87 | 93 | ||
| 88 | r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); | 94 | r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); |
| @@ -92,53 +98,56 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 92 | r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) }); | 98 | r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) }); |
| 93 | r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) }); | 99 | r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) }); |
| 94 | 100 | ||
| 95 | r.ifconfig0.write(|mut w| { | 101 | r.ifconfig0.write(|w| { |
| 96 | w = w.addrmode().variant(AddressMode::_24BIT); | 102 | w.addrmode().variant(AddressMode::_24BIT); |
| 97 | if config.deep_power_down.is_some() { | 103 | w.dpmenable().bit(config.deep_power_down.is_some()); |
| 98 | w = w.dpmenable().enable(); | 104 | w.ppsize().variant(config.write_page_size); |
| 99 | } else { | 105 | w.readoc().variant(config.read_opcode); |
| 100 | w = w.dpmenable().disable(); | 106 | w.writeoc().variant(config.write_opcode); |
| 101 | } | ||
| 102 | w = w.ppsize().variant(config.write_page_size); | ||
| 103 | w = w.readoc().variant(config.read_opcode); | ||
| 104 | w = w.writeoc().variant(config.write_opcode); | ||
| 105 | w | 107 | w |
| 106 | }); | 108 | }); |
| 107 | 109 | ||
| 108 | if let Some(dpd) = &config.deep_power_down { | 110 | if let Some(dpd) = &config.deep_power_down { |
| 109 | r.dpmdur.write(|mut w| unsafe { | 111 | r.dpmdur.write(|w| unsafe { |
| 110 | w = w.enter().bits(dpd.enter_time); | 112 | w.enter().bits(dpd.enter_time); |
| 111 | w = w.exit().bits(dpd.exit_time); | 113 | w.exit().bits(dpd.exit_time); |
| 112 | w | 114 | w |
| 113 | }) | 115 | }) |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | r.ifconfig1.write(|w| { | 118 | r.ifconfig1.write(|w| unsafe { |
| 117 | let w = unsafe { w.sckdelay().bits(80) }; | 119 | w.sckdelay().bits(80); |
| 118 | let w = w.dpmen().exit(); | 120 | w.dpmen().exit(); |
| 119 | let w = w.spimode().mode0(); | 121 | w.spimode().mode0(); |
| 120 | let w = unsafe { w.sckfreq().bits(3) }; | 122 | w.sckfreq().bits(3); |
| 123 | w | ||
| 124 | }); | ||
| 125 | |||
| 126 | r.xipoffset.write(|w| unsafe { | ||
| 127 | w.xipoffset().bits(config.xip_offset); | ||
| 121 | w | 128 | w |
| 122 | }); | 129 | }); |
| 123 | 130 | ||
| 124 | r.xipoffset | 131 | irq.set_handler(Self::on_interrupt); |
| 125 | .write(|w| unsafe { w.xipoffset().bits(config.xip_offset) }); | 132 | irq.unpend(); |
| 133 | irq.enable(); | ||
| 126 | 134 | ||
| 127 | // Enable it | 135 | // Enable it |
| 128 | r.enable.write(|w| w.enable().enabled()); | 136 | r.enable.write(|w| w.enable().enabled()); |
| 129 | 137 | ||
| 138 | let mut res = Self { | ||
| 139 | dpm_enabled: config.deep_power_down.is_some(), | ||
| 140 | phantom: PhantomData, | ||
| 141 | }; | ||
| 142 | |||
| 130 | r.events_ready.reset(); | 143 | r.events_ready.reset(); |
| 144 | r.intenset.write(|w| w.ready().set()); | ||
| 145 | |||
| 131 | r.tasks_activate.write(|w| w.tasks_activate().bit(true)); | 146 | r.tasks_activate.write(|w| w.tasks_activate().bit(true)); |
| 132 | while r.events_ready.read().bits() == 0 {} | ||
| 133 | r.events_ready.reset(); | ||
| 134 | 147 | ||
| 135 | irq.set_handler(Self::on_interrupt); | 148 | res.wait_ready().await; |
| 136 | irq.unpend(); | ||
| 137 | irq.enable(); | ||
| 138 | 149 | ||
| 139 | Self { | 150 | res |
| 140 | phantom: PhantomData, | ||
| 141 | } | ||
| 142 | } | 151 | } |
| 143 | 152 | ||
| 144 | fn on_interrupt(_: *mut ()) { | 153 | fn on_interrupt(_: *mut ()) { |
| @@ -151,19 +160,6 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 151 | } | 160 | } |
| 152 | } | 161 | } |
| 153 | 162 | ||
| 154 | pub fn sleep(&mut self) { | ||
| 155 | let r = T::regs(); | ||
| 156 | |||
| 157 | info!("flash: sleeping"); | ||
| 158 | info!("flash: state = {:?}", r.status.read().bits()); | ||
| 159 | r.ifconfig1.modify(|_, w| w.dpmen().enter()); | ||
| 160 | info!("flash: state = {:?}", r.status.read().bits()); | ||
| 161 | cortex_m::asm::delay(1000000); | ||
| 162 | info!("flash: state = {:?}", r.status.read().bits()); | ||
| 163 | |||
| 164 | r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit()); | ||
| 165 | } | ||
| 166 | |||
| 167 | pub async fn custom_instruction( | 163 | pub async fn custom_instruction( |
| 168 | &mut self, | 164 | &mut self, |
| 169 | opcode: u8, | 165 | opcode: u8, |
| @@ -246,6 +242,44 @@ impl<'d, T: Instance> Qspi<'d, T> { | |||
| 246 | } | 242 | } |
| 247 | } | 243 | } |
| 248 | 244 | ||
| 245 | impl<'d, T: Instance> Drop for Qspi<'d, T> { | ||
| 246 | fn drop(&mut self) { | ||
| 247 | let r = T::regs(); | ||
| 248 | |||
| 249 | if self.dpm_enabled { | ||
| 250 | info!("qspi: doing deep powerdown..."); | ||
| 251 | |||
| 252 | r.ifconfig1.modify(|_, w| w.dpmen().enter()); | ||
| 253 | |||
| 254 | // Wait for DPM enter. | ||
| 255 | // Unfortunately we must spin. There's no way to do this interrupt-driven. | ||
| 256 | // The READY event does NOT fire on DPM enter (but it does fire on DPM exit :shrug:) | ||
| 257 | while r.status.read().dpm().is_disabled() {} | ||
| 258 | } | ||
| 259 | |||
| 260 | // it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it. | ||
| 261 | r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit()); | ||
| 262 | |||
| 263 | // Workaround https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev1/ERR/nRF52840/Rev1/latest/anomaly_840_122.html?cp=4_0_1_2_1_7 | ||
| 264 | // Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate, | ||
| 265 | // so we only do the second one here. | ||
| 266 | unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) } | ||
| 267 | |||
| 268 | r.enable.write(|w| w.enable().disabled()); | ||
| 269 | |||
| 270 | // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, | ||
| 271 | // leaving it floating, the flash chip might read it as zero which would cause it to | ||
| 272 | // spuriously exit DPM. | ||
| 273 | gpio::deconfigure_pin(r.psel.sck.read().bits()); | ||
| 274 | gpio::deconfigure_pin(r.psel.io0.read().bits()); | ||
| 275 | gpio::deconfigure_pin(r.psel.io1.read().bits()); | ||
| 276 | gpio::deconfigure_pin(r.psel.io2.read().bits()); | ||
| 277 | gpio::deconfigure_pin(r.psel.io3.read().bits()); | ||
| 278 | |||
| 279 | info!("qspi: dropped"); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 249 | impl<'d, T: Instance> Flash for Qspi<'d, T> { | 283 | impl<'d, T: Instance> Flash for Qspi<'d, T> { |
| 250 | #[rustfmt::skip] | 284 | #[rustfmt::skip] |
| 251 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a; | 285 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a; |
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index bb43b7c7c..a29c1a3ee 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs | |||
| @@ -14,6 +14,7 @@ use traits::spi::FullDuplex; | |||
| 14 | use crate::gpio::sealed::Pin as _; | 14 | use crate::gpio::sealed::Pin as _; |
| 15 | use crate::gpio::{OptionalPin, Pin as GpioPin}; | 15 | use crate::gpio::{OptionalPin, Pin as GpioPin}; |
| 16 | use crate::interrupt::Interrupt; | 16 | use crate::interrupt::Interrupt; |
| 17 | use crate::{fmt::*, gpio}; | ||
| 17 | use crate::{pac, util::slice_in_ram_or}; | 18 | use crate::{pac, util::slice_in_ram_or}; |
| 18 | 19 | ||
| 19 | pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 20 | pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; |
| @@ -153,6 +154,24 @@ impl<'d, T: Instance> Spim<'d, T> { | |||
| 153 | } | 154 | } |
| 154 | } | 155 | } |
| 155 | 156 | ||
| 157 | impl<'d, T: Instance> Drop for Spim<'d, T> { | ||
| 158 | fn drop(&mut self) { | ||
| 159 | info!("spim drop"); | ||
| 160 | |||
| 161 | // TODO check for abort, wait for xxxstopped | ||
| 162 | |||
| 163 | // disable! | ||
| 164 | let r = T::regs(); | ||
| 165 | r.enable.write(|w| w.enable().disabled()); | ||
| 166 | |||
| 167 | gpio::deconfigure_pin(r.psel.sck.read().bits()); | ||
| 168 | gpio::deconfigure_pin(r.psel.miso.read().bits()); | ||
| 169 | gpio::deconfigure_pin(r.psel.mosi.read().bits()); | ||
| 170 | |||
| 171 | info!("spim drop: done"); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 156 | impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> { | 175 | impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> { |
| 157 | type Error = Error; | 176 | type Error = Error; |
| 158 | 177 | ||
diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 05c4c260f..ea3ac7553 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs | |||
| @@ -13,10 +13,10 @@ use embassy::util::{AtomicWaker, Unborrow}; | |||
| 13 | use embassy_extras::unborrow; | 13 | use embassy_extras::unborrow; |
| 14 | 14 | ||
| 15 | use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; | 15 | use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; |
| 16 | use crate::fmt::*; | ||
| 17 | use crate::gpio::Pin as GpioPin; | 16 | use crate::gpio::Pin as GpioPin; |
| 18 | use crate::pac; | 17 | use crate::pac; |
| 19 | use crate::util::{slice_in_ram, slice_in_ram_or}; | 18 | use crate::util::{slice_in_ram, slice_in_ram_or}; |
| 19 | use crate::{fmt::*, gpio}; | ||
| 20 | 20 | ||
| 21 | pub enum Frequency { | 21 | pub enum Frequency { |
| 22 | #[doc = "26738688: 100 kbps"] | 22 | #[doc = "26738688: 100 kbps"] |
| @@ -30,12 +30,16 @@ pub enum Frequency { | |||
| 30 | #[non_exhaustive] | 30 | #[non_exhaustive] |
| 31 | pub struct Config { | 31 | pub struct Config { |
| 32 | pub frequency: Frequency, | 32 | pub frequency: Frequency, |
| 33 | pub sda_pullup: bool, | ||
| 34 | pub scl_pullup: bool, | ||
| 33 | } | 35 | } |
| 34 | 36 | ||
| 35 | impl Default for Config { | 37 | impl Default for Config { |
| 36 | fn default() -> Self { | 38 | fn default() -> Self { |
| 37 | Self { | 39 | Self { |
| 38 | frequency: Frequency::K100, | 40 | frequency: Frequency::K100, |
| 41 | sda_pullup: false, | ||
| 42 | scl_pullup: false, | ||
| 39 | } | 43 | } |
| 40 | } | 44 | } |
| 41 | } | 45 | } |
| @@ -61,15 +65,19 @@ impl<'d, T: Instance> Twim<'d, T> { | |||
| 61 | sda.conf().write(|w| { | 65 | sda.conf().write(|w| { |
| 62 | w.dir().input(); | 66 | w.dir().input(); |
| 63 | w.input().connect(); | 67 | w.input().connect(); |
| 64 | w.pull().pullup(); | ||
| 65 | w.drive().s0d1(); | 68 | w.drive().s0d1(); |
| 69 | if config.sda_pullup { | ||
| 70 | w.pull().pullup(); | ||
| 71 | } | ||
| 66 | w | 72 | w |
| 67 | }); | 73 | }); |
| 68 | scl.conf().write(|w| { | 74 | scl.conf().write(|w| { |
| 69 | w.dir().input(); | 75 | w.dir().input(); |
| 70 | w.input().connect(); | 76 | w.input().connect(); |
| 71 | w.pull().pullup(); | ||
| 72 | w.drive().s0d1(); | 77 | w.drive().s0d1(); |
| 78 | if config.scl_pullup { | ||
| 79 | w.pull().pullup(); | ||
| 80 | } | ||
| 73 | w | 81 | w |
| 74 | }); | 82 | }); |
| 75 | 83 | ||
| @@ -422,9 +430,10 @@ impl<'a, T: Instance> Drop for Twim<'a, T> { | |||
| 422 | let r = T::regs(); | 430 | let r = T::regs(); |
| 423 | r.enable.write(|w| w.enable().disabled()); | 431 | r.enable.write(|w| w.enable().disabled()); |
| 424 | 432 | ||
| 425 | info!("uarte drop: done"); | 433 | gpio::deconfigure_pin(r.psel.sda.read().bits()); |
| 434 | gpio::deconfigure_pin(r.psel.scl.read().bits()); | ||
| 426 | 435 | ||
| 427 | // TODO: disable pins | 436 | info!("twim drop: done"); |
| 428 | } | 437 | } |
| 429 | } | 438 | } |
| 430 | 439 | ||
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 3ad7a787d..2f6d1a391 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -15,7 +15,7 @@ use futures::future::poll_fn; | |||
| 15 | use crate::chip::EASY_DMA_SIZE; | 15 | use crate::chip::EASY_DMA_SIZE; |
| 16 | use crate::fmt::{assert, panic, *}; | 16 | use crate::fmt::{assert, panic, *}; |
| 17 | use crate::gpio::sealed::Pin as _; | 17 | use crate::gpio::sealed::Pin as _; |
| 18 | use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin}; | 18 | use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin}; |
| 19 | use crate::interrupt::Interrupt; | 19 | use crate::interrupt::Interrupt; |
| 20 | use crate::pac; | 20 | use crate::pac; |
| 21 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; | 21 | use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; |
| @@ -166,9 +166,12 @@ impl<'a, T: Instance> Drop for Uarte<'a, T> { | |||
| 166 | // Finally we can disable! | 166 | // Finally we can disable! |
| 167 | r.enable.write(|w| w.enable().disabled()); | 167 | r.enable.write(|w| w.enable().disabled()); |
| 168 | 168 | ||
| 169 | info!("uarte drop: done"); | 169 | gpio::deconfigure_pin(r.psel.rxd.read().bits()); |
| 170 | gpio::deconfigure_pin(r.psel.txd.read().bits()); | ||
| 171 | gpio::deconfigure_pin(r.psel.rts.read().bits()); | ||
| 172 | gpio::deconfigure_pin(r.psel.cts.read().bits()); | ||
| 170 | 173 | ||
| 171 | // TODO: disable pins | 174 | info!("uarte drop: done"); |
| 172 | } | 175 | } |
| 173 | } | 176 | } |
| 174 | 177 | ||
