Refactor the LEDC driver to reduce code duplication (#130)

This commit is contained in:
Jesse Braham 2022-08-03 15:48:45 +02:00 committed by GitHub
parent 4d4b60a2a0
commit 601d70dd9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 697 deletions

View File

@ -1,14 +1,16 @@
use paste::paste; use paste::paste;
#[cfg(feature = "esp32")]
use super::HighSpeed;
use super::{ use super::{
timer::{TimerIFace, TimerSpeed}, timer::{TimerIFace, TimerSpeed},
HighSpeed,
LowSpeed, LowSpeed,
}; };
use crate::{ use crate::{
gpio::{types::OutputSignal, OutputPin}, gpio::{types::OutputSignal, OutputPin},
pac::ledc::RegisterBlock, pac::ledc::RegisterBlock,
}; };
/// Channel errors /// Channel errors
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -29,7 +31,9 @@ pub enum Number {
Channel3, Channel3,
Channel4, Channel4,
Channel5, Channel5,
#[cfg(not(feature = "esp32c3"))]
Channel6, Channel6,
#[cfg(not(feature = "esp32c3"))]
Channel7, Channel7,
} }
@ -115,7 +119,7 @@ where
return Err(Error::Channel); return Err(Error::Channel);
} }
let duty_range = 2_u32.pow(duty_exp); let duty_range = 2u32.pow(duty_exp);
let duty_value = (duty_range as f32 * duty_pct) as u32; let duty_value = (duty_range as f32 * duty_pct) as u32;
if duty_value == 0 || duty_pct > 1.0 { if duty_value == 0 || duty_pct > 1.0 {
@ -130,9 +134,10 @@ where
} }
} }
#[cfg(feature = "esp32")]
/// Macro to configure channel parameters in hw /// Macro to configure channel parameters in hw
macro_rules! set_channel { macro_rules! set_channel {
( $self: ident, $speed: ident, $num: literal, $channel_number: ident ) => { ($self: ident, $speed: ident, $num: literal, $channel_number: ident) => {
paste! { paste! {
$self.ledc.[<$speed sch $num _hpoint>] $self.ledc.[<$speed sch $num _hpoint>]
.write(|w| unsafe { w.[<hpoint>]().bits(0x0) }); .write(|w| unsafe { w.[<hpoint>]().bits(0x0) });
@ -158,9 +163,39 @@ macro_rules! set_channel {
}; };
} }
#[cfg(not(feature = "esp32"))]
/// Macro to configure channel parameters in hw
macro_rules! set_channel {
($self: ident, $speed: ident, $num: literal, $channel_number: ident) => {
paste! {
$self.ledc.[<ch $num _hpoint>]
.write(|w| unsafe { w.[<hpoint>]().bits(0x0) });
$self.ledc.[<ch $num _conf0>].modify(|_, w| unsafe {
w.[<sig_out_en>]()
.set_bit()
.[<timer_sel>]()
.bits($channel_number)
});
$self.ledc.[<ch $num _conf1>].write(|w| unsafe {
w.[<duty_start>]()
.set_bit()
.[<duty_inc>]()
.set_bit()
.[<duty_num>]()
.bits(0x1)
.[<duty_cycle>]()
.bits(0x1)
.[<duty_scale>]()
.bits(0x0)
});
}
};
}
#[cfg(feature = "esp32")]
/// Macro to set duty parameters in hw /// Macro to set duty parameters in hw
macro_rules! set_duty { macro_rules! set_duty {
( $self: ident, $speed: ident, $num: literal, $duty: ident ) => { ($self: ident, $speed: ident, $num: literal, $duty: ident) => {
paste! { paste! {
$self.ledc $self.ledc
.[<$speed sch $num _duty>] .[<$speed sch $num _duty>]
@ -169,9 +204,22 @@ macro_rules! set_duty {
}; };
} }
#[cfg(not(feature = "esp32"))]
/// Macro to set duty parameters in hw
macro_rules! set_duty {
($self: ident, $speed: ident, $num: literal, $duty: ident) => {
paste! {
$self.ledc
.[<ch $num _duty>]
.write(|w| unsafe { w.[<duty>]().bits($duty << 4) })
}
};
}
#[cfg(feature = "esp32")]
/// Macro to update channel configuration (only for LowSpeed channels) /// Macro to update channel configuration (only for LowSpeed channels)
macro_rules! update_channel { macro_rules! update_channel {
( $self: ident, $num: literal) => { ($self: ident, $num: literal) => {
paste! { paste! {
$self.ledc $self.ledc
.[<lsch $num _conf0>] .[<lsch $num _conf0>]
@ -180,6 +228,19 @@ macro_rules! update_channel {
}; };
} }
#[cfg(not(feature = "esp32"))]
/// Macro to update channel configuration (only for LowSpeed channels)
macro_rules! update_channel {
($self: ident, $num: literal) => {
paste! {
$self.ledc
.[<ch $num _conf0>]
.modify(|_, w| w.[<para_up>]().set_bit());
}
};
}
#[cfg(feature = "esp32")]
/// Channel HW interface for HighSpeed channels /// Channel HW interface for HighSpeed channels
impl<'a, O> ChannelHW<O> for Channel<'a, HighSpeed, O> impl<'a, O> ChannelHW<O> for Channel<'a, HighSpeed, O>
where where
@ -312,12 +373,14 @@ where
self.output_pin self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG5); .connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG5);
} }
#[cfg(not(feature = "esp32c3"))]
Number::Channel6 => { Number::Channel6 => {
set_channel!(self, l, 6, channel_number); set_channel!(self, l, 6, channel_number);
update_channel!(self, 6); update_channel!(self, 6);
self.output_pin self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG6); .connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG6);
} }
#[cfg(not(feature = "esp32c3"))]
Number::Channel7 => { Number::Channel7 => {
set_channel!(self, l, 7, channel_number); set_channel!(self, l, 7, channel_number);
update_channel!(self, 7); update_channel!(self, 7);
@ -341,7 +404,9 @@ where
Number::Channel3 => set_duty!(self, l, 3, duty), Number::Channel3 => set_duty!(self, l, 3, duty),
Number::Channel4 => set_duty!(self, l, 4, duty), Number::Channel4 => set_duty!(self, l, 4, duty),
Number::Channel5 => set_duty!(self, l, 5, duty), Number::Channel5 => set_duty!(self, l, 5, duty),
#[cfg(not(feature = "esp32c3"))]
Number::Channel6 => set_duty!(self, l, 6, duty), Number::Channel6 => set_duty!(self, l, 6, duty),
#[cfg(not(feature = "esp32c3"))]
Number::Channel7 => set_duty!(self, l, 7, duty), Number::Channel7 => set_duty!(self, l, 7, duty),
}; };
} }

View File

@ -1,16 +1,19 @@
//! LEDC (LED PWM Controller) peripheral control //! LEDC (LED PWM Controller) peripheral control
//! //!
//! Currently only supports fixed frequency output. Hardware fade support and //! Currently only supports fixed-frequency output. Hardware fade support and
//! interrupts are not currently implemented. High Speed (only ESP32) and Low //! interrupts are not currently implemented. High Speed channels are availble
//! Speed channels are available. //! for the ESP32 only, while Low Speed channels are available for all supported
//! chips.
//! //!
//! # LowSpeed Example: //! # LowSpeed Example:
//! The following will configure the Low Speed Channel0 to 24kHz output with
//! 10% duty using the ABPClock ```
//! let mut ledc = LEDC::new(&clock_control, &mut
//! system.peripheral_clock_control);
//! //!
//! The following will configure the Low Speed Channel0 to 24kHz output with
//! 10% duty using the ABPClock
//!
//! ```rust,ignore
//! let mut ledc = LEDC::new(&clock_control, &mut system.peripheral_clock_control);
//! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); //! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
//!
//! let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer0); //! let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer0);
//! lstimer0 //! lstimer0
//! .configure(timer::config::Config { //! .configure(timer::config::Config {
@ -28,11 +31,15 @@
//! }) //! })
//! .unwrap(); //! .unwrap();
//! ``` //! ```
//! # HighSpeed (only ESP32) Example: //!
//! # HighSpeed Example (ESP32 only):
//!
//! The following will configure the High Speed Channel0 to 24kHz output with //! The following will configure the High Speed Channel0 to 24kHz output with
//! 10% duty using the ABPClock ``` //! 10% duty using the ABPClock
//! let ledc = LEDC::new(&clock_control, &mut //!
//! system.peripheral_clock_control); //! ```rust,ignore
//! let ledc = LEDC::new(&clock_control, &mut system.peripheral_clock_control);
//!
//! let mut hstimer0 = ledc.get_timer::<HighSpeed>(timer::Number::Timer0); //! let mut hstimer0 = ledc.get_timer::<HighSpeed>(timer::Number::Timer0);
//! hstimer0 //! hstimer0
//! .configure(timer::config::Config { //! .configure(timer::config::Config {
@ -50,14 +57,17 @@
//! }) //! })
//! .unwrap(); //! .unwrap();
//! ``` //! ```
//!
//! # TODO //! # TODO
//!
//! - Source clock selection
//! - Hardware fade support //! - Hardware fade support
//! - Interrupts //! - Interrupts
use channel::Channel; use self::{
use timer::Timer; channel::Channel,
timer::{Timer, TimerSpeed},
use self::timer::TimerSpeed; };
use crate::{ use crate::{
clock::Clocks, clock::Clocks,
gpio::OutputPin, gpio::OutputPin,
@ -79,13 +89,16 @@ pub struct LEDC<'a> {
clock_control_config: &'a Clocks, clock_control_config: &'a Clocks,
} }
#[cfg(feature = "esp32")]
/// Used to specify HighSpeed Timer/Channel /// Used to specify HighSpeed Timer/Channel
pub struct HighSpeed {} pub struct HighSpeed {}
/// Used to specify LowSpeed Timer/Channel /// Used to specify LowSpeed Timer/Channel
pub struct LowSpeed {} pub struct LowSpeed {}
pub trait Speed {} pub trait Speed {}
#[cfg(feature = "esp32")]
impl Speed for HighSpeed {} impl Speed for HighSpeed {}
impl Speed for LowSpeed {} impl Speed for LowSpeed {}
@ -103,11 +116,21 @@ impl<'a> LEDC<'a> {
} }
/// Set global slow clock source /// Set global slow clock source
#[cfg(feature = "esp32")]
pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) { pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) {
self.ledc.conf.write(|w| w.apb_clk_sel().set_bit()); self.ledc.conf.write(|w| w.apb_clk_sel().set_bit());
self.ledc self.ledc.lstimer0_conf.modify(|_, w| w.para_up().set_bit());
.lstimer0_conf }
.modify(|_, w| w.para_up().set_bit());
#[cfg(not(feature = "esp32"))]
/// Set global slow clock source
pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) {
match clock_source {
LSGlobalClkSource::APBClk => {
self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(1) })
}
}
self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit());
} }
/// Return a new timer /// Return a new timer

