Skip to content

Commit

Permalink
Attempt at async RMT with buffer refilling, building on esp-rs#787
Browse files Browse the repository at this point in the history
Trying to address the 64 symbol limit in esp-rs#1779.
Having troubles, I tried disabling the interrupts so it just polls the device,
but the transmission never actually starts until I reset the controller.
  • Loading branch information
tschundler committed Jul 13, 2024
1 parent 170bc16 commit d345381
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 35 deletions.
82 changes: 81 additions & 1 deletion esp-hal-smartled/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ use esp_hal::{
clock::Clocks,
gpio::OutputPin,
peripheral::Peripheral,
rmt::{Error as RmtError, PulseCode, TxChannel, TxChannelConfig, TxChannelCreator},
rmt::{
asynch::TxChannelAsync, Error as RmtError, PulseCode, TxChannel, TxChannelConfig,
TxChannelCreator, TxChannelCreatorAsync,
},
};
use smart_leds_trait::{SmartLedsWrite, RGB8};

Expand Down Expand Up @@ -274,3 +277,80 @@ where
}
}
}

/// Adapter taking an RMT channel and a specific pin and providing RGB LED
/// interaction functionality using the `smart-leds` crate
pub struct SmartLedsAdapterAsync<TX>
where
TX: TxChannelAsync,
{
channel: Option<TX>,
pulses: (u32, u32),
}

impl<'d, TX> SmartLedsAdapterAsync<TX>
where
TX: TxChannelAsync,
{
/// Create a new adapter object that drives the pin using the RMT channel.
pub fn new<C, O>(
channel: C,
pin: impl Peripheral<P = O> + 'd,
clocks: &Clocks,
) -> SmartLedsAdapterAsync<TX>
where
O: OutputPin + 'd,
C: TxChannelCreatorAsync<'d, TX, O>,
{
let config = TxChannelConfig {
clk_divider: 1,
idle_output_level: false,
carrier_modulation: false,
idle_output: true,

..TxChannelConfig::default()
};

let channel = channel.configure(pin, config).unwrap();

// Assume the RMT peripheral is set up to use the APB clock
let src_clock = clocks.apb_clock.to_MHz();

Self {
channel: Some(channel),
pulses: (
u32::from(PulseCode {
level1: true,
length1: ((SK68XX_T0H_NS * src_clock) / 1000) as u16,
level2: false,
length2: ((SK68XX_T0L_NS * src_clock) / 1000) as u16,
}),
u32::from(PulseCode {
level1: true,
length1: ((SK68XX_T1H_NS * src_clock) / 1000) as u16,
level2: false,
length2: ((SK68XX_T1L_NS * src_clock) / 1000) as u16,
}),
),
}
}

/// Convert all RGB8 items of the iterator to the RMT format and
/// add them to internal buffer, then start a singular RMT operation
/// based on that buffer.
pub async fn write<T, I>(&mut self, iterator: T) -> Result<(), LedAdapterError>
where
T: IntoIterator<Item = I>,
I: Into<RGB8>,
{
let channel = self.channel.as_mut().unwrap();
let mut itr = PulseGenerator::new(
ByteGenerator::new(iterator.into_iter().map(|v| v.into())),
self.pulses,
);
match channel.transmit(&mut itr).await {
Ok(()) => Ok(()),
Err(e) => Err(LedAdapterError::TransmissionError(e)),
}
}
}
111 changes: 77 additions & 34 deletions esp-hal/src/rmt.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(associated_type_bounds)]

//! # Remote Control Peripheral (RMT)
//!
//! ## Overview
Expand Down Expand Up @@ -286,10 +288,7 @@ impl<'d> Rmt<'d, crate::Async> {
_clocks: &Clocks,
) -> Result<Self, Error> {
Self::new_internal(
peripheral,
frequency,
_clocks,
Some(asynch::async_interrupt_handler),
peripheral, frequency, _clocks, None, //Some(asynch::async_interrupt_handler),
)
}
}
Expand Down Expand Up @@ -481,7 +480,7 @@ where
}
<C as private::TxChannelInternal<crate::Blocking>>::reset_threshold_set();

