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);
//!
//! // 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
//! let mut seq = [PulseCode {
@ -95,9 +95,6 @@ pub enum SetupError {
/// The global configuration for the RMT peripheral is invalid
/// (e.g. the fractional parameters are outOfBound)
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
@ -213,7 +210,7 @@ impl From<PulseCode> for u32 {
}
/// Functionality that every OutputChannel must support
pub trait OutputChannel {
pub trait OutputChannel<CC> {
/// Set the logical level that the connected pin is pulled to
/// while the channel is idle
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
/// the calling entity. The configured pin thus can be re-configured
/// 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
fn send_pulse_sequence<const N: usize>(
&mut self,
@ -355,123 +355,24 @@ macro_rules! channel_instance {
self.mem_offset = 0;
}
}
};
}
macro_rules! output_channel {
($num:literal, $cxi:ident, $output_signal:path
) => {
impl OutputChannel 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
paste!(
#[doc = "Wrapper for`" $cxi "` object."]
pub struct [<Configured $cxi>] {
channel: $cxi,
}
/// 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
}
impl ConfiguredChannel for [<Configured $cxi>] {
/// Send a pulse sequence in a blocking fashion
fn send_pulse_sequence<const N: usize>(
&mut self,
repeat_mode: RepeatMode,
sequence: &[PulseCode; N],
) -> Result<(), TransmissionError> {
let precomputed_sequence = sequence.map(|x| u32::from(x));
/// 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
}
/// Send a pulse sequence in a blocking fashion
fn send_pulse_sequence<const N: usize>(
&mut self,
repeat_mode: RepeatMode,
sequence: &[PulseCode; N],
) -> 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));
self.send_pulse_sequence_raw(repeat_mode, &precomputed_sequence)
}
self.send_pulse_sequence_raw(repeat_mode, &precomputed_sequence)
}
/// Send a raw pulse sequence in a blocking fashion
///
@ -570,7 +471,7 @@ macro_rules! output_channel {
.set_bit()
});
self.reset_fifo();
self.channel.reset_fifo();
let mut sequence_iter = sequence.iter();
// We have to differentiate here if we can fit the whole sequence
@ -578,10 +479,10 @@ macro_rules! output_channel {
// the sequence into chuncks.
if sequence.len() >= CHANNEL_RAM_SIZE as usize {
// 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 {
// 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
@ -686,7 +587,7 @@ macro_rules! output_channel {
}
// Refill the buffer
(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)
unsafe { &*RMT::PTR }.int_clr.write(|w| {
@ -738,7 +639,117 @@ macro_rules! output_channel {
// transmission once it has been started!
};
}
}
);
};
}
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,
}
}
}
);
};
}
@ -1034,4 +1045,4 @@ rmt!(
(1, Channel1, channel1, OutputSignal::RMT_SIG_OUT1),
(2, Channel2, channel2, OutputSignal::RMT_SIG_OUT2),
(3, Channel3, channel3, OutputSignal::RMT_SIG_OUT3),
);
);

View File

@ -20,7 +20,7 @@ use smart_leds_trait::{SmartLedsWrite, RGB8};
use crate::pulse_control::ClockSource;
use crate::{
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
@ -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>
where
CHANNEL: OutputChannel,
CHANNEL: ConfiguredChannel,
PIN: OutputPin,
{
/// 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))]
channel
.set_idle_output_level(false)
@ -109,7 +115,7 @@ where
.set_idle_output(true)
.set_clock_source(ClockSource::APB);
channel.assign_pin(pin);
let channel = channel.assign_pin(pin);
Self {
channel,
rmt_buffer: [0; BUFFER_SIZE],
@ -165,7 +171,7 @@ where
impl<CHANNEL, PIN, const BUFFER_SIZE: usize> SmartLedsWrite
for SmartLedsAdapter<CHANNEL, PIN, BUFFER_SIZE>
where
CHANNEL: OutputChannel,
CHANNEL: ConfiguredChannel,
PIN: OutputPin,
{
type Error = LedAdapterError;

View File

@ -10,7 +10,7 @@ use esp32_hal::{
gpio::IO,
pac::Peripherals,
prelude::*,
pulse_control::{OutputChannel, PulseCode, RepeatMode},
pulse_control::{ConfiguredChannel, OutputChannel, PulseCode, RepeatMode},
timer::TimerGroup,
PulseControl,
Rtc,
@ -46,7 +46,7 @@ fn main() -> ! {
.set_idle_output(true);
// 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
let mut seq = [PulseCode {
@ -75,4 +75,4 @@ fn main() -> ! {
.send_pulse_sequence(RepeatMode::SingleShot, &seq)
.unwrap();
}
}
}

View File

@ -10,7 +10,7 @@ use esp32c3_hal::{
gpio::IO,
pac::Peripherals,
prelude::*,
pulse_control::{ClockSource, OutputChannel, PulseCode, RepeatMode},
pulse_control::{ClockSource, ConfiguredChannel, OutputChannel, PulseCode, RepeatMode},
system::SystemExt,
timer::TimerGroup,
PulseControl,
@ -61,7 +61,7 @@ fn main() -> ! {
.set_idle_output(true);
// 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
let mut seq = [PulseCode {

View File

@ -10,7 +10,7 @@ use esp32s2_hal::{
gpio::IO,
pac::Peripherals,
prelude::*,
pulse_control::{OutputChannel, PulseCode, RepeatMode},
pulse_control::{ConfiguredChannel, OutputChannel, PulseCode, RepeatMode},
timer::TimerGroup,
PulseControl,
Rtc,
@ -47,7 +47,7 @@ fn main() -> ! {
.set_idle_output(true);
// 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
let mut seq = [PulseCode {

View File

@ -10,7 +10,7 @@ use esp32s3_hal::{
gpio::IO,
pac::Peripherals,
prelude::*,
pulse_control::{ClockSource, OutputChannel, PulseCode, RepeatMode},
pulse_control::{ConfiguredChannel, ClockSource, OutputChannel, PulseCode, RepeatMode},
timer::TimerGroup,
PulseControl,
Rtc,
@ -55,7 +55,7 @@ fn main() -> ! {
.set_idle_output(true);
// 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
let mut seq = [PulseCode {