View File

@ -1,273 +0,0 @@
use paste::paste;
use super::{
timer::{TimerIFace, TimerSpeed},
LowSpeed,
};
use crate::{
gpio::{types::OutputSignal, OutputPin},
pac::ledc::RegisterBlock,
};
/// Channel errors
#[derive(Debug)]
pub enum Error {
/// Invalid duty % value
Duty,
/// Timer not configured
Timer,
/// Channel not configured
Channel,
}
/// Channel number
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Number {
Channel0,
Channel1,
Channel2,
Channel3,
Channel4,
Channel5,
#[cfg(not(feature = "esp32c3"))]
Channel6,
#[cfg(not(feature = "esp32c3"))]
Channel7,
}
/// Channel configuration
pub mod config {
use crate::ledc::timer::{TimerIFace, TimerSpeed};
/// Channel configuration
#[derive(Copy, Clone)]
pub struct Config<'a, S: TimerSpeed> {
pub timer: &'a dyn TimerIFace<S>,
pub duty_pct: f32,
}
}
/// Channel interface
pub trait ChannelIFace<'a, S: TimerSpeed + 'a, O: OutputPin>
where
Channel<'a, S, O>: ChannelHW<O>,
{
/// Configure channel
fn configure(&mut self, config: config::Config<'a, S>) -> Result<(), Error>;
/// Set channel duty HW
fn set_duty(&self, duty_pct: f32) -> Result<(), Error>;
}
/// Channel HW interface
pub trait ChannelHW<O: OutputPin> {
/// Configure Channel HW except for the duty which is set via
/// [`Self::set_duty_hw`].
fn configure_hw(&mut self) -> Result<(), Error>;
/// Set channel duty HW
fn set_duty_hw(&self, duty: u32);
}
/// Channel struct
pub struct Channel<'a, S: TimerSpeed, O: OutputPin> {
ledc: &'a RegisterBlock,
timer: Option<&'a dyn TimerIFace<S>>,
number: Number,
output_pin: O,
}
impl<'a, S: TimerSpeed, O: OutputPin> Channel<'a, S, O> {
/// Return a new channel
pub fn new(number: Number, output_pin: O) -> Self {
let ledc = unsafe { &*crate::pac::LEDC::ptr() };
Channel {
ledc,
timer: None,
number,
output_pin,
}
}
}
impl<'a, S: TimerSpeed, O: OutputPin> ChannelIFace<'a, S, O> for Channel<'a, S, O>
where
Channel<'a, S, O>: ChannelHW<O>,
{
/// Configure channel
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()?;
Ok(())
}
/// Set duty % of channel
fn set_duty(&self, duty_pct: f32) -> Result<(), Error> {
let duty_exp;
if let Some(timer) = self.timer {
if let Some(timer_duty) = timer.get_duty() {
duty_exp = timer_duty as u32;
} else {
return Err(Error::Timer);
}
} else {
return Err(Error::Channel);
}
let duty_range = 2_u32.pow(duty_exp);
let duty_value = (duty_range as f32 * duty_pct) as u32;
if duty_value == 0 || duty_pct > 1.0 {
// Not enough bits to represent the requested duty % or duty_pct greater than
// 1.0
return Err(Error::Duty);
}
self.set_duty_hw(duty_value);
Ok(())
}
}
/// Macro to configure channel parameters in hw
macro_rules! set_channel {
( $self: ident, $speed: ident, $num: literal, $channel_number: ident ) => {
paste! {
$self.ledc.[<ch $num _hpoint>]
.write(|w| unsafe { w.[<hpoint>]().bits(0x0) });
$self.ledc.[<ch $num _conf0>].modify(|_, w| unsafe {
w.[<sig_out_en>]()
.set_bit()
.[<timer_sel>]()
.bits($channel_number)
});
$self.ledc.[<ch $num _conf1>].write(|w| unsafe {
w.[<duty_start>]()
.set_bit()
.[<duty_inc>]()
.set_bit()
.[<duty_num>]()
.bits(0x1)
.[<duty_cycle>]()
.bits(0x1)
.[<duty_scale>]()
.bits(0x0)
});
}
};
}
/// Macro to set duty parameters in hw
macro_rules! set_duty {
( $self: ident, $speed: ident, $num: literal, $duty: ident ) => {
paste! {
$self.ledc
.[<ch $num _duty>]
.write(|w| unsafe { w.[<duty>]().bits($duty << 4) })
}
};
}
/// Macro to update channel configuration (only for LowSpeed channels)
macro_rules! update_channel {
( $self: ident, $num: literal) => {
paste! {
$self.ledc
.[<ch $num _conf0>]
.modify(|_, w| w.[<para_up>]().set_bit());
}
};
}
/// Channel HW interface for LowSpeed channels
impl<'a, O: OutputPin> ChannelHW<O> for Channel<'a, LowSpeed, O>
where
O: OutputPin,
{
/// Configure Channel HW
fn configure_hw(&mut self) -> Result<(), Error> {
if let Some(timer) = self.timer {
if !timer.is_configured() {
return Err(Error::Timer);
}
self.output_pin.set_to_push_pull_output();
let channel_number = timer.get_number() as u8;
match self.number {
Number::Channel0 => {
set_channel!(self, l, 0, channel_number);
update_channel!(self, 0);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG0);
}
Number::Channel1 => {
set_channel!(self, l, 1, channel_number);
update_channel!(self, 1);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG1);
}
Number::Channel2 => {
set_channel!(self, l, 2, channel_number);
update_channel!(self, 2);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG2);
}
Number::Channel3 => {
set_channel!(self, l, 3, channel_number);
update_channel!(self, 3);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG3);
}
Number::Channel4 => {
set_channel!(self, l, 4, channel_number);
update_channel!(self, 4);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG4);
}
Number::Channel5 => {
set_channel!(self, l, 5, channel_number);
update_channel!(self, 5);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG5);
}
#[cfg(not(feature = "esp32c3"))]
Number::Channel6 => {
set_channel!(self, l, 6, channel_number);
update_channel!(self, 6);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG6);
}
#[cfg(not(feature = "esp32c3"))]
Number::Channel7 => {
set_channel!(self, l, 7, channel_number);
update_channel!(self, 7);
self.output_pin
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG7);
}
}
} else {
return Err(Error::Timer);
}
Ok(())
}
/// Set duty in channel HW
fn set_duty_hw(&self, duty: u32) {
match self.number {
Number::Channel0 => set_duty!(self, l, 0, duty),
Number::Channel1 => set_duty!(self, l, 1, duty),
Number::Channel2 => set_duty!(self, l, 2, duty),
Number::Channel3 => set_duty!(self, l, 3, duty),
Number::Channel4 => set_duty!(self, l, 4, duty),
Number::Channel5 => set_duty!(self, l, 5, duty),
#[cfg(not(feature = "esp32c3"))]
Number::Channel6 => set_duty!(self, l, 6, duty),
#[cfg(not(feature = "esp32c3"))]
Number::Channel7 => set_duty!(self, l, 7, duty),
};
}
}