// re-fill RX RAM
// re-fill TX RAM
let ptr = (constants::RMT_RAM_START
+ C::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4)
as *mut u32;
Expand Down Expand Up @@ -1014,11 +1013,11 @@ pub trait TxChannel: private::TxChannelInternal<crate::Blocking> {
D: IntoIterator<Item = T, IntoIter = R>,
R: Iterator<Item = T>,
{
let mut data = data.into_iter();
let mut data = data.into_iter().fuse();
Self::send_raw(&mut data, false, 0);
SingleShotTxTransaction {
channel: self,
data: data.fuse(),
data,
ram_index: 0,
}
}
Expand Down Expand Up @@ -1142,41 +1141,90 @@ pub mod asynch {
const INIT: AtomicWaker = AtomicWaker::new();
static WAKER: [AtomicWaker; NUM_CHANNELS] = [INIT; NUM_CHANNELS];

pub(crate) struct RmtTxFuture<T>
pub(crate) struct RmtTxFuture<C, D, T: Into<u32> + Copy>
where
T: TxChannelAsync,
C: TxChannelAsync,
D: Iterator<Item = T>,
{
_phantom: PhantomData<T>,
_phantom: PhantomData<C>,
data: Fuse<D>,
ram_index: usize,
}

impl<T> RmtTxFuture<T>
impl<C, D, T> RmtTxFuture<C, D, T>
where
T: TxChannelAsync,
C: TxChannelAsync,
T: Into<u32> + Copy,
D: Iterator<Item = T>,
{
pub fn new(_instance: &T) -> Self {
pub fn new(_instance: &C, data: Fuse<D>) -> Self {
Self {
_phantom: PhantomData,
data,
ram_index: 0,
}
}
}

impl<T> core::future::Future for RmtTxFuture<T>
impl<C, D, T> core::future::Future for RmtTxFuture<C, D, T>
where
T: TxChannelAsync,
C: TxChannelAsync,
T: Into<u32> + Copy,
D: Iterator<Item = T>,
{
type Output = ();
type Output = Result<(), Error>;

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
WAKER[T::CHANNEL as usize].register(ctx.waker());
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
//WAKER[C::CHANNEL as usize].register(ctx.waker());

if T::is_error() || T::is_done() {
Poll::Ready(())
} else {
Poll::Pending
if C::is_error() {
return Poll::Ready(Err(Error::TransmissionError));
}

if C::is_done() {
return Poll::Ready(Ok(()));
}

// never gets set :|
if !C::is_threshold_set() {
return Poll::Pending;
}
C::reset_threshold_set();

// re-fill TX RAM
let ptr = (constants::RMT_RAM_START
+ C::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4)
as *mut u32;

loop {
let Some(v) = self.data.next() else {
break;
};
unsafe {
ptr.add(self.ram_index).write_volatile(v.into());
}
self.ram_index += 1;
if self.ram_index == constants::RMT_CHANNEL_RAM_SIZE {
self.ram_index = 0;
break;
}
if self.ram_index == (constants::RMT_CHANNEL_RAM_SIZE / 2) {
break;
}
}
if C::is_threshold_set() {
return Poll::Ready(Err(Error::Underflow));
}
Poll::Pending
}
}

impl<C, D, T> Unpin for RmtTxFuture<C, D, T>
where
C: TxChannelAsync,
T: Into<u32> + Copy,
D: Iterator<Item = T>,
{
}
/// TX channel in async mode
pub trait TxChannelAsync: private::TxChannelInternal<crate::Async> {
/// Start transmitting the given pulse code sequence.
Expand All @@ -1187,19 +1235,14 @@ pub mod asynch {
Self: Sized,
D: IntoIterator<Item = T>,
{
Self::clear_interrupts();
Self::listen_interrupt(super::private::Event::End);
Self::listen_interrupt(super::private::Event::Error);
let mut data = data.into_iter();
// Self::clear_interrupts();
// Self::listen_interrupt(super::private::Event::End);
// Self::listen_interrupt(super::private::Event::Error);
// Self::listen_interrupt(super::private::Event::Threshold);
let mut data = data.into_iter().fuse();
Self::send_raw(&mut data, false, 0);

RmtTxFuture::new(self).await;

if Self::is_error() {
Err(Error::TransmissionError)
} else {
Ok(())
}
RmtTxFuture::new(self, data).await
}
}

Expand Down

0 comments on commit d345381

Please sign in to comment.