use super::{
low_level,
timer::{TimerIFace, TimerSpeed},
};
use crate::{
gpio::{
DriveMode,
OutputConfig,
interconnect::{self, PeripheralOutput},
},
pac::ledc::RegisterBlock,
peripherals::LEDC,
};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FadeError {
StartDuty,
EndDuty,
DutyRange,
Duration,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Duty,
Timer,
Channel,
Fade(FadeError),
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Number {
Channel0 = 0,
Channel1 = 1,
Channel2 = 2,
Channel3 = 3,
Channel4 = 4,
Channel5 = 5,
#[cfg(ledc_channel_count = "8")]
Channel6 = 6,
#[cfg(ledc_channel_count = "8")]
Channel7 = 7,
}
pub mod config {
use crate::{
gpio::DriveMode,
ledc::timer::{TimerIFace, TimerSpeed},
};
#[derive(Copy, Clone)]
pub struct Config<'a, S: TimerSpeed> {
pub timer: &'a dyn TimerIFace<S>,
pub duty_pct: u8,
pub drive_mode: DriveMode,
}
}
pub trait ChannelIFace<'a, S: TimerSpeed + 'a>
where
Channel<'a, S>: ChannelHW,
{
fn configure(&mut self, config: config::Config<'a, S>) -> Result<(), Error>;
fn set_duty(&self, duty_pct: u8) -> Result<(), Error>;
fn start_duty_fade(
&self,
start_duty_pct: u8,
end_duty_pct: u8,
duration_ms: u16,
) -> Result<(), Error>;
fn is_duty_fade_running(&self) -> bool;
}
pub trait ChannelHW {
fn configure_hw(&mut self) -> Result<(), Error>;
fn configure_hw_with_drive_mode(&mut self, cfg: DriveMode) -> Result<(), Error>;
fn set_duty_hw(&self, duty: u32);
fn start_duty_fade_hw(
&self,
start_duty: u32,
duty_inc: bool,
duty_steps: u16,
cycles_per_step: u16,
duty_per_cycle: u16,
);
fn is_duty_fade_running_hw(&self) -> bool;
}
pub struct Channel<'a, S: TimerSpeed> {
ledc: &'a RegisterBlock,
timer: Option<&'a dyn TimerIFace<S>>,
number: Number,
output_pin: interconnect::OutputSignal<'a>,
}
impl<'a, S: TimerSpeed> Channel<'a, S> {
pub fn new(number: Number, output_pin: impl PeripheralOutput<'a>) -> Self {
let ledc = LEDC::regs();
Channel {
ledc,
timer: None,
number,
output_pin: output_pin.into(),
}
}
}
impl<'a, S: TimerSpeed> ChannelIFace<'a, S> for Channel<'a, S>
where
Channel<'a, S>: ChannelHW,
{
fn configure(&mut self, config: config::Config<'a, S>) -> Result<(), Error> {
self.timer = Some(config.timer);
self.set_duty(config.duty_pct)?;
self.configure_hw_with_drive_mode(config.drive_mode)?;
Ok(())
}
fn set_duty(&self, duty_pct: u8) -> Result<(), Error> {
let duty_exp;
if let Some(timer) = self.timer {
if let Some(timer_duty) = timer.duty() {
duty_exp = timer_duty as u32;
} else {
return Err(Error::Timer);
}
} else {
return Err(Error::Channel);
}
let duty_range = 2u32.pow(duty_exp);
let duty_value = (duty_range * duty_pct as u32) / 100;
if duty_pct > 100u8 {
return Err(Error::Duty);
}
self.set_duty_hw(duty_value);
Ok(())
}
fn start_duty_fade(
&self,
start_duty_pct: u8,
end_duty_pct: u8,
duration_ms: u16,
) -> Result<(), Error> {
let duty_exp;
let frequency;
if start_duty_pct > 100u8 {
return Err(Error::Fade(FadeError::StartDuty));
}
if end_duty_pct > 100u8 {
return Err(Error::Fade(FadeError::EndDuty));
}
if let Some(timer) = self.timer {
if let Some(timer_duty) = timer.duty() {
if timer.frequency() > 0 {
duty_exp = timer_duty as u32;
frequency = timer.frequency();
} else {
return Err(Error::Timer);
}
} else {
return Err(Error::Timer);
}
} else {
return Err(Error::Channel);
}
let duty_range = (1u32 << duty_exp) - 1;
let start_duty_value = (duty_range * start_duty_pct as u32) / 100;
let end_duty_value = (duty_range * end_duty_pct as u32) / 100;
let pwm_cycles = (duration_ms as u32) * frequency / 1000;
let abs_duty_diff = end_duty_value.abs_diff(start_duty_value);
let duty_steps: u32 = u16::try_from(abs_duty_diff).unwrap_or(65535).into();
let cycles_per_step: u16 = (pwm_cycles / duty_steps)
.try_into()
.map_err(|_| Error::Fade(FadeError::Duration))
.and_then(|res| {
if res > 1023 {
Err(Error::Fade(FadeError::Duration))
} else {
Ok(res)
}
})?;
let duty_per_cycle: u16 = (abs_duty_diff / duty_steps)
.try_into()
.map_err(|_| Error::Fade(FadeError::DutyRange))?;
self.start_duty_fade_hw(
start_duty_value,
end_duty_value > start_duty_value,
duty_steps.try_into().unwrap(),
cycles_per_step,
duty_per_cycle,
);
Ok(())
}
fn is_duty_fade_running(&self) -> bool {
self.is_duty_fade_running_hw()
}
}
mod ehal1 {
use embedded_hal::pwm::{self, ErrorKind, ErrorType, SetDutyCycle};
use super::{Channel, ChannelHW, Error};
use crate::ledc::timer::TimerSpeed;
impl pwm::Error for Error {
fn kind(&self) -> pwm::ErrorKind {
ErrorKind::Other
}
}
impl<S: TimerSpeed> ErrorType for Channel<'_, S> {
type Error = Error;
}
impl<'a, S: TimerSpeed> SetDutyCycle for Channel<'a, S>
where
Channel<'a, S>: ChannelHW,
{
fn max_duty_cycle(&self) -> u16 {
let duty_exp;
if let Some(timer_duty) = self.timer.and_then(|timer| timer.duty()) {
duty_exp = timer_duty as u32;
} else {
return 0;
}
let duty_range = 2u32.pow(duty_exp);
duty_range as u16
}
fn set_duty_cycle(&mut self, mut duty: u16) -> Result<(), Self::Error> {
let max = self.max_duty_cycle();
duty = if duty > max { max } else { duty };
self.set_duty_hw(duty.into());
Ok(())
}
}
}
impl<S: crate::ledc::timer::TimerSpeed> Channel<'_, S> {
fn set_channel(&mut self, timer_number: u8) {
low_level::set_channel(self.ledc, self.number, timer_number, S::IS_HS);
low_level::start_duty_without_fading(self.ledc, self.number, S::IS_HS);
}
fn start_duty_without_fading(&self) {
low_level::start_duty_without_fading(self.ledc, self.number, S::IS_HS);
}
fn update_channel(&self) {
low_level::update_channel(self.ledc, self.number, S::IS_HS);
}
}
impl<S> ChannelHW for Channel<'_, S>
where
S: crate::ledc::timer::TimerSpeed,
{
fn configure_hw(&mut self) -> Result<(), Error> {
self.configure_hw_with_drive_mode(DriveMode::PushPull)
}
fn configure_hw_with_drive_mode(&mut self, cfg: DriveMode) -> Result<(), Error> {
if let Some(timer) = self.timer {
if !timer.is_configured() {
return Err(Error::Timer);
}
self.output_pin
.apply_output_config(&OutputConfig::default().with_drive_mode(cfg));
self.output_pin.set_output_enable(true);
let timer_number = timer.number() as u8;
self.set_channel(timer_number);
self.update_channel();
let signal = low_level::output_signal(self.number, S::IS_HS);
signal.connect_to(&self.output_pin);
} else {
return Err(Error::Timer);
}
Ok(())
}
fn set_duty_hw(&self, duty: u32) {
low_level::set_duty_hw(self.ledc, self.number, S::IS_HS, duty);
self.start_duty_without_fading();
self.update_channel();
}
fn start_duty_fade_hw(
&self,
start_duty: u32,
duty_inc: bool,
duty_steps: u16,
cycles_per_step: u16,
duty_per_cycle: u16,
) {
low_level::start_duty_fade_hw(
self.ledc,
self.number,
S::IS_HS,
start_duty,
duty_inc,
duty_steps,
cycles_per_step,
duty_per_cycle,
);
self.update_channel();
}
fn is_duty_fade_running_hw(&self) -> bool {
low_level::is_duty_fade_running_hw(self.ledc, self.number, S::IS_HS)
}
}