View File

@ -1,104 +0,0 @@
//! LEDC (LED PWM Controller) peripheral control
//!
//! Currently only supports fixed frequency output. Hardware fade support and
//! interrupts are not currently implemented. Low Speed channels are available.
//!
//! # LowSpeed Example:
//! The following will configure the Low Speed Channel0 to 24kHz output with
//! 10% duty using the ABPClock ```
//! let mut ledc = LEDC::new(&clock_control, &mut
//! system.peripheral_clock_control);
//!
//! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
//! let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer0);
//! lstimer0
//! .configure(timer::config::Config {
//! duty: timer::config::Duty::Duty5Bit,
//! clock_source: timer::LSClockSource::APBClk,
//! frequency: 24u32.kHz(),
//! })
//! .unwrap();
//!
//! let mut channel0 = ledc.get_channel(channel::Number::Channel0, led);
//! channel0
//! .configure(channel::config::Config {
//! timer: &lstimer0,
//! duty: 0.1,
//! })
//! .unwrap();
//! ```
//! # TODO
//! - Hardware fade support
//! - Interrupts
use channel::Channel;
use timer::Timer;
use self::timer::TimerSpeed;
use crate::{
clock::Clocks,
gpio::OutputPin,
system::{Peripheral, PeripheralClockControl},
};
pub mod channel;
pub mod timer;
/// Global slow clock source
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum LSGlobalClkSource {
APBClk,
}
/// LEDC (LED PWM Controller)
pub struct LEDC<'a> {
ledc: &'a crate::pac::ledc::RegisterBlock,
clock_control_config: &'a Clocks,
}
/// Used to specify LowSpeed Timer/Channel
pub struct LowSpeed {}
pub trait Speed {}
impl Speed for LowSpeed {}
impl<'a> LEDC<'a> {
/// Return a new LEDC
pub fn new(clock_control_config: &'a Clocks, system: &mut PeripheralClockControl) -> Self {
system.enable(Peripheral::Ledc);
let ledc = unsafe { &*crate::pac::LEDC::ptr() };
LEDC {
ledc,
clock_control_config,
}
}
/// Set global slow clock source
pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) {
match _clock_source {
LSGlobalClkSource::APBClk => {
self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(1) })
} /* LSGlobalClkSource::XTALClk => {
* self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(3) })
* } */
}
self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit());
}
/// Return a new timer
pub fn get_timer<S: TimerSpeed>(&self, number: timer::Number) -> Timer<S> {
Timer::new(self.ledc, self.clock_control_config, number)
}
/// Return a new channel
pub fn get_channel<S: TimerSpeed, O: OutputPin>(
&self,
number: channel::Number,
output_pin: O,
) -> Channel<S, O> {
Channel::new(number, output_pin)
}
}

