RMT: Shouldn't be possible to assign pins multiple times into channel

This commit is contained in:
Juraj Sadel 2022-09-19 16:10:45 +02:00
parent b1d5e37f36
commit 0f9fdb7c15
6 changed files with 155 additions and 138 deletions

View File

@ -60,7 +60,7 @@
//! .set_idle_output(true); //! .set_idle_output(true);
//! //!
//! // Assign GPIO pin where pulses should be sent to //! // Assign GPIO pin where pulses should be sent to
//! rmt_channel0.assign_pin(io.pins.gpio8); //! let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio8);
//! //!
//! // Create pulse sequence //! // Create pulse sequence
//! let mut seq = [PulseCode { //! let mut seq = [PulseCode {
@ -95,9 +95,6 @@ pub enum SetupError {
/// The global configuration for the RMT peripheral is invalid /// The global configuration for the RMT peripheral is invalid
/// (e.g. the fractional parameters are outOfBound) /// (e.g. the fractional parameters are outOfBound)
InvalidGlobalConfig, InvalidGlobalConfig,
/// A pin was already assigned to the channel, at this point in
/// time, only one assigned pin per channel is supported
PinAlreadyAssigned,
} }
/// Errors that can occur during a transmission attempt /// Errors that can occur during a transmission attempt
@ -213,7 +210,7 @@ impl From<PulseCode> for u32 {
} }
/// Functionality that every OutputChannel must support /// Functionality that every OutputChannel must support
pub trait OutputChannel { pub trait OutputChannel<CC> {
/// Set the logical level that the connected pin is pulled to /// Set the logical level that the connected pin is pulled to
/// while the channel is idle /// while the channel is idle
fn set_idle_output_level(&mut self, level: bool) -> &mut Self; fn set_idle_output_level(&mut self, level: bool) -> &mut Self;
@ -237,8 +234,11 @@ pub trait OutputChannel {
/// (Note that we only take a reference here, so the ownership remains with /// (Note that we only take a reference here, so the ownership remains with
/// the calling entity. The configured pin thus can be re-configured /// the calling entity. The configured pin thus can be re-configured
/// independently.) /// independently.)
fn assign_pin<RmtPin: OutputPin>(&mut self, pin: RmtPin) -> &mut Self; fn assign_pin<RmtPin: OutputPin>(self, pin: RmtPin) -> CC;
}
/// Functionality that is allowed only on `ConfiguredChannel`
pub trait ConfiguredChannel {
/// Send a pulse sequence in a blocking fashion /// Send a pulse sequence in a blocking fashion
fn send_pulse_sequence<const N: usize>( fn send_pulse_sequence<const N: usize>(
&mut self, &mut self,
@ -355,119 +355,20 @@ macro_rules! channel_instance {
self.mem_offset = 0; self.mem_offset = 0;
} }
} }
};
}
macro_rules! output_channel { paste!(
($num:literal, $cxi:ident, $output_signal:path #[doc = "Wrapper for`" $cxi "` object."]
) => { pub struct [<Configured $cxi>] {
impl OutputChannel for $cxi { channel: $cxi,
/// Set the logical level that the connected pin is pulled to
/// while the channel is idle
#[inline(always)]
fn set_idle_output_level(&mut self, level: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.idle_out_lv().bit(level));
}
else {
conf1!($num)
.modify(|_, w| w.idle_out_lv().bit(level));
}
};
self
}
/// Enable/Disable the output while the channel is idle
#[inline(always)]
fn set_idle_output(&mut self, state: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.idle_out_en().bit(state));
}
else {
conf1!($num)
.modify(|_, w| w.idle_out_en().bit(state));
}
};
self
}
/// Set channel clock divider value
#[inline(always)]
fn set_channel_divider(&mut self, divider: u8) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| unsafe { w.div_cnt().bits(divider) });
}
else {
conf0!($num)
.modify(|_, w| unsafe { w.div_cnt().bits(divider) });
}
};
self
}
/// Enable/Disable carrier modulation
#[inline(always)]
fn set_carrier_modulation(&mut self, state: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.carrier_en().bit(state));
}
else {
conf0!($num)
.modify(|_, w| w.carrier_en().bit(state));
}
};
self
}
/// Set the clock source (for the ESP32-S2 and ESP32 this can be done on a
/// channel level)
#[cfg(any(esp32s2, esp32))]
#[inline(always)]
fn set_clock_source(&mut self, source: ClockSource) -> &mut Self {
let bit_value = match source {
ClockSource::RefTick => false,
ClockSource::APB => true,
};
conf1!($num)
.modify(|_, w| w.ref_always_on().bit(bit_value));
self
}
/// Assign a pin that should be driven by this channel
fn assign_pin<RmtPin: OutputPin >(
&mut self,
mut pin: RmtPin,
) -> &mut Self {
// Configure Pin as output anc connect to signal
pin.set_to_push_pull_output()
.connect_peripheral_to_output($output_signal);
self
} }
impl ConfiguredChannel for [<Configured $cxi>] {
/// Send a pulse sequence in a blocking fashion /// Send a pulse sequence in a blocking fashion
fn send_pulse_sequence<const N: usize>( fn send_pulse_sequence<const N: usize>(
&mut self, &mut self,
repeat_mode: RepeatMode, repeat_mode: RepeatMode,
sequence: &[PulseCode; N], sequence: &[PulseCode; N],
) -> Result<(), TransmissionError> { ) -> Result<(), TransmissionError> {
// Create an internal object with an u32 representation of the pulses
// While this is a memory overhead, we need this for performance reasons (doing the
// conversion while already sending and replacing pulse codes in the fifo is too
// slow)
let precomputed_sequence = sequence.map(|x| u32::from(x)); let precomputed_sequence = sequence.map(|x| u32::from(x));
self.send_pulse_sequence_raw(repeat_mode, &precomputed_sequence) self.send_pulse_sequence_raw(repeat_mode, &precomputed_sequence)
@ -570,7 +471,7 @@ macro_rules! output_channel {
.set_bit() .set_bit()
}); });
self.reset_fifo(); self.channel.reset_fifo();
let mut sequence_iter = sequence.iter(); let mut sequence_iter = sequence.iter();
// We have to differentiate here if we can fit the whole sequence // We have to differentiate here if we can fit the whole sequence
@ -578,10 +479,10 @@ macro_rules! output_channel {
// the sequence into chuncks. // the sequence into chuncks.
if sequence.len() >= CHANNEL_RAM_SIZE as usize { if sequence.len() >= CHANNEL_RAM_SIZE as usize {
// Write the first 48 entries // Write the first 48 entries
self.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE); self.channel.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE);
} else { } else {
// Write whole sequence to FIFO RAM // Write whole sequence to FIFO RAM
self.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE); self.channel.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE);
} }
// Clear the relevant interrupts // Clear the relevant interrupts
@ -686,7 +587,7 @@ macro_rules! output_channel {
} }
// Refill the buffer // Refill the buffer
(false, false, false, true) => { (false, false, false, true) => {
self.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE / 2); self.channel.write_sequence(&mut sequence_iter, CHANNEL_RAM_SIZE / 2);
// Clear the threshold interrupt (write-through) // Clear the threshold interrupt (write-through)
unsafe { &*RMT::PTR }.int_clr.write(|w| { unsafe { &*RMT::PTR }.int_clr.write(|w| {
@ -739,6 +640,116 @@ macro_rules! output_channel {
}; };
} }
} }
);
};
}
macro_rules! output_channel {
($num:literal, $cxi:ident, $output_signal:path
) => {
paste!(
impl OutputChannel<[<Configured $cxi>]> for $cxi {
/// Set the logical level that the connected pin is pulled to
/// while the channel is idle
#[inline(always)]
fn set_idle_output_level(&mut self, level: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.idle_out_lv().bit(level));
}
else {
conf1!($num)
.modify(|_, w| w.idle_out_lv().bit(level));
}
};
self
}
/// Enable/Disable the output while the channel is idle
#[inline(always)]
fn set_idle_output(&mut self, state: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.idle_out_en().bit(state));
}
else {
conf1!($num)
.modify(|_, w| w.idle_out_en().bit(state));
}
};
self
}
/// Set channel clock divider value
#[inline(always)]
fn set_channel_divider(&mut self, divider: u8) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| unsafe { w.div_cnt().bits(divider) });
}
else {
conf0!($num)
.modify(|_, w| unsafe { w.div_cnt().bits(divider) });
}
};
self
}
/// Enable/Disable carrier modulation
#[inline(always)]
fn set_carrier_modulation(&mut self, state: bool) -> &mut Self {
cfg_if::cfg_if! {
if #[cfg(any(esp32c3, esp32s3))] {
unsafe { &*RMT::PTR }
.ch_tx_conf0[$num]
.modify(|_, w| w.carrier_en().bit(state));
}
else {
conf0!($num)
.modify(|_, w| w.carrier_en().bit(state));
}
};
self
}
/// Set the clock source (for the ESP32-S2 and ESP32 this can be done on a
/// channel level)
#[cfg(any(esp32s2, esp32))]
#[inline(always)]
fn set_clock_source(&mut self, source: ClockSource) -> &mut Self {
let bit_value = match source {
ClockSource::RefTick => false,
ClockSource::APB => true,
};
conf1!($num)
.modify(|_, w| w.ref_always_on().bit(bit_value));
self
}
/// Assign a pin that should be driven by this channel
fn assign_pin<RmtPin: OutputPin >(
self,
mut pin: RmtPin,
) -> [<Configured $cxi>] {
// Configure Pin as output anc connect to signal
pin.set_to_push_pull_output()
.connect_peripheral_to_output($output_signal);
[<Configured $cxi>] {
channel: self,
}
}
}
);
}; };
} }

