Merge pull request #208 from embassy-rs/deconfigure-pins
nRF lowpower improvements
This commit is contained in:
commit
b515170e0a
9 changed files with 312 additions and 64 deletions
|
@ -33,7 +33,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
|
||||||
let config = qspi::Config::default();
|
let config = qspi::Config::default();
|
||||||
let irq = interrupt::take!(QSPI);
|
let irq = interrupt::take!(QSPI);
|
||||||
let mut q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config);
|
let mut q = qspi::Qspi::new(p.QSPI, irq, sck, csn, io0, io1, io2, io3, config).await;
|
||||||
|
|
||||||
let mut id = [1; 3];
|
let mut id = [1; 3];
|
||||||
q.custom_instruction(0x9F, &[], &mut id).await.unwrap();
|
q.custom_instruction(0x9F, &[], &mut id).await.unwrap();
|
||||||
|
|
81
embassy-nrf-examples/src/bin/qspi_lowpower.rs
Normal file
81
embassy-nrf-examples/src/bin/qspi_lowpower.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(min_type_alias_impl_trait)]
|
||||||
|
#![feature(impl_trait_in_bindings)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
use defmt::panic;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy::traits::flash::Flash;
|
||||||
|
use embassy_nrf::Peripherals;
|
||||||
|
use embassy_nrf::{interrupt, qspi};
|
||||||
|
use example_common::*;
|
||||||
|
|
||||||
|
// Workaround for alignment requirements.
|
||||||
|
// Nicer API will probably come in the future.
|
||||||
|
#[repr(C, align(4))]
|
||||||
|
struct AlignedBuf([u8; 64]);
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, mut p: Peripherals) {
|
||||||
|
let mut irq = interrupt::take!(QSPI);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut config = qspi::Config::default();
|
||||||
|
config.deep_power_down = Some(qspi::DeepPowerDownConfig {
|
||||||
|
enter_time: 3, // tDP = 30uS
|
||||||
|
exit_time: 3, // tRDP = 35uS
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut q = qspi::Qspi::new(
|
||||||
|
&mut p.QSPI,
|
||||||
|
&mut irq,
|
||||||
|
&mut p.P0_19,
|
||||||
|
&mut p.P0_17,
|
||||||
|
&mut p.P0_20,
|
||||||
|
&mut p.P0_21,
|
||||||
|
&mut p.P0_22,
|
||||||
|
&mut p.P0_23,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut id = [1; 3];
|
||||||
|
q.custom_instruction(0x9F, &[], &mut id).await.unwrap();
|
||||||
|
info!("id: {}", id);
|
||||||
|
|
||||||
|
// Read status register
|
||||||
|
let mut status = [4; 1];
|
||||||
|
q.custom_instruction(0x05, &[], &mut status).await.unwrap();
|
||||||
|
|
||||||
|
info!("status: {:?}", status[0]);
|
||||||
|
|
||||||
|
if status[0] & 0x40 == 0 {
|
||||||
|
status[0] |= 0x40;
|
||||||
|
|
||||||
|
q.custom_instruction(0x01, &status, &mut []).await.unwrap();
|
||||||
|
|
||||||
|
info!("enabled quad in status");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = AlignedBuf([0u8; 64]);
|
||||||
|
|
||||||
|
info!("reading...");
|
||||||
|
q.read(0, &mut buf.0).await.unwrap();
|
||||||
|
info!("read: {=[u8]:x}", buf.0);
|
||||||
|
|
||||||
|
// Drop the QSPI instance. This disables the peripehral and deconfigures the pins.
|
||||||
|
// This clears the borrow on the singletons, so they can now be used again.
|
||||||
|
mem::drop(q);
|
||||||
|
|
||||||
|
// Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do.
|
||||||
|
// During this sleep, the nRF chip should only use ~3uA
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
35
embassy-nrf-examples/src/bin/twim.rs
Normal file
35
embassy-nrf-examples/src/bin/twim.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//! Example on how to read a 24C/24LC i2c eeprom.
|
||||||
|
//!
|
||||||
|
//! Connect SDA to P0.03, SCL to P0.04
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(min_type_alias_impl_trait)]
|
||||||
|
#![feature(impl_trait_in_bindings)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use defmt::{panic, *};
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_nrf::twim::{self, Twim};
|
||||||
|
use embassy_nrf::{interrupt, Peripherals};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 0x50;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Initializing TWI...");
|
||||||
|
let config = twim::Config::default();
|
||||||
|
let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
|
||||||
|
let mut buf = [0u8; 16];
|
||||||
|
twi.write_then_read(ADDRESS, &mut [0x00], &mut buf).unwrap();
|
||||||
|
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
}
|
54
embassy-nrf-examples/src/bin/twim_lowpower.rs
Normal file
54
embassy-nrf-examples/src/bin/twim_lowpower.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//! Example on how to read a 24C/24LC i2c eeprom with low power consumption.
|
||||||
|
//! The eeprom is read every 1 second, while ensuring lowest possible power while
|
||||||
|
//! sleeping between reads.
|
||||||
|
//!
|
||||||
|
//! Connect SDA to P0.03, SCL to P0.04
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(min_type_alias_impl_trait)]
|
||||||
|
#![feature(impl_trait_in_bindings)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use defmt::{panic, *};
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_nrf::twim::{self, Twim};
|
||||||
|
use embassy_nrf::{interrupt, Peripherals};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 0x50;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, mut p: Peripherals) {
|
||||||
|
info!("Started!");
|
||||||
|
let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("Initializing TWI...");
|
||||||
|
let config = twim::Config::default();
|
||||||
|
|
||||||
|
// Create the TWIM instance with borrowed singletons, so they're not consumed.
|
||||||
|
let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
|
||||||
|
let mut buf = [0u8; 16];
|
||||||
|
twi.write_then_read(ADDRESS, &mut [0x00], &mut buf).unwrap();
|
||||||
|
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
// Drop the TWIM instance. This disables the peripehral and deconfigures the pins.
|
||||||
|
// This clears the borrow on the singletons, so they can now be used again.
|
||||||
|
mem::drop(twi);
|
||||||
|
|
||||||
|
// Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do.
|
||||||
|
// During this sleep, the nRF chip should only use ~3uA
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ use gpio::pin_cnf::DRIVE_A;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::pac::p0 as gpio;
|
use crate::pac::p0 as gpio;
|
||||||
|
|
||||||
|
use self::sealed::Pin as _;
|
||||||
|
|
||||||
/// A GPIO port with up to 32 pins.
|
/// A GPIO port with up to 32 pins.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum Port {
|
pub enum Port {
|
||||||
|
@ -487,6 +489,17 @@ impl OptionalPin for NoPin {
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
|
|
||||||
|
pub(crate) fn deconfigure_pin(psel_bits: u32) {
|
||||||
|
if psel_bits & 0x8000_0000 != 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
AnyPin::steal(psel_bits as _).conf().reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================
|
||||||
|
|
||||||
macro_rules! impl_pin {
|
macro_rules! impl_pin {
|
||||||
($type:ident, $port_num:expr, $pin_num:expr) => {
|
($type:ident, $port_num:expr, $pin_num:expr) => {
|
||||||
impl crate::gpio::Pin for peripherals::$type {}
|
impl crate::gpio::Pin for peripherals::$type {}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use core::ptr;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
use embassy::interrupt::{Interrupt, InterruptExt};
|
use embassy::interrupt::{Interrupt, InterruptExt};
|
||||||
use embassy::traits::flash::{Error, Flash};
|
use embassy::traits::flash::{Error, Flash};
|
||||||
|
@ -10,7 +11,8 @@ use embassy_extras::unborrow;
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
use crate::fmt::{assert, assert_eq, *};
|
use crate::fmt::{assert, assert_eq, *};
|
||||||
use crate::gpio::Pin as GpioPin;
|
use crate::gpio::sealed::Pin as _;
|
||||||
|
use crate::gpio::{self, Pin as GpioPin};
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
|
|
||||||
pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode;
|
pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode;
|
||||||
|
@ -29,7 +31,9 @@ pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode;
|
||||||
// - set gpio in high drive
|
// - set gpio in high drive
|
||||||
|
|
||||||
pub struct DeepPowerDownConfig {
|
pub struct DeepPowerDownConfig {
|
||||||
|
/// Time required for entering DPM, in units of 16us
|
||||||
pub enter_time: u16,
|
pub enter_time: u16,
|
||||||
|
/// Time required for exiting DPM, in units of 16us
|
||||||
pub exit_time: u16,
|
pub exit_time: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,11 +59,12 @@ impl Default for Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Qspi<'d, T: Instance> {
|
pub struct Qspi<'d, T: Instance> {
|
||||||
|
dpm_enabled: bool,
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Qspi<'d, T> {
|
impl<'d, T: Instance> Qspi<'d, T> {
|
||||||
pub fn new(
|
pub async fn new(
|
||||||
_qspi: impl Unborrow<Target = T> + 'd,
|
_qspi: impl Unborrow<Target = T> + 'd,
|
||||||
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
sck: impl Unborrow<Target = impl GpioPin> + 'd,
|
sck: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
@ -69,20 +74,21 @@ impl<'d, T: Instance> Qspi<'d, T> {
|
||||||
io2: impl Unborrow<Target = impl GpioPin> + 'd,
|
io2: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
io3: impl Unborrow<Target = impl GpioPin> + 'd,
|
io3: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Qspi<'d, T> {
|
||||||
unborrow!(irq, sck, csn, io0, io1, io2, io3);
|
unborrow!(irq, sck, csn, io0, io1, io2, io3);
|
||||||
|
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
for cnf in &[
|
let sck = sck.degrade();
|
||||||
sck.conf(),
|
let csn = csn.degrade();
|
||||||
csn.conf(),
|
let io0 = io0.degrade();
|
||||||
io0.conf(),
|
let io1 = io1.degrade();
|
||||||
io1.conf(),
|
let io2 = io2.degrade();
|
||||||
io2.conf(),
|
let io3 = io3.degrade();
|
||||||
io3.conf(),
|
|
||||||
] {
|
for pin in [&sck, &csn, &io0, &io1, &io2, &io3] {
|
||||||
cnf.write(|w| w.dir().output().drive().h0h1());
|
pin.set_high();
|
||||||
|
pin.conf().write(|w| w.dir().output().drive().h0h1());
|
||||||
}
|
}
|
||||||
|
|
||||||
r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
|
r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
|
||||||
|
@ -92,53 +98,56 @@ impl<'d, T: Instance> Qspi<'d, T> {
|
||||||
r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) });
|
r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) });
|
||||||
r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) });
|
r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) });
|
||||||
|
|
||||||
r.ifconfig0.write(|mut w| {
|
r.ifconfig0.write(|w| {
|
||||||
w = w.addrmode().variant(AddressMode::_24BIT);
|
w.addrmode().variant(AddressMode::_24BIT);
|
||||||
if config.deep_power_down.is_some() {
|
w.dpmenable().bit(config.deep_power_down.is_some());
|
||||||
w = w.dpmenable().enable();
|
w.ppsize().variant(config.write_page_size);
|
||||||
} else {
|
w.readoc().variant(config.read_opcode);
|
||||||
w = w.dpmenable().disable();
|
w.writeoc().variant(config.write_opcode);
|
||||||
}
|
|
||||||
w = w.ppsize().variant(config.write_page_size);
|
|
||||||
w = w.readoc().variant(config.read_opcode);
|
|
||||||
w = w.writeoc().variant(config.write_opcode);
|
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(dpd) = &config.deep_power_down {
|
if let Some(dpd) = &config.deep_power_down {
|
||||||
r.dpmdur.write(|mut w| unsafe {
|
r.dpmdur.write(|w| unsafe {
|
||||||
w = w.enter().bits(dpd.enter_time);
|
w.enter().bits(dpd.enter_time);
|
||||||
w = w.exit().bits(dpd.exit_time);
|
w.exit().bits(dpd.exit_time);
|
||||||
w
|
w
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ifconfig1.write(|w| {
|
r.ifconfig1.write(|w| unsafe {
|
||||||
let w = unsafe { w.sckdelay().bits(80) };
|
w.sckdelay().bits(80);
|
||||||
let w = w.dpmen().exit();
|
w.dpmen().exit();
|
||||||
let w = w.spimode().mode0();
|
w.spimode().mode0();
|
||||||
let w = unsafe { w.sckfreq().bits(3) };
|
w.sckfreq().bits(3);
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
|
|
||||||
r.xipoffset
|
r.xipoffset.write(|w| unsafe {
|
||||||
.write(|w| unsafe { w.xipoffset().bits(config.xip_offset) });
|
w.xipoffset().bits(config.xip_offset);
|
||||||
|
w
|
||||||
// Enable it
|
});
|
||||||
r.enable.write(|w| w.enable().enabled());
|
|
||||||
|
|
||||||
r.events_ready.reset();
|
|
||||||
r.tasks_activate.write(|w| w.tasks_activate().bit(true));
|
|
||||||
while r.events_ready.read().bits() == 0 {}
|
|
||||||
r.events_ready.reset();
|
|
||||||
|
|
||||||
irq.set_handler(Self::on_interrupt);
|
irq.set_handler(Self::on_interrupt);
|
||||||
irq.unpend();
|
irq.unpend();
|
||||||
irq.enable();
|
irq.enable();
|
||||||
|
|
||||||
Self {
|
// Enable it
|
||||||
|
r.enable.write(|w| w.enable().enabled());
|
||||||
|
|
||||||
|
let mut res = Self {
|
||||||
|
dpm_enabled: config.deep_power_down.is_some(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
r.events_ready.reset();
|
||||||
|
r.intenset.write(|w| w.ready().set());
|
||||||
|
|
||||||
|
r.tasks_activate.write(|w| w.tasks_activate().bit(true));
|
||||||
|
|
||||||
|
res.wait_ready().await;
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt(_: *mut ()) {
|
fn on_interrupt(_: *mut ()) {
|
||||||
|
@ -151,19 +160,6 @@ impl<'d, T: Instance> Qspi<'d, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleep(&mut self) {
|
|
||||||
let r = T::regs();
|
|
||||||
|
|
||||||
info!("flash: sleeping");
|
|
||||||
info!("flash: state = {:?}", r.status.read().bits());
|
|
||||||
r.ifconfig1.modify(|_, w| w.dpmen().enter());
|
|
||||||
info!("flash: state = {:?}", r.status.read().bits());
|
|
||||||
cortex_m::asm::delay(1000000);
|
|
||||||
info!("flash: state = {:?}", r.status.read().bits());
|
|
||||||
|
|
||||||
r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn custom_instruction(
|
pub async fn custom_instruction(
|
||||||
&mut self,
|
&mut self,
|
||||||
opcode: u8,
|
opcode: u8,
|
||||||
|
@ -246,6 +242,44 @@ impl<'d, T: Instance> Qspi<'d, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Drop for Qspi<'d, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
if self.dpm_enabled {
|
||||||
|
info!("qspi: doing deep powerdown...");
|
||||||
|
|
||||||
|
r.ifconfig1.modify(|_, w| w.dpmen().enter());
|
||||||
|
|
||||||
|
// Wait for DPM enter.
|
||||||
|
// Unfortunately we must spin. There's no way to do this interrupt-driven.
|
||||||
|
// The READY event does NOT fire on DPM enter (but it does fire on DPM exit :shrug:)
|
||||||
|
while r.status.read().dpm().is_disabled() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it.
|
||||||
|
r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit());
|
||||||
|
|
||||||
|
// Workaround https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev1/ERR/nRF52840/Rev1/latest/anomaly_840_122.html?cp=4_0_1_2_1_7
|
||||||
|
// Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate,
|
||||||
|
// so we only do the second one here.
|
||||||
|
unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) }
|
||||||
|
|
||||||
|
r.enable.write(|w| w.enable().disabled());
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
gpio::deconfigure_pin(r.psel.sck.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.io0.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.io1.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.io2.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.io3.read().bits());
|
||||||
|
|
||||||
|
info!("qspi: dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Flash for Qspi<'d, T> {
|
impl<'d, T: Instance> Flash for Qspi<'d, T> {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
|
type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Error>> + 'a;
|
||||||
|
|
|
@ -14,6 +14,7 @@ use traits::spi::FullDuplex;
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
use crate::gpio::{OptionalPin, Pin as GpioPin};
|
use crate::gpio::{OptionalPin, Pin as GpioPin};
|
||||||
use crate::interrupt::Interrupt;
|
use crate::interrupt::Interrupt;
|
||||||
|
use crate::{fmt::*, gpio};
|
||||||
use crate::{pac, util::slice_in_ram_or};
|
use crate::{pac, util::slice_in_ram_or};
|
||||||
|
|
||||||
pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
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> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Drop for Spim<'d, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
info!("spim drop");
|
||||||
|
|
||||||
|
// TODO check for abort, wait for xxxstopped
|
||||||
|
|
||||||
|
// 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.miso.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.mosi.read().bits());
|
||||||
|
|
||||||
|
info!("spim drop: done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> {
|
impl<'d, T: Instance> FullDuplex<u8> for Spim<'d, T> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,10 @@ use embassy::util::{AtomicWaker, Unborrow};
|
||||||
use embassy_extras::unborrow;
|
use embassy_extras::unborrow;
|
||||||
|
|
||||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||||
use crate::fmt::*;
|
|
||||||
use crate::gpio::Pin as GpioPin;
|
use crate::gpio::Pin as GpioPin;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::util::{slice_in_ram, slice_in_ram_or};
|
use crate::util::{slice_in_ram, slice_in_ram_or};
|
||||||
|
use crate::{fmt::*, gpio};
|
||||||
|
|
||||||
pub enum Frequency {
|
pub enum Frequency {
|
||||||
#[doc = "26738688: 100 kbps"]
|
#[doc = "26738688: 100 kbps"]
|
||||||
|
@ -30,12 +30,16 @@ pub enum Frequency {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub frequency: Frequency,
|
pub frequency: Frequency,
|
||||||
|
pub sda_pullup: bool,
|
||||||
|
pub scl_pullup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
frequency: Frequency::K100,
|
frequency: Frequency::K100,
|
||||||
|
sda_pullup: false,
|
||||||
|
scl_pullup: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,15 +65,19 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||||
sda.conf().write(|w| {
|
sda.conf().write(|w| {
|
||||||
w.dir().input();
|
w.dir().input();
|
||||||
w.input().connect();
|
w.input().connect();
|
||||||
w.pull().pullup();
|
|
||||||
w.drive().s0d1();
|
w.drive().s0d1();
|
||||||
|
if config.sda_pullup {
|
||||||
|
w.pull().pullup();
|
||||||
|
}
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
scl.conf().write(|w| {
|
scl.conf().write(|w| {
|
||||||
w.dir().input();
|
w.dir().input();
|
||||||
w.input().connect();
|
w.input().connect();
|
||||||
w.pull().pullup();
|
|
||||||
w.drive().s0d1();
|
w.drive().s0d1();
|
||||||
|
if config.scl_pullup {
|
||||||
|
w.pull().pullup();
|
||||||
|
}
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -422,9 +430,10 @@ impl<'a, T: Instance> Drop for Twim<'a, T> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
r.enable.write(|w| w.enable().disabled());
|
r.enable.write(|w| w.enable().disabled());
|
||||||
|
|
||||||
info!("uarte drop: done");
|
gpio::deconfigure_pin(r.psel.sda.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.scl.read().bits());
|
||||||
|
|
||||||
// TODO: disable pins
|
info!("twim drop: done");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ use futures::future::poll_fn;
|
||||||
use crate::chip::EASY_DMA_SIZE;
|
use crate::chip::EASY_DMA_SIZE;
|
||||||
use crate::fmt::{assert, panic, *};
|
use crate::fmt::{assert, panic, *};
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
use crate::gpio::{OptionalPin as GpioOptionalPin, Pin as GpioPin};
|
use crate::gpio::{self, OptionalPin as GpioOptionalPin, Pin as GpioPin};
|
||||||
use crate::interrupt::Interrupt;
|
use crate::interrupt::Interrupt;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
|
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
|
||||||
|
@ -166,9 +166,12 @@ impl<'a, T: Instance> Drop for Uarte<'a, T> {
|
||||||
// Finally we can disable!
|
// Finally we can disable!
|
||||||
r.enable.write(|w| w.enable().disabled());
|
r.enable.write(|w| w.enable().disabled());
|
||||||
|
|
||||||
info!("uarte drop: done");
|
gpio::deconfigure_pin(r.psel.rxd.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.txd.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.rts.read().bits());
|
||||||
|
gpio::deconfigure_pin(r.psel.cts.read().bits());
|
||||||
|
|
||||||
// TODO: disable pins
|
info!("uarte drop: done");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue