Add basic LEDC support for esp32, esp32c3, esp32s2 and esp32s3
This commit is contained in:
parent
224e84d9dc
commit
ec6b58ee7d
@ -298,22 +298,22 @@ pub enum OutputSignal {
|
|||||||
VSPICS0 = 68,
|
VSPICS0 = 68,
|
||||||
VSPICS1 = 69,
|
VSPICS1 = 69,
|
||||||
VSPICS2 = 70,
|
VSPICS2 = 70,
|
||||||
LEDC_HS_SIG_0 = 71,
|
LEDC_HS_SIG0 = 71,
|
||||||
LEDC_HS_SIG_1 = 72,
|
LEDC_HS_SIG1 = 72,
|
||||||
LEDC_HS_SIG_2 = 73,
|
LEDC_HS_SIG2 = 73,
|
||||||
LEDC_HS_SIG_3 = 74,
|
LEDC_HS_SIG3 = 74,
|
||||||
LEDC_HS_SIG_4 = 75,
|
LEDC_HS_SIG4 = 75,
|
||||||
LEDC_HS_SIG_5 = 76,
|
LEDC_HS_SIG5 = 76,
|
||||||
LEDC_HS_SIG_6 = 77,
|
LEDC_HS_SIG6 = 77,
|
||||||
LEDC_HS_SIG_7 = 78,
|
LEDC_HS_SIG7 = 78,
|
||||||
LEDC_LS_SIG_0 = 79,
|
LEDC_LS_SIG0 = 79,
|
||||||
LEDC_LS_SIG_1 = 80,
|
LEDC_LS_SIG1 = 80,
|
||||||
LEDC_LS_SIG_2 = 81,
|
LEDC_LS_SIG2 = 81,
|
||||||
LEDC_LS_SIG_3 = 82,
|
LEDC_LS_SIG3 = 82,
|
||||||
LEDC_LS_SIG_4 = 83,
|
LEDC_LS_SIG4 = 83,
|
||||||
LEDC_LS_SIG_5 = 84,
|
LEDC_LS_SIG5 = 84,
|
||||||
LEDC_LS_SIG_6 = 85,
|
LEDC_LS_SIG6 = 85,
|
||||||
LEDC_LS_SIG_7 = 86,
|
LEDC_LS_SIG7 = 86,
|
||||||
RMT_SIG_0 = 87,
|
RMT_SIG_0 = 87,
|
||||||
RMT_SIG_1 = 88,
|
RMT_SIG_1 = 88,
|
||||||
RMT_SIG_2 = 89,
|
RMT_SIG_2 = 89,
|
||||||
|
|||||||
@ -102,6 +102,14 @@ pub enum OutputSignal {
|
|||||||
SPI3_CS0 = 76,
|
SPI3_CS0 = 76,
|
||||||
SPI3_CS1 = 77,
|
SPI3_CS1 = 77,
|
||||||
SPI3_CS2 = 78,
|
SPI3_CS2 = 78,
|
||||||
|
LEDC_LS_SIG0 = 79,
|
||||||
|
LEDC_LS_SIG1 = 80,
|
||||||
|
LEDC_LS_SIG2 = 81,
|
||||||
|
LEDC_LS_SIG3 = 82,
|
||||||
|
LEDC_LS_SIG4 = 83,
|
||||||
|
LEDC_LS_SIG5 = 84,
|
||||||
|
LEDC_LS_SIG6 = 85,
|
||||||
|
LEDC_LS_SIG7 = 86,
|
||||||
RMT_SIG_OUT0 = 87,
|
RMT_SIG_OUT0 = 87,
|
||||||
RMT_SIG_OUT1 = 88,
|
RMT_SIG_OUT1 = 88,
|
||||||
RMT_SIG_OUT2 = 89,
|
RMT_SIG_OUT2 = 89,
|
||||||
|
|||||||
@ -143,6 +143,14 @@ pub enum OutputSignal {
|
|||||||
SPI3_WP = 70,
|
SPI3_WP = 70,
|
||||||
SPI3_CS0 = 71,
|
SPI3_CS0 = 71,
|
||||||
SPI3_CS1 = 72,
|
SPI3_CS1 = 72,
|
||||||
|
LEDC_LS_SIG0 = 73,
|
||||||
|
LEDC_LS_SIG1 = 74,
|
||||||
|
LEDC_LS_SIG2 = 75,
|
||||||
|
LEDC_LS_SIG3 = 76,
|
||||||
|
LEDC_LS_SIG4 = 77,
|
||||||
|
LEDC_LS_SIG5 = 78,
|
||||||
|
LEDC_LS_SIG6 = 79,
|
||||||
|
LEDC_LS_SIG7 = 80,
|
||||||
RMT_SIG_OUT0 = 81,
|
RMT_SIG_OUT0 = 81,
|
||||||
RMT_SIG_OUT1 = 82,
|
RMT_SIG_OUT1 = 82,
|
||||||
RMT_SIG_OUT2 = 83,
|
RMT_SIG_OUT2 = 83,
|
||||||
|
|||||||
348
esp-hal-common/src/ledc/esp32/channel.rs
Normal file
348
esp-hal-common/src/ledc/esp32/channel.rs
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
timer::{TimerIFace, TimerSpeed},
|
||||||
|
HighSpeed,
|
||||||
|
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,
|
||||||
|
Channel6,
|
||||||
|
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.[<$speed sch $num _hpoint>]
|
||||||
|
.write(|w| unsafe { w.[<hpoint>]().bits(0x0) });
|
||||||
|
$self.ledc.[<$speed sch $num _conf0>].modify(|_, w| unsafe {
|
||||||
|
w.[<sig_out_en>]()
|
||||||
|
.set_bit()
|
||||||
|
.[<timer_sel>]()
|
||||||
|
.bits($channel_number)
|
||||||
|
});
|
||||||
|
$self.ledc.[<$speed sch $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
|
||||||
|
.[<$speed sch $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
|
||||||
|
.[<lsch $num _conf0>]
|
||||||
|
.modify(|_, w| w.[<para_up>]().set_bit());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Channel HW interface for HighSpeed channels
|
||||||
|
impl<'a, O> ChannelHW<O> for Channel<'a, HighSpeed, O>
|
||||||
|
where
|
||||||
|
O: OutputPin,
|
||||||
|
{
|
||||||
|
/// Configure Channel HW except for the duty which is set via
|
||||||
|
/// [`Self::set_duty_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, h, 0, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG0);
|
||||||
|
}
|
||||||
|
Number::Channel1 => {
|
||||||
|
set_channel!(self, h, 1, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG1);
|
||||||
|
}
|
||||||
|
Number::Channel2 => {
|
||||||
|
set_channel!(self, h, 2, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG2);
|
||||||
|
}
|
||||||
|
Number::Channel3 => {
|
||||||
|
set_channel!(self, h, 3, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG3);
|
||||||
|
}
|
||||||
|
Number::Channel4 => {
|
||||||
|
set_channel!(self, h, 4, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG4);
|
||||||
|
}
|
||||||
|
Number::Channel5 => {
|
||||||
|
set_channel!(self, h, 5, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG5);
|
||||||
|
}
|
||||||
|
Number::Channel6 => {
|
||||||
|
set_channel!(self, h, 6, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_SIG6);
|
||||||
|
}
|
||||||
|
Number::Channel7 => {
|
||||||
|
set_channel!(self, h, 7, channel_number);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_HS_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, h, 0, duty),
|
||||||
|
Number::Channel1 => set_duty!(self, h, 1, duty),
|
||||||
|
Number::Channel2 => set_duty!(self, h, 2, duty),
|
||||||
|
Number::Channel3 => set_duty!(self, h, 3, duty),
|
||||||
|
Number::Channel4 => set_duty!(self, h, 4, duty),
|
||||||
|
Number::Channel5 => set_duty!(self, h, 5, duty),
|
||||||
|
Number::Channel6 => set_duty!(self, h, 6, duty),
|
||||||
|
Number::Channel7 => set_duty!(self, h, 7, duty),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
Number::Channel6 => {
|
||||||
|
set_channel!(self, l, 6, channel_number);
|
||||||
|
update_channel!(self, 6);
|
||||||
|
self.output_pin
|
||||||
|
.connect_peripheral_to_output(OutputSignal::LEDC_LS_SIG6);
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
Number::Channel6 => set_duty!(self, l, 6, duty),
|
||||||
|
Number::Channel7 => set_duty!(self, l, 7, duty),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
126
esp-hal-common/src/ledc/esp32/mod.rs
Normal file
126
esp-hal-common/src/ledc/esp32/mod.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
//! LEDC (LED PWM Controller) peripheral control
|
||||||
|
//!
|
||||||
|
//! Currently only supports fixed frequency output. Hardware fade support and
|
||||||
|
//! interrupts are not currently implemented. High Speed (only ESP32) and 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();
|
||||||
|
//! ```
|
||||||
|
//! # HighSpeed (only ESP32) Example:
|
||||||
|
//! The following will configure the High Speed Channel0 to 24kHz output with
|
||||||
|
//! 10% duty using the ABPClock ```
|
||||||
|
//! let ledc = LEDC::new(&clock_control, &mut
|
||||||
|
//! system.peripheral_clock_control);
|
||||||
|
//! let mut hstimer0 = ledc.get_timer::<HighSpeed>(timer::Number::Timer0);
|
||||||
|
//! hstimer0
|
||||||
|
//! .configure(timer::config::Config {
|
||||||
|
//! duty: timer::config::Duty::Duty5Bit,
|
||||||
|
//! clock_source: timer::HSClockSource::APBClk,
|
||||||
|
//! frequency: 24u32.kHz(),
|
||||||
|
//! })
|
||||||
|
//! .unwrap();
|
||||||
|
//!
|
||||||
|
//! let mut channel0 = ledc.get_channel(channel::Number::Channel0, led);
|
||||||
|
//! channel0
|
||||||
|
//! .configure(channel::config::Config {
|
||||||
|
//! timer: &hstimer0,
|
||||||
|
//! 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 HighSpeed Timer/Channel
|
||||||
|
pub struct HighSpeed {}
|
||||||
|
/// Used to specify LowSpeed Timer/Channel
|
||||||
|
pub struct LowSpeed {}
|
||||||
|
|
||||||
|
pub trait Speed {}
|
||||||
|
|
||||||
|
impl Speed for HighSpeed {}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
self.ledc.conf.write(|w| w.apb_clk_sel().set_bit());
|
||||||
|
self.ledc
|
||||||
|
.lstimer0_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)
|
||||||
|
}
|
||||||
|
}
|
||||||
355
esp-hal-common/src/ledc/esp32/timer.rs
Normal file
355
esp-hal-common/src/ledc/esp32/timer.rs
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
use super::{LowSpeed, Speed};
|
||||||
|
use crate::{clock::Clocks, pac::ledc, prelude::_fugit_MegahertzU32};
|
||||||
|
/// Timer errors
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Invalid Divisor
|
||||||
|
Divisor,
|
||||||
|
}
|
||||||
|
|
||||||
|
use super::HighSpeed;
|
||||||
|
|
||||||
|
/// Clock source for HS Timers
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
pub enum HSClockSource {
|
||||||
|
// TODO RefTick,
|
||||||
|
APBClk,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
Duty15Bit,
|
||||||
|
Duty16Bit,
|
||||||
|
Duty17Bit,
|
||||||
|
Duty18Bit,
|
||||||
|
Duty19Bit,
|
||||||
|
Duty20Bit,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timer source type for HighSpeed timers
|
||||||
|
impl TimerSpeed for HighSpeed {
|
||||||
|
type ClockSourceType = HSClockSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.lstimer0_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_lstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
Number::Timer1 => self.ledc.lstimer1_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_lstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
Number::Timer2 => self.ledc.lstimer2_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_lstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
Number::Timer3 => self.ledc.lstimer3_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_lstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the timer in HW
|
||||||
|
fn update_hw(&self) {
|
||||||
|
match self.number {
|
||||||
|
Number::Timer0 => self.ledc.lstimer0_conf.modify(|_, w| w.para_up().set_bit()),
|
||||||
|
Number::Timer1 => self.ledc.lstimer1_conf.modify(|_, w| w.para_up().set_bit()),
|
||||||
|
Number::Timer2 => self.ledc.lstimer2_conf.modify(|_, w| w.para_up().set_bit()),
|
||||||
|
Number::Timer3 => self.ledc.lstimer3_conf.modify(|_, w| w.para_up().set_bit()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timer HW implementation for HighSpeed timers
|
||||||
|
impl<'a> TimerHW<HighSpeed> for Timer<'a, HighSpeed> {
|
||||||
|
/// Get the current source timer frequency from the HW
|
||||||
|
fn get_freq_hw(&self) -> Option<_fugit_MegahertzU32> {
|
||||||
|
self.clock_source.map(|cs| match cs {
|
||||||
|
// TODO RefTick HSClockSource::RefTick => self.clock_control_config.apb_clock,
|
||||||
|
HSClockSource::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_hstimer = self.clock_source.unwrap() == HSClockSource::APBClk;
|
||||||
|
match self.number {
|
||||||
|
Number::Timer0 => self.ledc.hstimer0_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_hstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
Number::Timer1 => self.ledc.hstimer1_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_hstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
Number::Timer2 => self.ledc.hstimer2_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_hstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
Number::Timer3 => self.ledc.hstimer3_conf.modify(|_, w| unsafe {
|
||||||
|
w.tick_sel()
|
||||||
|
.bit(sel_hstimer)
|
||||||
|
.rst()
|
||||||
|
.clear_bit()
|
||||||
|
.pause()
|
||||||
|
.clear_bit()
|
||||||
|
.div_num()
|
||||||
|
.bits(divisor)
|
||||||
|
.duty_res()
|
||||||
|
.bits(duty)
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the timer in HW
|
||||||
|
fn update_hw(&self) {
|
||||||
|
// Nothing to do for HS timers
|
||||||
|
}
|
||||||
|
}
|
||||||
273
esp-hal-common/src/ledc/others/channel.rs
Normal file
273
esp-hal-common/src/ledc/others/channel.rs
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
104
esp-hal-common/src/ledc/others/mod.rs
Normal file
104
esp-hal-common/src/ledc/others/mod.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
//! 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
263
esp-hal-common/src/ledc/others/timer.rs
Normal file
263
esp-hal-common/src/ledc/others/timer.rs
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
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()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -40,6 +40,11 @@ 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 prelude;
|
pub mod prelude;
|
||||||
pub mod pulse_control;
|
pub mod pulse_control;
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
|
|||||||
@ -15,6 +15,8 @@ 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,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,6 +21,7 @@ pub enum Peripheral {
|
|||||||
#[cfg(not(feature = "esp32c3"))]
|
#[cfg(not(feature = "esp32c3"))]
|
||||||
I2cExt1,
|
I2cExt1,
|
||||||
Rmt,
|
Rmt,
|
||||||
|
Ledc,
|
||||||
#[cfg(feature = "esp32c3")]
|
#[cfg(feature = "esp32c3")]
|
||||||
ApbSarAdc,
|
ApbSarAdc,
|
||||||
}
|
}
|
||||||
@ -68,6 +69,10 @@ impl PeripheralClockControl {
|
|||||||
perip_clk_en0.modify(|_, w| w.rmt_clk_en().set_bit());
|
perip_clk_en0.modify(|_, w| w.rmt_clk_en().set_bit());
|
||||||
perip_rst_en0.modify(|_, w| w.rmt_rst().clear_bit());
|
perip_rst_en0.modify(|_, w| w.rmt_rst().clear_bit());
|
||||||
}
|
}
|
||||||
|
Peripheral::Ledc => {
|
||||||
|
perip_clk_en0.modify(|_, w| w.ledc_clk_en().set_bit());
|
||||||
|
perip_rst_en0.modify(|_, w| w.ledc_rst().clear_bit());
|
||||||
|
}
|
||||||
#[cfg(feature = "esp32c3")]
|
#[cfg(feature = "esp32c3")]
|
||||||
Peripheral::ApbSarAdc => {
|
Peripheral::ApbSarAdc => {
|
||||||
perip_clk_en0.modify(|_, w| w.apb_saradc_clk_en().set_bit());
|
perip_clk_en0.modify(|_, w| w.apb_saradc_clk_en().set_bit());
|
||||||
|
|||||||
67
esp32-hal/examples/ledc.rs
Normal file
67
esp32-hal/examples/ledc.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//! Turns on LED with the option to change LED intensity depending on `duty` value.
|
||||||
|
//!
|
||||||
|
//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO4)
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
use esp32_hal::{
|
||||||
|
gpio::IO,
|
||||||
|
Serial,
|
||||||
|
timer::TimerGroup,
|
||||||
|
RtcCntl,
|
||||||
|
clock::ClockControl,
|
||||||
|
prelude::*,
|
||||||
|
pac::Peripherals,
|
||||||
|
};
|
||||||
|
use esp_hal_common::ledc::{
|
||||||
|
channel::{self, ChannelIFace},
|
||||||
|
timer::{self, TimerIFace},
|
||||||
|
HighSpeed, LEDC,
|
||||||
|
};
|
||||||
|
use panic_halt as _;
|
||||||
|
use xtensa_lx_rt::entry;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let peripherals = Peripherals::take().unwrap();
|
||||||
|
let mut system = peripherals.DPORT.split();
|
||||||
|
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||||
|
let mut wdt = timer_group0.wdt;
|
||||||
|
let mut serial0 = Serial::new(peripherals.UART0);
|
||||||
|
let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL);
|
||||||
|
|
||||||
|
// Disable watchdog timer
|
||||||
|
wdt.disable();
|
||||||
|
rtc_cntl.set_wdt_global_enable(false);
|
||||||
|
|
||||||
|
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
let led = io.pins.gpio4.into_push_pull_output();
|
||||||
|
|
||||||
|
writeln!(serial0, "\nESP32 Started\n\n").unwrap();
|
||||||
|
|
||||||
|
let ledc = LEDC::new(&clocks, &mut system.peripheral_clock_control);
|
||||||
|
|
||||||
|
let mut hstimer0 = ledc.get_timer::<HighSpeed>(timer::Number::Timer0);
|
||||||
|
|
||||||
|
hstimer0
|
||||||
|
.configure(timer::config::Config {
|
||||||
|
duty: timer::config::Duty::Duty5Bit,
|
||||||
|
clock_source: timer::HSClockSource::APBClk,
|
||||||
|
frequency: 24u32.kHz(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut channel0 = ledc.get_channel(channel::Number::Channel0, led);
|
||||||
|
channel0
|
||||||
|
.configure(channel::config::Config {
|
||||||
|
timer: &hstimer0,
|
||||||
|
duty_pct: 0.1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ pub use esp_hal_common::{
|
|||||||
gpio as gpio_types,
|
gpio as gpio_types,
|
||||||
i2c,
|
i2c,
|
||||||
interrupt,
|
interrupt,
|
||||||
|
ledc,
|
||||||
macros,
|
macros,
|
||||||
pac,
|
pac,
|
||||||
prelude,
|
prelude,
|
||||||
|
|||||||
75
esp32c3-hal/examples/ledc.rs
Normal file
75
esp32c3-hal/examples/ledc.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//! Turns on LED with the option to change LED intensity depending on `duty`
|
||||||
|
//! value.
|
||||||
|
//!
|
||||||
|
//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO4)
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp32c3_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
gpio::IO,
|
||||||
|
pac::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
timer::TimerGroup,
|
||||||
|
RtcCntl,
|
||||||
|
};
|
||||||
|
use esp_hal_common::ledc::{
|
||||||
|
channel::{self, ChannelIFace},
|
||||||
|
timer::{self, TimerIFace},
|
||||||
|
LSGlobalClkSource,
|
||||||
|
LowSpeed,
|
||||||
|
LEDC,
|
||||||
|
};
|
||||||
|
use esp_println;
|
||||||
|
use panic_halt as _;
|
||||||
|
use riscv_rt::entry;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let peripherals = Peripherals::take().unwrap();
|
||||||
|
let mut system = peripherals.SYSTEM.split();
|
||||||
|
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL);
|
||||||
|
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||||
|
let _timer0 = timer_group0.timer0;
|
||||||
|
let mut wdt0 = timer_group0.wdt;
|
||||||
|
let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
|
||||||
|
let mut wdt1 = timer_group1.wdt;
|
||||||
|
|
||||||
|
// Disable watchdog timers
|
||||||
|
rtc_cntl.set_super_wdt_enable(false);
|
||||||
|
rtc_cntl.set_wdt_global_enable(false);
|
||||||
|
wdt0.disable();
|
||||||
|
wdt1.disable();
|
||||||
|
|
||||||
|
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
let led = io.pins.gpio4.into_push_pull_output();
|
||||||
|
|
||||||
|
esp_println::println!("\nESP32C3 Started\n\n");
|
||||||
|
|
||||||
|
let mut ledc = LEDC::new(&clocks, &mut system.peripheral_clock_control);
|
||||||
|
|
||||||
|
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
|
||||||
|
|
||||||
|
let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer2);
|
||||||
|
|
||||||
|
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_pct: 0.1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ pub use esp_hal_common::{
|
|||||||
gpio as gpio_types,
|
gpio as gpio_types,
|
||||||
i2c,
|
i2c,
|
||||||
interrupt,
|
interrupt,
|
||||||
|
ledc,
|
||||||
macros,
|
macros,
|
||||||
pac,
|
pac,
|
||||||
prelude,
|
prelude,
|
||||||
|
|||||||
73
esp32s2-hal/examples/ledc.rs
Normal file
73
esp32s2-hal/examples/ledc.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//! Turns on LED with the option to change LED intensity depending on `duty`
|
||||||
|
//! value.
|
||||||
|
//!
|
||||||
|
//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO4)
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp32s2_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
gpio::IO,
|
||||||
|
pac::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
timer::TimerGroup,
|
||||||
|
RtcCntl,
|
||||||
|
Serial,
|
||||||
|
};
|
||||||
|
use esp_hal_common::ledc::{
|
||||||
|
channel::{self, ChannelIFace},
|
||||||
|
timer::{self, TimerIFace},
|
||||||
|
LSGlobalClkSource,
|
||||||
|
LowSpeed,
|
||||||
|
LEDC,
|
||||||
|
};
|
||||||
|
use esp_println;
|
||||||
|
use panic_halt as _;
|
||||||
|
use xtensa_lx_rt::entry;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let peripherals = Peripherals::take().unwrap();
|
||||||
|
let mut system = peripherals.SYSTEM.split();
|
||||||
|
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||||
|
let _timer0 = timer_group0.timer0;
|
||||||
|
let mut wdt = timer_group0.wdt;
|
||||||
|
let _serial0 = Serial::new(peripherals.UART0);
|
||||||
|
let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL);
|
||||||
|
|
||||||
|
// Disable watchdog timer
|
||||||
|
wdt.disable();
|
||||||
|
rtc_cntl.set_wdt_global_enable(false);
|
||||||
|
|
||||||
|
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
let led = io.pins.gpio4.into_push_pull_output();
|
||||||
|
|
||||||
|
esp_println::println!("\nESP32S2 Started\n\n");
|
||||||
|
|
||||||
|
let mut ledc = LEDC::new(&clocks, &mut system.peripheral_clock_control);
|
||||||
|
|
||||||
|
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
|
||||||
|
|
||||||
|
let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer2);
|
||||||
|
|
||||||
|
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_pct: 0.1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
73
esp32s3-hal/examples/ledc.rs
Normal file
73
esp32s3-hal/examples/ledc.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//! Turns on LED with the option to change LED intensity depending on `duty`
|
||||||
|
//! value.
|
||||||
|
//!
|
||||||
|
//! This assumes that a LED is connected to the pin assigned to `led`. (GPIO4)
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use esp32s3_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
gpio::IO,
|
||||||
|
pac::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
timer::TimerGroup,
|
||||||
|
RtcCntl,
|
||||||
|
Serial,
|
||||||
|
};
|
||||||
|
use esp_hal_common::ledc::{
|
||||||
|
channel::{self, ChannelIFace},
|
||||||
|
timer::{self, TimerIFace},
|
||||||
|
LSGlobalClkSource,
|
||||||
|
LowSpeed,
|
||||||
|
LEDC,
|
||||||
|
};
|
||||||
|
use esp_println;
|
||||||
|
use panic_halt as _;
|
||||||
|
use xtensa_lx_rt::entry;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let peripherals = Peripherals::take().unwrap();
|
||||||
|
let mut system = peripherals.SYSTEM.split();
|
||||||
|
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||||
|
let _timer0 = timer_group0.timer0;
|
||||||
|
let mut wdt = timer_group0.wdt;
|
||||||
|
let mut _serial0 = Serial::new(peripherals.UART0);
|
||||||
|
let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL);
|
||||||
|
|
||||||
|
// Disable watchdog timer
|
||||||
|
wdt.disable();
|
||||||
|
rtc_cntl.set_wdt_global_enable(false);
|
||||||
|
|
||||||
|
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
|
let led = io.pins.gpio4.into_push_pull_output();
|
||||||
|
|
||||||
|
esp_println::println!("\nESP32S3 Started\n\n");
|
||||||
|
|
||||||
|
let mut ledc = LEDC::new(&clocks, &mut system.peripheral_clock_control);
|
||||||
|
|
||||||
|
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
|
||||||
|
|
||||||
|
let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer2);
|
||||||
|
|
||||||
|
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_pct: 0.1,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user