View File

@ -20,7 +20,7 @@ use smart_leds_trait::{SmartLedsWrite, RGB8};
use crate::pulse_control::ClockSource; use crate::pulse_control::ClockSource;
use crate::{ use crate::{
gpio::OutputPin, gpio::OutputPin,
pulse_control::{OutputChannel, PulseCode, RepeatMode, TransmissionError}, pulse_control::{ConfiguredChannel, OutputChannel, PulseCode, RepeatMode, TransmissionError},
}; };
// Specifies what clock frequency we're using for the RMT peripheral (if // Specifies what clock frequency we're using for the RMT peripheral (if
@ -89,11 +89,17 @@ pub struct SmartLedsAdapter<CHANNEL, PIN, const BUFFER_SIZE: usize> {
impl<CHANNEL, PIN, const BUFFER_SIZE: usize> SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE> impl<CHANNEL, PIN, const BUFFER_SIZE: usize> SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE>
where where
CHANNEL: OutputChannel, CHANNEL: ConfiguredChannel,
PIN: OutputPin, PIN: OutputPin,
{ {
/// Create a new adapter object that drives the pin using the RMT channel. /// Create a new adapter object that drives the pin using the RMT channel.
pub fn new(mut channel: CHANNEL, pin: PIN) -> SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE> { pub fn new<UnconfiguredChannel>(
mut channel: UnconfiguredChannel,
pin: PIN,
) -> SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE>
where
UnconfiguredChannel: OutputChannel<CHANNEL>,
{
#[cfg(any(esp32c3, esp32s3))] #[cfg(any(esp32c3, esp32s3))]
channel channel
.set_idle_output_level(false) .set_idle_output_level(false)
@ -109,7 +115,7 @@ where
.set_idle_output(true) .set_idle_output(true)
.set_clock_source(ClockSource::APB); .set_clock_source(ClockSource::APB);
channel.assign_pin(pin); let channel = channel.assign_pin(pin);
Self { Self {
channel, channel,
rmt_buffer: [0; BUFFER_SIZE], rmt_buffer: [0; BUFFER_SIZE],
@ -165,7 +171,7 @@ where
impl<CHANNEL, PIN, const BUFFER_SIZE: usize> SmartLedsWrite impl<CHANNEL, PIN, const BUFFER_SIZE: usize> SmartLedsWrite
for SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE> for SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE>
where where
CHANNEL: OutputChannel, CHANNEL: ConfiguredChannel,
PIN: OutputPin, PIN: OutputPin,
{ {
type Error = LedAdapterError; type Error = LedAdapterError;

View File

@ -10,7 +10,7 @@ use esp32_hal::{
gpio::IO, gpio::IO,
pac::Peripherals, pac::Peripherals,
prelude::*, prelude::*,
pulse_control::{OutputChannel, PulseCode, RepeatMode}, pulse_control::{ConfiguredChannel, OutputChannel, PulseCode, RepeatMode},
timer::TimerGroup, timer::TimerGroup,
PulseControl, PulseControl,
Rtc, Rtc,
@ -46,7 +46,7 @@ fn main() -> ! {
.set_idle_output(true); .set_idle_output(true);
// Assign GPIO pin where pulses should be sent to // Assign GPIO pin where pulses should be sent to
rmt_channel0.assign_pin(io.pins.gpio4); let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio4);
// Create pulse sequence // Create pulse sequence
let mut seq = [PulseCode { let mut seq = [PulseCode {

View File

@ -10,7 +10,7 @@ use esp32c3_hal::{
gpio::IO, gpio::IO,
pac::Peripherals, pac::Peripherals,
prelude::*, prelude::*,
pulse_control::{ClockSource, OutputChannel, PulseCode, RepeatMode}, pulse_control::{ClockSource, ConfiguredChannel, OutputChannel, PulseCode, RepeatMode},
system::SystemExt, system::SystemExt,
timer::TimerGroup, timer::TimerGroup,
PulseControl, PulseControl,
@ -61,7 +61,7 @@ fn main() -> ! {
.set_idle_output(true); .set_idle_output(true);
// Assign GPIO pin where pulses should be sent to // Assign GPIO pin where pulses should be sent to
rmt_channel0.assign_pin(io.pins.gpio4); let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio4);
// Create pulse sequence // Create pulse sequence
let mut seq = [PulseCode { let mut seq = [PulseCode {

View File

@ -10,7 +10,7 @@ use esp32s2_hal::{
gpio::IO, gpio::IO,
pac::Peripherals, pac::Peripherals,
prelude::*, prelude::*,
pulse_control::{OutputChannel, PulseCode, RepeatMode}, pulse_control::{ConfiguredChannel, OutputChannel, PulseCode, RepeatMode},
timer::TimerGroup, timer::TimerGroup,
PulseControl, PulseControl,
Rtc, Rtc,
@ -47,7 +47,7 @@ fn main() -> ! {
.set_idle_output(true); .set_idle_output(true);
// Assign GPIO pin where pulses should be sent to // Assign GPIO pin where pulses should be sent to
rmt_channel0.assign_pin(io.pins.gpio4); let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio4);
// Create pulse sequence // Create pulse sequence
let mut seq = [PulseCode { let mut seq = [PulseCode {

View File

@ -10,7 +10,7 @@ use esp32s3_hal::{
gpio::IO, gpio::IO,
pac::Peripherals, pac::Peripherals,
prelude::*, prelude::*,
pulse_control::{ClockSource, OutputChannel, PulseCode, RepeatMode}, pulse_control::{ConfiguredChannel, ClockSource, OutputChannel, PulseCode, RepeatMode},
timer::TimerGroup, timer::TimerGroup,
PulseControl, PulseControl,
Rtc, Rtc,
@ -55,7 +55,7 @@ fn main() -> ! {
.set_idle_output(true); .set_idle_output(true);
// Assign GPIO pin where pulses should be sent to // Assign GPIO pin where pulses should be sent to
rmt_channel0.assign_pin(io.pins.gpio4); let mut rmt_channel0 = rmt_channel0.assign_pin(io.pins.gpio4);
// Create pulse sequence // Create pulse sequence
let mut seq = [PulseCode { let mut seq = [PulseCode {