View File

@ -1,263 +0,0 @@
use super::{LowSpeed, Speed};
use crate::{clock::Clocks, pac::ledc, prelude::_fugit_MegahertzU32};
/// Timer errors
#[derive(Debug)]
pub enum Error {
/// Invalid Divisor
Divisor,
}
/// Clock source for LS Timers
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum LSClockSource {
APBClk,
// TODO SLOWClk
// SLOWClk,
}
/// Timer number
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Number {
Timer0,
Timer1,
Timer2,
Timer3,
}
/// Timer configuration
pub mod config {
use crate::prelude::_fugit_HertzU32;
/// Number of bits reserved for duty cycle adjustment
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Duty {
Duty1Bit = 1,
Duty2Bit,
Duty3Bit,
Duty4Bit,
Duty5Bit,
Duty6Bit,
Duty7Bit,
Duty8Bit,
Duty9Bit,
Duty10Bit,
Duty11Bit,
Duty12Bit,
Duty13Bit,
Duty14Bit,
}
/// Timer configuration
#[derive(Copy, Clone)]
pub struct Config<CS> {
pub duty: Duty,
pub clock_source: CS,
pub frequency: _fugit_HertzU32,
}
}
/// Trait defining the type of timer source
pub trait TimerSpeed: Speed {
type ClockSourceType;
}
/// Timer source type for LowSpeed timers
impl TimerSpeed for LowSpeed {
type ClockSourceType = LSClockSource;
}
/// Interface for Timers
pub trait TimerIFace<S: TimerSpeed> {
/// Return the frequency of the timer
fn get_freq(&self) -> Option<_fugit_MegahertzU32>;
/// Configure the timer
fn configure(&mut self, config: config::Config<S::ClockSourceType>) -> Result<(), Error>;
/// Check if the timer has been configured
fn is_configured(&self) -> bool;
/// Return the duty resolution of the timer
fn get_duty(&self) -> Option<config::Duty>;
/// Return the timer number
fn get_number(&self) -> Number;
}
/// Interface for HW configuration of timer
pub trait TimerHW<S: TimerSpeed> {
/// Get the current source timer frequency from the HW
fn get_freq_hw(&self) -> Option<_fugit_MegahertzU32>;
/// Configure the HW for the timer
fn configure_hw(&self, divisor: u32);
/// Update the timer in HW
fn update_hw(&self);
}
/// Timer struct
pub struct Timer<'a, S: TimerSpeed> {
ledc: &'a crate::pac::ledc::RegisterBlock,
clock_control_config: &'a Clocks,
number: Number,
duty: Option<config::Duty>,
configured: bool,
clock_source: Option<S::ClockSourceType>,
}
impl<'a, S: TimerSpeed> TimerIFace<S> for Timer<'a, S>
where
Timer<'a, S>: TimerHW<S>,
{
/// Return the frequency of the timer
fn get_freq(&self) -> Option<_fugit_MegahertzU32> {
self.get_freq_hw()
}
/// Configure the timer
fn configure(&mut self, config: config::Config<S::ClockSourceType>) -> Result<(), Error> {
self.duty = Some(config.duty);
self.clock_source = Some(config.clock_source);
let src_freq: u32 = self.get_freq().unwrap().to_Hz();
let precision = 2_u64.pow(config.duty as u32);
let frequency: u32 = config.frequency.raw();
let divisor = (((src_freq as u64) << 8) + ((frequency as u64 * precision) / 2))
/ (frequency as u64 * precision);
if divisor >= 0x10_0000 || divisor == 0 {
return Err(Error::Divisor);
}
self.configure_hw(divisor as u32);
self.update_hw();
self.configured = true;
Ok(())
}
/// Check if the timer has been configured
fn is_configured(&self) -> bool {
self.configured
}
/// Return the duty resolution of the timer
fn get_duty(&self) -> Option<config::Duty> {
self.duty
}
/// Return the timer number
fn get_number(&self) -> Number {
self.number
}
}
impl<'a, S: TimerSpeed> Timer<'a, S> {
/// Create a new intance of a timer
pub fn new(
ledc: &'a ledc::RegisterBlock,
clock_control_config: &'a Clocks,
number: Number,
) -> Self {
Timer {
ledc,
clock_control_config,
number,
duty: None,
configured: false,
clock_source: None,
}
}
// TODO This function will be relevant when we add other clk sources
// Helper function that return the current frequency of the LowSpeed global
// source
// fn get_slow_clock_freq(&self) -> _fugit_MegahertzU32 {
// if self.ledc.conf.read().apb_clk_sel().bit_is_clear() {
// 8u32.MHz().into()
// } else {
// // 80u32.MHz().into()
// self.clock_control_config.apb_clock
// }
// }
}
/// Timer HW implementation for LowSpeed timers
impl<'a> TimerHW<LowSpeed> for Timer<'a, LowSpeed> {
/// Get the current source timer frequency from the HW
fn get_freq_hw(&self) -> Option<_fugit_MegahertzU32> {
self.clock_source.map(|cs| match cs {
LSClockSource::APBClk => self.clock_control_config.apb_clock,
})
}
/// Configure the HW for the timer
fn configure_hw(&self, divisor: u32) {
let duty = self.duty.unwrap() as u8;
let sel_lstimer = self.clock_source.unwrap() == LSClockSource::APBClk;
match self.number {
Number::Timer0 => self.ledc.timer0_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
Number::Timer1 => self.ledc.timer1_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
Number::Timer2 => self.ledc.timer2_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
Number::Timer3 => self.ledc.timer3_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
};
}
/// Update the timer in HW
fn update_hw(&self) {
match self.number {
Number::Timer0 => self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit()),
Number::Timer1 => self.ledc.timer1_conf.modify(|_, w| w.para_up().set_bit()),
Number::Timer2 => self.ledc.timer2_conf.modify(|_, w| w.para_up().set_bit()),
Number::Timer3 => self.ledc.timer3_conf.modify(|_, w| w.para_up().set_bit()),
};
}
}

