Merge pull request #14 from timokroeger/uarte-power-optimization
UARTE power optimization and improvements
This commit is contained in:
commit
9bb4c97dc2
@ -17,8 +17,8 @@ use crate::hal::gpio::Port as GpioPort;
|
|||||||
use crate::hal::pac;
|
use crate::hal::pac;
|
||||||
use crate::hal::prelude::*;
|
use crate::hal::prelude::*;
|
||||||
use crate::hal::target_constants::EASY_DMA_SIZE;
|
use crate::hal::target_constants::EASY_DMA_SIZE;
|
||||||
use crate::interrupt;
|
|
||||||
use crate::interrupt::OwnedInterrupt;
|
use crate::interrupt::OwnedInterrupt;
|
||||||
|
use crate::{interrupt, util};
|
||||||
|
|
||||||
pub use crate::hal::uarte::Pins;
|
pub use crate::hal::uarte::Pins;
|
||||||
// Re-export SVD variants to allow user to directly set values.
|
// Re-export SVD variants to allow user to directly set values.
|
||||||
@ -131,6 +131,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self) -> (T, T::Interrupt, Pins) {
|
pub fn free(self) -> (T, T::Interrupt, Pins) {
|
||||||
|
// Wait for the peripheral to be disabled from the ISR.
|
||||||
|
while self.instance.enable.read().enable().is_enabled() {}
|
||||||
(self.instance, self.irq, self.pins)
|
(self.instance, self.irq, self.pins)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +158,13 @@ where
|
|||||||
uarte.events_endtx.reset();
|
uarte.events_endtx.reset();
|
||||||
trace!("endtx");
|
trace!("endtx");
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
if uarte.events_txstarted.read().bits() != 0 {
|
||||||
|
// The ENDTX was signal triggered because DMA has finished.
|
||||||
|
uarte.events_txstarted.reset();
|
||||||
|
try_disable = true;
|
||||||
|
}
|
||||||
|
|
||||||
T::state().tx_done.signal(());
|
T::state().tx_done.signal(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +179,13 @@ where
|
|||||||
trace!("endrx");
|
trace!("endrx");
|
||||||
let len = uarte.rxd.amount.read().bits();
|
let len = uarte.rxd.amount.read().bits();
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
if uarte.events_rxstarted.read().bits() != 0 {
|
||||||
|
// The ENDRX was signal triggered because DMA buffer is full.
|
||||||
|
uarte.events_rxstarted.reset();
|
||||||
|
try_disable = true;
|
||||||
|
}
|
||||||
|
|
||||||
T::state().rx_done.signal(len);
|
T::state().rx_done.signal(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +220,7 @@ impl<T: Instance> embassy::uart::Uart for Uarte<T> {
|
|||||||
// `mem::forget()` on a previous future after polling it once.
|
// `mem::forget()` on a previous future after polling it once.
|
||||||
assert!(!self.tx_started());
|
assert!(!self.tx_started());
|
||||||
|
|
||||||
self.enable();
|
T::state().tx_done.reset();
|
||||||
|
|
||||||
SendFuture {
|
SendFuture {
|
||||||
uarte: self,
|
uarte: self,
|
||||||
@ -227,7 +243,7 @@ impl<T: Instance> embassy::uart::Uart for Uarte<T> {
|
|||||||
// `mem::forget()` on a previous future after polling it once.
|
// `mem::forget()` on a previous future after polling it once.
|
||||||
assert!(!self.rx_started());
|
assert!(!self.rx_started());
|
||||||
|
|
||||||
self.enable();
|
T::state().rx_done.reset();
|
||||||
|
|
||||||
ReceiveFuture {
|
ReceiveFuture {
|
||||||
uarte: self,
|
uarte: self,
|
||||||
@ -241,7 +257,7 @@ pub struct SendFuture<'a, T>
|
|||||||
where
|
where
|
||||||
T: Instance,
|
T: Instance,
|
||||||
{
|
{
|
||||||
uarte: &'a Uarte<T>,
|
uarte: &'a mut Uarte<T>,
|
||||||
buf: &'a [u8],
|
buf: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +275,9 @@ where
|
|||||||
.instance
|
.instance
|
||||||
.tasks_stoptx
|
.tasks_stoptx
|
||||||
.write(|w| unsafe { w.bits(1) });
|
.write(|w| unsafe { w.bits(1) });
|
||||||
T::state().tx_done.blocking_wait();
|
|
||||||
|
// TX is stopped almost instantly, spinning is fine.
|
||||||
|
while !T::state().tx_done.signaled() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,28 +291,34 @@ where
|
|||||||
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let Self { uarte, buf } = unsafe { self.get_unchecked_mut() };
|
let Self { uarte, buf } = unsafe { self.get_unchecked_mut() };
|
||||||
|
|
||||||
if !uarte.tx_started() {
|
if T::state().tx_done.poll_wait(cx).is_pending() {
|
||||||
let uarte = &uarte.instance;
|
|
||||||
|
|
||||||
T::state().tx_done.reset();
|
|
||||||
|
|
||||||
let ptr = buf.as_ptr();
|
let ptr = buf.as_ptr();
|
||||||
let len = buf.len();
|
let len = buf.len();
|
||||||
assert!(len <= EASY_DMA_SIZE);
|
assert!(len <= EASY_DMA_SIZE);
|
||||||
// TODO: panic if buffer is not in SRAM
|
// TODO: panic if buffer is not in SRAM
|
||||||
|
|
||||||
|
uarte.enable();
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
uarte.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
|
||||||
uarte
|
uarte
|
||||||
|
.instance
|
||||||
|
.txd
|
||||||
|
.ptr
|
||||||
|
.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||||
|
uarte
|
||||||
|
.instance
|
||||||
.txd
|
.txd
|
||||||
.maxcnt
|
.maxcnt
|
||||||
.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||||
|
|
||||||
trace!("starttx");
|
trace!("starttx");
|
||||||
uarte.tasks_starttx.write(|w| unsafe { w.bits(1) });
|
uarte.instance.tasks_starttx.write(|w| unsafe { w.bits(1) });
|
||||||
}
|
while !uarte.tx_started() {} // Make sure transmission has started
|
||||||
|
|
||||||
T::state().tx_done.poll_wait(cx).map(|()| Ok(()))
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +327,7 @@ pub struct ReceiveFuture<'a, T>
|
|||||||
where
|
where
|
||||||
T: Instance,
|
T: Instance,
|
||||||
{
|
{
|
||||||
uarte: &'a Uarte<T>,
|
uarte: &'a mut Uarte<T>,
|
||||||
buf: &'a mut [u8],
|
buf: &'a mut [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,14 +337,15 @@ where
|
|||||||
{
|
{
|
||||||
fn drop(self: &mut Self) {
|
fn drop(self: &mut Self) {
|
||||||
if self.uarte.rx_started() {
|
if self.uarte.rx_started() {
|
||||||
trace!("stoprx");
|
trace!("stoprx (drop)");
|
||||||
|
|
||||||
self.uarte.instance.events_rxstarted.reset();
|
self.uarte.instance.events_rxstarted.reset();
|
||||||
self.uarte
|
self.uarte
|
||||||
.instance
|
.instance
|
||||||
.tasks_stoprx
|
.tasks_stoprx
|
||||||
.write(|w| unsafe { w.bits(1) });
|
.write(|w| unsafe { w.bits(1) });
|
||||||
T::state().rx_done.blocking_wait();
|
|
||||||
|
util::low_power_wait_until(|| T::state().rx_done.signaled())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,27 +359,35 @@ where
|
|||||||
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let Self { uarte, buf } = unsafe { self.get_unchecked_mut() };
|
let Self { uarte, buf } = unsafe { self.get_unchecked_mut() };
|
||||||
|
|
||||||
if !uarte.rx_started() {
|
match T::state().rx_done.poll_wait(cx) {
|
||||||
let uarte = &uarte.instance;
|
Poll::Pending if !uarte.rx_started() => {
|
||||||
|
let ptr = buf.as_ptr();
|
||||||
|
let len = buf.len();
|
||||||
|
assert!(len <= EASY_DMA_SIZE);
|
||||||
|
|
||||||
T::state().rx_done.reset();
|
uarte.enable();
|
||||||
|
|
||||||
let ptr = buf.as_ptr();
|
compiler_fence(Ordering::SeqCst);
|
||||||
let len = buf.len();
|
uarte
|
||||||
assert!(len <= EASY_DMA_SIZE);
|
.instance
|
||||||
|
.rxd
|
||||||
|
.ptr
|
||||||
|
.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||||
|
uarte
|
||||||
|
.instance
|
||||||
|
.rxd
|
||||||
|
.maxcnt
|
||||||
|
.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
trace!("startrx");
|
||||||
uarte.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
uarte.instance.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||||
uarte
|
while !uarte.rx_started() {} // Make sure reception has started
|
||||||
.rxd
|
|
||||||
.maxcnt
|
|
||||||
.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
|
||||||
|
|
||||||
trace!("startrx");
|
Poll::Pending
|
||||||
uarte.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
}
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(_) => Poll::Ready(Ok(())),
|
||||||
}
|
}
|
||||||
|
|
||||||
T::state().rx_done.poll_wait(cx).map(|_| Ok(()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,8 +398,19 @@ where
|
|||||||
{
|
{
|
||||||
/// Stops the ongoing reception and returns the number of bytes received.
|
/// Stops the ongoing reception and returns the number of bytes received.
|
||||||
pub async fn stop(self) -> usize {
|
pub async fn stop(self) -> usize {
|
||||||
drop(self);
|
let len = if self.uarte.rx_started() {
|
||||||
let len = T::state().rx_done.wait().await;
|
trace!("stoprx (stop)");
|
||||||
|
|
||||||
|
self.uarte.instance.events_rxstarted.reset();
|
||||||
|
self.uarte
|
||||||
|
.instance
|
||||||
|
.tasks_stoprx
|
||||||
|
.write(|w| unsafe { w.bits(1) });
|
||||||
|
T::state().rx_done.wait().await
|
||||||
|
} else {
|
||||||
|
// Transfer was stopped before it even started. No bytes were sent.
|
||||||
|
0
|
||||||
|
};
|
||||||
len as _
|
len as _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,12 @@
|
|||||||
pub mod peripheral;
|
pub mod peripheral;
|
||||||
pub mod ring_buffer;
|
pub mod ring_buffer;
|
||||||
|
|
||||||
|
/// Low power blocking wait loop using WFE/SEV.
|
||||||
|
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
||||||
|
while !condition() {
|
||||||
|
// WFE might "eat" an event that would have otherwise woken the executor.
|
||||||
|
cortex_m::asm::wfe();
|
||||||
|
}
|
||||||
|
// Retrigger an event to be transparent to the executor.
|
||||||
|
cortex_m::asm::sev();
|
||||||
|
}
|
||||||
|
@ -63,12 +63,7 @@ impl<T: Send> Signal<T> {
|
|||||||
futures::future::poll_fn(move |cx| self.poll_wait(cx))
|
futures::future::poll_fn(move |cx| self.poll_wait(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks until the signal has been received.
|
pub fn signaled(&self) -> bool {
|
||||||
///
|
cortex_m::interrupt::free(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_)))
|
||||||
/// Returns immediately when [`poll_wait()`] has not been called before.
|
|
||||||
pub fn blocking_wait(&self) {
|
|
||||||
while cortex_m::interrupt::free(|_| {
|
|
||||||
matches!(unsafe { &*self.state.get() }, State::Waiting(_))
|
|
||||||
}) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user