View File

@ -1,5 +1,10 @@
use fugit::MegahertzU32;
#[cfg(feature = "esp32")]
use super::HighSpeed;
use super::{LowSpeed, Speed}; use super::{LowSpeed, Speed};
use crate::{clock::Clocks, pac::ledc, prelude::_fugit_MegahertzU32}; use crate::{clock::Clocks, pac::ledc};
/// Timer errors /// Timer errors
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -7,13 +12,12 @@ pub enum Error {
Divisor, Divisor,
} }
use super::HighSpeed; #[cfg(feature = "esp32")]
/// Clock source for HS Timers /// Clock source for HS Timers
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum HSClockSource { pub enum HSClockSource {
// TODO RefTick,
APBClk, APBClk,
// TODO RefTick,
} }
/// Clock source for LS Timers /// Clock source for LS Timers
@ -21,7 +25,6 @@ pub enum HSClockSource {
pub enum LSClockSource { pub enum LSClockSource {
APBClk, APBClk,
// TODO SLOWClk // TODO SLOWClk
// SLOWClk,
} }
/// Timer number /// Timer number
@ -35,8 +38,7 @@ pub enum Number {
/// Timer configuration /// Timer configuration
pub mod config { pub mod config {
use fugit::HertzU32;
use crate::prelude::_fugit_HertzU32;
/// Number of bits reserved for duty cycle adjustment /// Number of bits reserved for duty cycle adjustment
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
@ -55,11 +57,17 @@ pub mod config {
Duty12Bit, Duty12Bit,
Duty13Bit, Duty13Bit,
Duty14Bit, Duty14Bit,
#[cfg(feature = "esp32")]
Duty15Bit, Duty15Bit,
#[cfg(feature = "esp32")]
Duty16Bit, Duty16Bit,
#[cfg(feature = "esp32")]
Duty17Bit, Duty17Bit,
#[cfg(feature = "esp32")]
Duty18Bit, Duty18Bit,
#[cfg(feature = "esp32")]
Duty19Bit, Duty19Bit,
#[cfg(feature = "esp32")]
Duty20Bit, Duty20Bit,
} }
@ -68,7 +76,7 @@ pub mod config {
pub struct Config<CS> { pub struct Config<CS> {
pub duty: Duty, pub duty: Duty,
pub clock_source: CS, pub clock_source: CS,
pub frequency: _fugit_HertzU32, pub frequency: HertzU32,
} }
} }
@ -82,6 +90,7 @@ impl TimerSpeed for LowSpeed {
type ClockSourceType = LSClockSource; type ClockSourceType = LSClockSource;
} }
#[cfg(feature = "esp32")]
/// Timer source type for HighSpeed timers /// Timer source type for HighSpeed timers
impl TimerSpeed for HighSpeed { impl TimerSpeed for HighSpeed {
type ClockSourceType = HSClockSource; type ClockSourceType = HSClockSource;
@ -90,7 +99,7 @@ impl TimerSpeed for HighSpeed {
/// Interface for Timers /// Interface for Timers
pub trait TimerIFace<S: TimerSpeed> { pub trait TimerIFace<S: TimerSpeed> {
/// Return the frequency of the timer /// Return the frequency of the timer
fn get_freq(&self) -> Option<_fugit_MegahertzU32>; fn get_freq(&self) -> Option<MegahertzU32>;
/// Configure the timer /// Configure the timer
fn configure(&mut self, config: config::Config<S::ClockSourceType>) -> Result<(), Error>; fn configure(&mut self, config: config::Config<S::ClockSourceType>) -> Result<(), Error>;
@ -108,7 +117,7 @@ pub trait TimerIFace<S: TimerSpeed> {
/// Interface for HW configuration of timer /// Interface for HW configuration of timer
pub trait TimerHW<S: TimerSpeed> { pub trait TimerHW<S: TimerSpeed> {
/// Get the current source timer frequency from the HW /// Get the current source timer frequency from the HW
fn get_freq_hw(&self) -> Option<_fugit_MegahertzU32>; fn get_freq_hw(&self) -> Option<MegahertzU32>;
/// Configure the HW for the timer /// Configure the HW for the timer
fn configure_hw(&self, divisor: u32); fn configure_hw(&self, divisor: u32);
@ -132,7 +141,7 @@ where
Timer<'a, S>: TimerHW<S>, Timer<'a, S>: TimerHW<S>,
{ {
/// Return the frequency of the timer /// Return the frequency of the timer
fn get_freq(&self) -> Option<_fugit_MegahertzU32> { fn get_freq(&self) -> Option<MegahertzU32> {
self.get_freq_hw() self.get_freq_hw()
} }
@ -141,8 +150,9 @@ where
self.duty = Some(config.duty); self.duty = Some(config.duty);
self.clock_source = Some(config.clock_source); self.clock_source = Some(config.clock_source);
// TODO: we should return some error here if `unwrap()` fails
let src_freq: u32 = self.get_freq().unwrap().to_Hz(); let src_freq: u32 = self.get_freq().unwrap().to_Hz();
let precision = 2_u64.pow(config.duty as u32); let precision = 2u64.pow(config.duty as u32);
let frequency: u32 = config.frequency.raw(); let frequency: u32 = config.frequency.raw();
let divisor = (((src_freq as u64) << 8) + ((frequency as u64 * precision) / 2)) let divisor = (((src_freq as u64) << 8) + ((frequency as u64 * precision) / 2))
@ -192,33 +202,23 @@ impl<'a, S: TimerSpeed> Timer<'a, S> {
clock_source: None, clock_source: None,
} }
} }
// TODO This function will be relevant when we add other clk sources
// Helper function that return the current frequency of the LowSpeed global
// source
// fn get_slow_clock_freq(&self) -> _fugit_MegahertzU32 {
// if self.ledc.conf.read().apb_clk_sel().bit_is_clear() {
// 8u32.MHz().into()
// } else {
// // 80u32.MHz().into()
// self.clock_control_config.apb_clock
// }
// }
} }
/// Timer HW implementation for LowSpeed timers /// Timer HW implementation for LowSpeed timers
impl<'a> TimerHW<LowSpeed> for Timer<'a, LowSpeed> { impl<'a> TimerHW<LowSpeed> for Timer<'a, LowSpeed> {
/// Get the current source timer frequency from the HW /// Get the current source timer frequency from the HW
fn get_freq_hw(&self) -> Option<_fugit_MegahertzU32> { fn get_freq_hw(&self) -> Option<MegahertzU32> {
self.clock_source.map(|cs| match cs { self.clock_source.map(|cs| match cs {
LSClockSource::APBClk => self.clock_control_config.apb_clock, LSClockSource::APBClk => self.clock_control_config.apb_clock,
}) })
} }
#[cfg(feature = "esp32")]
/// Configure the HW for the timer /// Configure the HW for the timer
fn configure_hw(&self, divisor: u32) { fn configure_hw(&self, divisor: u32) {
let duty = self.duty.unwrap() as u8; let duty = self.duty.unwrap() as u8;
let sel_lstimer = self.clock_source.unwrap() == LSClockSource::APBClk; let sel_lstimer = self.clock_source == Some(LSClockSource::APBClk);
match self.number { match self.number {
Number::Timer0 => self.ledc.lstimer0_conf.modify(|_, w| unsafe { Number::Timer0 => self.ledc.lstimer0_conf.modify(|_, w| unsafe {
w.tick_sel() w.tick_sel()
@ -271,6 +271,65 @@ impl<'a> TimerHW<LowSpeed> for Timer<'a, LowSpeed> {
}; };
} }
#[cfg(not(feature = "esp32"))]
/// Configure the HW for the timer
fn configure_hw(&self, divisor: u32) {
let duty = self.duty.unwrap() as u8;
let sel_lstimer = self.clock_source == Some(LSClockSource::APBClk);
match self.number {
Number::Timer0 => self.ledc.timer0_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
Number::Timer1 => self.ledc.timer1_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
Number::Timer2 => self.ledc.timer2_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
Number::Timer3 => self.ledc.timer3_conf.modify(|_, w| unsafe {
w.tick_sel()
.bit(sel_lstimer)
.rst()
.clear_bit()
.pause()
.clear_bit()
.clk_div()
.bits(divisor)
.duty_res()
.bits(duty)
}),
};
}
#[cfg(feature = "esp32")]
/// Update the timer in HW /// Update the timer in HW
fn update_hw(&self) { fn update_hw(&self) {
match self.number { match self.number {
@ -280,12 +339,24 @@ impl<'a> TimerHW<LowSpeed> for Timer<'a, LowSpeed> {
Number::Timer3 => self.ledc.lstimer3_conf.modify(|_, w| w.para_up().set_bit()), Number::Timer3 => self.ledc.lstimer3_conf.modify(|_, w| w.para_up().set_bit()),
}; };
} }
#[cfg(not(feature = "esp32"))]
/// Update the timer in HW
fn update_hw(&self) {
match self.number {
Number::Timer0 => self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit()),
Number::Timer1 => self.ledc.timer1_conf.modify(|_, w| w.para_up().set_bit()),
Number::Timer2 => self.ledc.timer2_conf.modify(|_, w| w.para_up().set_bit()),
Number::Timer3 => self.ledc.timer3_conf.modify(|_, w| w.para_up().set_bit()),
};
}
} }
#[cfg(feature = "esp32")]
/// Timer HW implementation for HighSpeed timers /// Timer HW implementation for HighSpeed timers
impl<'a> TimerHW<HighSpeed> for Timer<'a, HighSpeed> { impl<'a> TimerHW<HighSpeed> for Timer<'a, HighSpeed> {
/// Get the current source timer frequency from the HW /// Get the current source timer frequency from the HW
fn get_freq_hw(&self) -> Option<_fugit_MegahertzU32> { fn get_freq_hw(&self) -> Option<MegahertzU32> {
self.clock_source.map(|cs| match cs { self.clock_source.map(|cs| match cs {
// TODO RefTick HSClockSource::RefTick => self.clock_control_config.apb_clock, // TODO RefTick HSClockSource::RefTick => self.clock_control_config.apb_clock,
HSClockSource::APBClk => self.clock_control_config.apb_clock, HSClockSource::APBClk => self.clock_control_config.apb_clock,
@ -295,7 +366,8 @@ impl<'a> TimerHW<HighSpeed> for Timer<'a, HighSpeed> {
/// Configure the HW for the timer /// Configure the HW for the timer
fn configure_hw(&self, divisor: u32) { fn configure_hw(&self, divisor: u32) {
let duty = self.duty.unwrap() as u8; let duty = self.duty.unwrap() as u8;
let sel_hstimer = self.clock_source.unwrap() == HSClockSource::APBClk; let sel_hstimer = self.clock_source == Some(HSClockSource::APBClk);
match self.number { match self.number {
Number::Timer0 => self.ledc.hstimer0_conf.modify(|_, w| unsafe { Number::Timer0 => self.ledc.hstimer0_conf.modify(|_, w| unsafe {
w.tick_sel() w.tick_sel()

View File

@ -40,10 +40,6 @@ pub mod i2c;
#[cfg_attr(target_arch = "riscv32", path = "interrupt/riscv.rs")] #[cfg_attr(target_arch = "riscv32", path = "interrupt/riscv.rs")]
#[cfg_attr(target_arch = "xtensa", path = "interrupt/xtensa.rs")] #[cfg_attr(target_arch = "xtensa", path = "interrupt/xtensa.rs")]
pub mod interrupt; pub mod interrupt;
#[cfg_attr(feature = "esp32", path = "ledc/esp32/mod.rs")]
#[cfg_attr(feature = "esp32c3", path = "ledc/others/mod.rs")]
#[cfg_attr(feature = "esp32s2", path = "ledc/others/mod.rs")]
#[cfg_attr(feature = "esp32s3", path = "ledc/others/mod.rs")]
pub mod ledc; pub mod ledc;
pub mod prelude; pub mod prelude;
pub mod pulse_control; pub mod pulse_control;

View File

@ -15,8 +15,6 @@ pub use embedded_hal::{
pub use fugit::{ pub use fugit::{
ExtU32 as _fugit_ExtU32, ExtU32 as _fugit_ExtU32,
ExtU64 as _fugit_ExtU64, ExtU64 as _fugit_ExtU64,
HertzU32 as _fugit_HertzU32,
MegahertzU32 as _fugit_MegahertzU32,
RateExtU32 as _fugit_RateExtU32, RateExtU32 as _fugit_RateExtU32,
RateExtU64 as _fugit_RateExtU64, RateExtU64 as _fugit_RateExtU64,
}; };