From cf63e752e135696af76f94406b43fc2cdd2e9f65 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Fri, 28 Oct 2022 19:44:32 +0300 Subject: [PATCH 01/10] initial commit --- chips/esp32-c3/src/led_pwm.rs | 267 ++++++++++++++++++++++++++++++++++ chips/esp32-c3/src/lib.rs | 1 + chips/esp32-c3/src/sysreg.rs | 39 ++++- 3 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 chips/esp32-c3/src/led_pwm.rs diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs new file mode 100644 index 0000000000..1268c222e4 --- /dev/null +++ b/chips/esp32-c3/src/led_pwm.rs @@ -0,0 +1,267 @@ +use kernel::utilities::registers::interfaces::ReadWriteable; +use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; +use kernel::utilities::StaticRef; +use kernel::ErrorCode; + +pub const LED_PWM_BASE: StaticRef = + unsafe { StaticRef::new(0x6001_9000 as *const LedPwmRegisters) }; + +register_structs! { + pub LedPwmRegisters { //<- possible naming or "LedcRegisters" + (0x0000 => ledc_ch0_conf0: ReadWrite), + (0x0004 => ledc_ch0_hpoint: ReadWrite), + (0x0008 => ledc_ch0_duty: ReadWrite), + (0x000C => ledc_ch0_conf1: ReadWrite), + (0x0010 => ledc_ch0_duty_r: ReadWrite), + (0x0014 => ledc_ch1_conf0: ReadWrite), + (0x0018 => ledc_ch1_hpoint: ReadWrite), + (0x001C => ledc_ch1_duty: ReadWrite), + (0x0020 => ledc_ch1_conf1: ReadWrite), + (0x0024 => ledc_ch1_duty_r: ReadWrite), + (0x0028 => ledc_ch2_conf0: ReadWrite), + (0x002C => ledc_ch2_hpoint: ReadWrite), + (0x0030 => ledc_ch2_duty: ReadWrite), + (0x0034 => ledc_ch2_conf1: ReadWrite), + (0x0038 => ledc_ch2_duty_r: ReadWrite), + (0x003C => ledc_ch3_conf0: ReadWrite), + (0x0040 => ledc_ch3_hpoint: ReadWrite), + (0x0044 => ledc_ch3_duty: ReadWrite), + (0x0048 => ledc_ch3_conf1: ReadWrite), + (0x004C => ledc_ch3_duty_r: ReadWrite), + (0x0050 => ledc_ch4_conf0: ReadWrite), + (0x0054 => ledc_ch4_hpoint: ReadWrite), + (0x0058 => ledc_ch4_duty: ReadWrite), + (0x005C => ledc_ch4_conf1: ReadWrite), + (0x0060 => ledc_ch4_duty_r: ReadWrite), + (0x0064 => ledc_ch5_conf0: ReadWrite), + (0x0068 => ledc_ch5_hpoint: ReadWrite), + (0x006C => ledc_ch5_duty: ReadWrite), + (0x0070 => ledc_ch5_conf1: ReadWrite), + (0x0074 => ledc_ch5_duty_r: ReadWrite), + (0x0078 => _reserved0), + (0x00A0 => ledc_timer0_conf: ReadWrite), + (0x00A4 => ledc_timer0_value: ReadWrite), + (0x00A8 => ledc_timer1_conf: ReadWrite), + (0x00AC => ledc_timer1_value: ReadWrite), + (0x00B0 => ledc_timer2_conf: ReadWrite), + (0x00B4 => ledc_timer2_value: ReadWrite), + (0x00B8 => ledc_timer3_conf: ReadWrite), + (0x00BC => ledc_timer3_value: ReadWrite), + (0x00C0 => ledc_int_raw: ReadWrite), + (0x00C4 => ledc_int_st: ReadWrite), + (0x00C8 => ledc_int_ena: ReadWrite), + (0x00CC => ledc_int_clr: ReadWrite), + (0x00D0 => ledc_conf: ReadWrite), + (0x00D4 => _reserved1), + (0x00FC => ledc_date: ReadWrite), + (0x0100 => @END), + } +} + +register_bitfields! [u32, + //($x) value used for timers, value between [0, 3] + //($n) value used for pwm generators, value between [0, 5] + + //naming suggestions are appreciated + LEDC_CH_CONF0 [ + //used to select one of the timers on channel ($n) + //($n): select Timer($n) + TIMER_SEL_CH OFFSET(0) NUMBITS(2) [], + //set to enable signal output on channel ($n) + SIG_OUT_EN_CH OFFSET(2) NUMBITS(1) [], + //used to control the output value when channel ($n) is inactive(LEDC_SIG_OUT_EN_CH($n) == 0) + IDLE_LV_CH OFFSET(3) NUMBITS(1) [], + //used to update the listed fields below for channel ($n); is automatically cleared by hardware + //HPOINT_CHn, DUTY_START_CHn, SIG_OUT_EN_CHn, TIMER_SEL_CHn, DUTY_NUM_CHn, DUTY_CYCLE_CHn, + //DUTY_SCALE_CHn, DUTY_INC_CHn and OVF_CNT_EN_CHn + PARA_UP_CH OFFSET(4) NUMBITS(1) [], + //used to configure maximum times of overflow - 1 + //LEDC_OVF_CNT_CH($n)_INT will be triggered when channel ($n) overflows for (LEDC_OVF_NUM_CH($n) + 1) times + OVF_NUM_CH OFFSET(5) NUMBITS(10) [], + //used to count the number of times when the timer selected by channel ($n) overflows + OVF_CNT_EN_CH OFFSET(15) NUMBITS(1) [], + //set to reset timer-overflow counter of channel ($n) + OVF_CNT_RESET_CH OFFSET(16) NUMBITS(1) [], + ], + LEDC_CH_CONF1 [ + DUTY_SCALE_CH OFFSET(0) NUMBITS(10) [], + DUTY_CYCLE_CH OFFSET(10) NUMBITS(10) [], + DUTY_NUM_CH OFFSET(20) NUMBITS(10) [], + DUTY_INC_CH OFFSET(30) NUMBITS(1) [], + DUTY_START_CH OFFSET(31) NUMBITS(1) [], + ], + LEDC_CONF [ + //used to select the clock source for all 4 timers + APB_CLK_SEL OFFSET(0) NUMBITS(2) [ + APB_CLK = 1, + FOSC_CLK = 2, + XTAL_CLK = 3, + ], + //used to control clock + //0: support clock only when application writes registers + //1: force clock on register + CLK_EN OFFSET(31) NUMBITS(1) [], + ], + LEDC_CH_HPOINT [ + HPOINT_CH OFFSET(0) NUMBITS(14) [], + ], + LEDC_CH_DUTY [ + DUTY_CH OFFSET(0) NUMBITS(19) [], + ], + LEDC_CH_DUTY_R [ + DUTY_R_CH OFFSET(0) NUMBITS(19) [], + ], + LEDC_TIMER_CONF [ + //used to control range of counter of timer ($x) + TIMER_DUTY_RES OFFSET(0) NUMBITS(4) [], + //used to configure the divisor for the divider of timer ($x) + CLK_DIV_TIMER OFFSET(4) NUMBITS(18) [], + //used to suspend the counter of timer ($x) + TIMER_PAUSE OFFSET(22) NUMBITS(1) [], + //used to reset timer ($x); counter will show 0 after reset + TIMER_RST OFFSET(23) NUMBITS(1) [], + //set to update LEDC_CLK_DIV_TIMER($x) and LEDC_TIMER($x)_DUTY_RES + TIMER_PARA_UP OFFSET(25) NUMBITS(1) [], + ], + LEDC_TIMER_VALUE [ + //stores the current counter value of timer ($x) + TIMER_CNT OFFSET(0) NUMBITS(14) [], + ], + LEDC_INT [ + //RAW: + //triggered when timer ($x) has reached its maximum counter value + //ST: + //masked interrupt status bit for the LEDC_TIMER($x)_OVF_INT interrupt + //when LEDC_TIMER($x)_OVF_INT_ENA is set to 1 + //ENA: + //set to enable LEDC_TIMER($x)_OVF_INT interrupt + //CLR: + //set to clear LEDC_TIMER($x)_OVF_INT interrupt + TIMER0_OVF OFFSET(0) NUMBITS(1) [], + TIMER1_OVF OFFSET(1) NUMBITS(1) [], + TIMER2_OVF OFFSET(2) NUMBITS(1) [], + TIMER3_OVF OFFSET(3) NUMBITS(1) [], + //RAW: + //interrupt raw bit for channel ($n) + //triggered when the gradual change of duty has finished + //ST: + //masked interrupt status bit for LEDC_DUTY_CHNG_END_CH($n)_INT interrupt + //when LEDC_DUTY_CHNG_CH($n)_INT_ENA is set to 1 + //ENA: + //set to enable LEDC_DUTY_CHNG_END_CH($n)_INT interrupt + //CLR: + //set to clear LEDC_DUTY_CHNG_END_CH($n)_INT interrupt + DUTY_CHNG_END_CH0 OFFSET(4) NUMBITS(1) [], + DUTY_CHNG_END_CH1 OFFSET(5) NUMBITS(1) [], + DUTY_CHNG_END_CH2 OFFSET(6) NUMBITS(1) [], + DUTY_CHNG_END_CH3 OFFSET(7) NUMBITS(1) [], + DUTY_CHNG_END_CH4 OFFSET(8) NUMBITS(1) [], + DUTY_CHNG_END_CH5 OFFSET(9) NUMBITS(1) [], + //RAW: + //interrupt raw bit for channel ($n) + //triggered when overflow counter has reached value specified by LEDC_OVF_NUM_CH($n) + //ST: + //masked interrupt status bit for LEDC_DUTY_CNT_CH($n)_INT interrupt + //when LEDC_OVF_CNT_CH($n)_INT_ENA is set to 1 + //ENA: + //set to enable LEDC_OVF_CH($n)_INT interrupt + //CLR: + //set to clear LEDC_OVF_CNT_CH($n)_INT interrupt + OVF_CNT_CH0 OFFSET(10) NUMBITS(1) [], + OVF_CNT_CH1 OFFSET(11) NUMBITS(1) [], + OVF_CNT_CH2 OFFSET(12) NUMBITS(1) [], + OVF_CNT_CH3 OFFSET(13) NUMBITS(1) [], + OVF_CNT_CH4 OFFSET(14) NUMBITS(1) [], + OVF_CNT_CH5 OFFSET(15) NUMBITS(1) [], + ], + LEDC_DATE [ + //version control register + LEDC_DATE OFFSET(0) NUMBITS(32) [], + ] +]; + +struct LedPwm { + registers: StaticRef, +} + +impl LedPwm { + pub const fn new() -> Self { + LedPwm { + registers: LED_PWM_BASE, + } + } + + pub fn use_apb_clock_source(&self) { + self.registers + .ledc_conf + .modify(LEDC_CONF::APB_CLK_SEL::APB_CLK); + } + + pub fn use_fosc_clk_source(&self) { + self.registers + .ledc_conf + .modify(LEDC_CONF::APB_CLK_SEL::FOSC_CLK); + } + + pub fn use_xtal_clk_source(&self) { + self.registers + .ledc_conf + .modify(LEDC_CONF::APB_CLK_SEL::XTAL_CLK); + } + + pub fn configure_clk_div_timer( + &self, + timer_number: u32, + //actual max size of 'a' is 10bits and 'b' is 8bits, but declared as u32 to avoid type mismatch + //could use u16 and u8 respectively and use 'as u32' in context; worth it ? + a: u32, + b: u32, + ) -> Result<(), ErrorCode> { + //^is the Result needed ? + if a >= 1024 && b >= 256 { + //implemented as to fit in the bitfield + //SIZE or INVAL error ? + Err(ErrorCode::SIZE) + } else { + match timer_number { + 0 => { + self.registers + .ledc_timer0_conf + .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); + self.registers + .ledc_timer0_conf + .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + Ok(()) + } + 1 => { + self.registers + .ledc_timer1_conf + .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); + self.registers + .ledc_timer1_conf + .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + Ok(()) + } + 2 => { + self.registers + .ledc_timer2_conf + .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); + self.registers + .ledc_timer2_conf + .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + Ok(()) + } + 3 => { + self.registers + .ledc_timer3_conf + .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); + self.registers + .ledc_timer3_conf + .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + Ok(()) + } + _ => Err(ErrorCode::INVAL), + } + } + } +} diff --git a/chips/esp32-c3/src/lib.rs b/chips/esp32-c3/src/lib.rs index d5eae88432..d59e4e3ce8 100644 --- a/chips/esp32-c3/src/lib.rs +++ b/chips/esp32-c3/src/lib.rs @@ -8,6 +8,7 @@ pub mod chip; pub mod intc; pub mod interrupts; +pub mod led_pwm; pub mod sysreg; pub mod timg { diff --git a/chips/esp32-c3/src/sysreg.rs b/chips/esp32-c3/src/sysreg.rs index 33b687cb47..e90d46a009 100644 --- a/chips/esp32-c3/src/sysreg.rs +++ b/chips/esp32-c3/src/sysreg.rs @@ -14,6 +14,8 @@ register_structs! { (0x00c => _reserved1), (0x010 => perip_clk_en0: ReadWrite), (0x014 => _reserved3), + (0x018 => perip_rst_en0: ReadWrite), + (0x01C => _reserved4), (0x058 => sysclk_config: ReadWrite), (0x05C => _reserved_unimplemented_yet), (0x1000 => @END), @@ -22,7 +24,11 @@ register_structs! { register_bitfields![u32, PERIP_CLK_EN0 [ - TIMERGROUP0 OFFSET(13) NUMBITS(1) [] + LEDC OFFSET(11) NUMBITS(1) [], + TIMERGROUP0 OFFSET(13) NUMBITS(1) [], + ], + PERIP_RST_EN0 [ + LEDC OFFSET(11) NUMBITS(1) [] ], CPU_PER_CONF [ CPUPERIOD_SEL OFFSET(0) NUMBITS(2) [ @@ -31,7 +37,7 @@ register_bitfields![u32, ], PLL_FREQ_SEL OFFSET(2) NUMBITS(1) [ MHz320 = 0, - MHz480 = 1 + MHz480 = 1, ], CPU_WAIT_MODE_FORCE_ON OFFSET(3) NUMBITS(1) [], CPU_WAIT_DELAY_NUM OFFSET(4) NUMBITS(4) [], @@ -41,10 +47,10 @@ register_bitfields![u32, SOC_CLK_SEL OFFSET(10) NUMBITS(2) [ Xtal = 0, Pll = 1, - Fosc = 2 + Fosc = 2, ], CLK_XTAL_FREQ OFFSET(12) NUMBITS(6) [], - ] + ], ]; #[repr(u32)] @@ -86,6 +92,31 @@ impl SysReg { ); } + //Enable the APB_CLK signal to LED PWM + pub fn enable_ledc(&self) { + self.registers + .perip_clk_en0 + .modify(PERIP_CLK_EN0::LEDC::SET); + } + + //Disable the APB_CLK signal to LED PWM + pub fn disable_ledc(&self) { + self.registers + .perip_clk_en0 + .modify(PERIP_CLK_EN0::LEDC::CLEAR); + } + + pub fn is_enabled_ledc(&self) -> bool { + self.registers.perip_clk_en0.is_set(PERIP_CLK_EN0::LEDC) + } + + //Reset the APB_CLK signal for LED PWM + pub fn reset_ledc(&self) { + self.registers + .perip_rst_en0 + .modify(PERIP_RST_EN0::LEDC::SET); + } + pub fn enable_timg0(&self) { self.registers .perip_clk_en0 From eeca123707c5f225dbf4d55a0c17154e08e33f5b Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Fri, 4 Nov 2022 14:20:57 +0200 Subject: [PATCH 02/10] finished Timers part + cleaner code --- chips/esp32-c3/src/led_pwm.rs | 286 ++++++++++++++++++---------------- 1 file changed, 151 insertions(+), 135 deletions(-) diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index 1268c222e4..ddc25e031a 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -1,4 +1,6 @@ -use kernel::utilities::registers::interfaces::ReadWriteable; +use core::convert::From; +use core::panic; +use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable}; use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; use kernel::utilities::StaticRef; use kernel::ErrorCode; @@ -7,46 +9,23 @@ pub const LED_PWM_BASE: StaticRef = unsafe { StaticRef::new(0x6001_9000 as *const LedPwmRegisters) }; register_structs! { + pub LedPwmChannel { + (0x0000 => conf0: ReadWrite), + (0x0004 => hpoint: ReadWrite), + (0x0008 => duty: ReadWrite), + (0x000C => conf1: ReadWrite), + (0x0010 => duty_r: ReadWrite), + (0x0014 => @END), + }, + pub LedPwmTimer { + (0x0000 => conf: ReadWrite), + (0x0004 => value: ReadWrite), + (0x0008 => @END), + }, pub LedPwmRegisters { //<- possible naming or "LedcRegisters" - (0x0000 => ledc_ch0_conf0: ReadWrite), - (0x0004 => ledc_ch0_hpoint: ReadWrite), - (0x0008 => ledc_ch0_duty: ReadWrite), - (0x000C => ledc_ch0_conf1: ReadWrite), - (0x0010 => ledc_ch0_duty_r: ReadWrite), - (0x0014 => ledc_ch1_conf0: ReadWrite), - (0x0018 => ledc_ch1_hpoint: ReadWrite), - (0x001C => ledc_ch1_duty: ReadWrite), - (0x0020 => ledc_ch1_conf1: ReadWrite), - (0x0024 => ledc_ch1_duty_r: ReadWrite), - (0x0028 => ledc_ch2_conf0: ReadWrite), - (0x002C => ledc_ch2_hpoint: ReadWrite), - (0x0030 => ledc_ch2_duty: ReadWrite), - (0x0034 => ledc_ch2_conf1: ReadWrite), - (0x0038 => ledc_ch2_duty_r: ReadWrite), - (0x003C => ledc_ch3_conf0: ReadWrite), - (0x0040 => ledc_ch3_hpoint: ReadWrite), - (0x0044 => ledc_ch3_duty: ReadWrite), - (0x0048 => ledc_ch3_conf1: ReadWrite), - (0x004C => ledc_ch3_duty_r: ReadWrite), - (0x0050 => ledc_ch4_conf0: ReadWrite), - (0x0054 => ledc_ch4_hpoint: ReadWrite), - (0x0058 => ledc_ch4_duty: ReadWrite), - (0x005C => ledc_ch4_conf1: ReadWrite), - (0x0060 => ledc_ch4_duty_r: ReadWrite), - (0x0064 => ledc_ch5_conf0: ReadWrite), - (0x0068 => ledc_ch5_hpoint: ReadWrite), - (0x006C => ledc_ch5_duty: ReadWrite), - (0x0070 => ledc_ch5_conf1: ReadWrite), - (0x0074 => ledc_ch5_duty_r: ReadWrite), + (0x0000 => ledc_ch: [LedPwmChannel; 6]), (0x0078 => _reserved0), - (0x00A0 => ledc_timer0_conf: ReadWrite), - (0x00A4 => ledc_timer0_value: ReadWrite), - (0x00A8 => ledc_timer1_conf: ReadWrite), - (0x00AC => ledc_timer1_value: ReadWrite), - (0x00B0 => ledc_timer2_conf: ReadWrite), - (0x00B4 => ledc_timer2_value: ReadWrite), - (0x00B8 => ledc_timer3_conf: ReadWrite), - (0x00BC => ledc_timer3_value: ReadWrite), + (0x00A0 => ledc_timer: [LedPwmTimer; 4]), (0x00C0 => ledc_int_raw: ReadWrite), (0x00C4 => ledc_int_st: ReadWrite), (0x00C8 => ledc_int_ena: ReadWrite), @@ -59,28 +38,31 @@ register_structs! { } register_bitfields! [u32, - //($x) value used for timers, value between [0, 3] - //($n) value used for pwm generators, value between [0, 5] + //x used for timers, value between [0, 3] + //n used for pwm generators, value between [0, 5] //naming suggestions are appreciated LEDC_CH_CONF0 [ - //used to select one of the timers on channel ($n) - //($n): select Timer($n) + //used to select one of the timers on channel n + //0: select Timer0 + //1: select Timer1 + //2: select Timer2 + //3: select Timer3 TIMER_SEL_CH OFFSET(0) NUMBITS(2) [], - //set to enable signal output on channel ($n) + //set to enable signal output on channel n SIG_OUT_EN_CH OFFSET(2) NUMBITS(1) [], - //used to control the output value when channel ($n) is inactive(LEDC_SIG_OUT_EN_CH($n) == 0) + //used to control the output value when channel n is inactive(LEDC_SIG_OUT_EN_CHn == 0) IDLE_LV_CH OFFSET(3) NUMBITS(1) [], - //used to update the listed fields below for channel ($n); is automatically cleared by hardware + //used to update the listed fields below for channel n; is automatically cleared by hardware //HPOINT_CHn, DUTY_START_CHn, SIG_OUT_EN_CHn, TIMER_SEL_CHn, DUTY_NUM_CHn, DUTY_CYCLE_CHn, //DUTY_SCALE_CHn, DUTY_INC_CHn and OVF_CNT_EN_CHn PARA_UP_CH OFFSET(4) NUMBITS(1) [], //used to configure maximum times of overflow - 1 - //LEDC_OVF_CNT_CH($n)_INT will be triggered when channel ($n) overflows for (LEDC_OVF_NUM_CH($n) + 1) times + //LEDC_OVF_CNT_CHn_INT will be triggered when channel n overflows for (LEDC_OVF_NUM_CHn + 1) times OVF_NUM_CH OFFSET(5) NUMBITS(10) [], - //used to count the number of times when the timer selected by channel ($n) overflows + //used to count the number of times when the timer selected by channel n overflows OVF_CNT_EN_CH OFFSET(15) NUMBITS(1) [], - //set to reset timer-overflow counter of channel ($n) + //set to reset timer-overflow counter of channel n OVF_CNT_RESET_CH OFFSET(16) NUMBITS(1) [], ], LEDC_CH_CONF1 [ @@ -92,11 +74,10 @@ register_bitfields! [u32, ], LEDC_CONF [ //used to select the clock source for all 4 timers - APB_CLK_SEL OFFSET(0) NUMBITS(2) [ - APB_CLK = 1, - FOSC_CLK = 2, - XTAL_CLK = 3, - ], + //01: APB_CLK + //10: FOSC_CLK + //11: XTAL_CLK + APB_CLK_SEL OFFSET(0) NUMBITS(2) [], //used to control clock //0: support clock only when application writes registers //1: force clock on register @@ -112,45 +93,45 @@ register_bitfields! [u32, DUTY_R_CH OFFSET(0) NUMBITS(19) [], ], LEDC_TIMER_CONF [ - //used to control range of counter of timer ($x) + //used to control range of counter of timer x TIMER_DUTY_RES OFFSET(0) NUMBITS(4) [], - //used to configure the divisor for the divider of timer ($x) + //used to configure the divisor for the divider of timer x CLK_DIV_TIMER OFFSET(4) NUMBITS(18) [], - //used to suspend the counter of timer ($x) + //used to suspend the counter of timer x TIMER_PAUSE OFFSET(22) NUMBITS(1) [], - //used to reset timer ($x); counter will show 0 after reset + //used to reset timer x; counter will show 0 after reset TIMER_RST OFFSET(23) NUMBITS(1) [], - //set to update LEDC_CLK_DIV_TIMER($x) and LEDC_TIMER($x)_DUTY_RES + //set to update LEDC_CLK_DIV_TIMERx and LEDC_TIMERx_DUTY_RES TIMER_PARA_UP OFFSET(25) NUMBITS(1) [], ], LEDC_TIMER_VALUE [ - //stores the current counter value of timer ($x) + //stores the current counter value of timer x TIMER_CNT OFFSET(0) NUMBITS(14) [], ], LEDC_INT [ //RAW: - //triggered when timer ($x) has reached its maximum counter value + //triggered when timer x has reached its maximum counter value //ST: - //masked interrupt status bit for the LEDC_TIMER($x)_OVF_INT interrupt - //when LEDC_TIMER($x)_OVF_INT_ENA is set to 1 + //masked interrupt status bit for the LEDC_TIMERx_OVF_INT interrupt + //when LEDC_TIMERx_OVF_INT_ENA is set to 1 //ENA: - //set to enable LEDC_TIMER($x)_OVF_INT interrupt + //set to enable LEDC_TIMERx_OVF_INT interrupt //CLR: - //set to clear LEDC_TIMER($x)_OVF_INT interrupt + //set to clear LEDC_TIMERx_OVF_INT interrupt TIMER0_OVF OFFSET(0) NUMBITS(1) [], TIMER1_OVF OFFSET(1) NUMBITS(1) [], TIMER2_OVF OFFSET(2) NUMBITS(1) [], TIMER3_OVF OFFSET(3) NUMBITS(1) [], //RAW: - //interrupt raw bit for channel ($n) + //interrupt raw bit for channel n //triggered when the gradual change of duty has finished //ST: - //masked interrupt status bit for LEDC_DUTY_CHNG_END_CH($n)_INT interrupt - //when LEDC_DUTY_CHNG_CH($n)_INT_ENA is set to 1 + //masked interrupt status bit for LEDC_DUTY_CHNG_END_CHn_INT interrupt + //when LEDC_DUTY_CHNG_CHn_INT_ENA is set to 1 //ENA: - //set to enable LEDC_DUTY_CHNG_END_CH($n)_INT interrupt + //set to enable LEDC_DUTY_CHNG_END_CHn_INT interrupt //CLR: - //set to clear LEDC_DUTY_CHNG_END_CH($n)_INT interrupt + //set to clear LEDC_DUTY_CHNG_END_CHn_INT interrupt DUTY_CHNG_END_CH0 OFFSET(4) NUMBITS(1) [], DUTY_CHNG_END_CH1 OFFSET(5) NUMBITS(1) [], DUTY_CHNG_END_CH2 OFFSET(6) NUMBITS(1) [], @@ -158,15 +139,15 @@ register_bitfields! [u32, DUTY_CHNG_END_CH4 OFFSET(8) NUMBITS(1) [], DUTY_CHNG_END_CH5 OFFSET(9) NUMBITS(1) [], //RAW: - //interrupt raw bit for channel ($n) - //triggered when overflow counter has reached value specified by LEDC_OVF_NUM_CH($n) + //interrupt raw bit for channel n + //triggered when overflow counter has reached value specified by LEDC_OVF_NUM_CHn //ST: - //masked interrupt status bit for LEDC_DUTY_CNT_CH($n)_INT interrupt - //when LEDC_OVF_CNT_CH($n)_INT_ENA is set to 1 + //masked interrupt status bit for LEDC_DUTY_CNT_CHn_INT interrupt + //when LEDC_OVF_CNT_CHn_INT_ENA is set to 1 //ENA: - //set to enable LEDC_OVF_CH($n)_INT interrupt + //set to enable LEDC_OVF_CHn_INT interrupt //CLR: - //set to clear LEDC_OVF_CNT_CH($n)_INT interrupt + //set to clear LEDC_OVF_CNT_CHn_INT interrupt OVF_CNT_CH0 OFFSET(10) NUMBITS(1) [], OVF_CNT_CH1 OFFSET(11) NUMBITS(1) [], OVF_CNT_CH2 OFFSET(12) NUMBITS(1) [], @@ -180,8 +161,32 @@ register_bitfields! [u32, ] ]; +pub enum Timer { + Timer0 = 0, + Timer1 = 1, + Timer2 = 2, + Timer3 = 3, +} + +#[derive(Copy, Clone)] +pub enum Pwm { + Pwm0 = 0, + Pwm1 = 1, + Pwm2 = 2, + Pwm3 = 3, + Pwm4 = 4, + Pwm5 = 5, +} + +pub enum ClockSource { + Apb = 1, + Fosc = 2, + Xtal = 3, +} + struct LedPwm { registers: StaticRef, + //maybe add an array with the current specs for each timer and pwm ? } impl LedPwm { @@ -191,77 +196,88 @@ impl LedPwm { } } - pub fn use_apb_clock_source(&self) { + pub fn configure_clk_source(&self, clock: ClockSource) { self.registers .ledc_conf - .modify(LEDC_CONF::APB_CLK_SEL::APB_CLK); + .modify(LEDC_CONF::APB_CLK_SEL.val(clock as u32)); } - pub fn use_fosc_clk_source(&self) { - self.registers - .ledc_conf - .modify(LEDC_CONF::APB_CLK_SEL::FOSC_CLK); + //are the below Results needed ? panic or assert instead ? + + //LEDC_CLK_DIV_TIMERx = A + B / 256 + //A corresponds to the most significant 10bits of LEDC_CLK_DIV_TIMERx + //B corresponds to the least significant 8bits of LEDC_CLK_DIV_TIMERx + pub fn set_clock_diviser_value(&self, timer: Timer, a: u32, b: u32) -> Result<(), ErrorCode> { + if a >= 1024 && b >= 256 { + Err(ErrorCode::SIZE) + } else { + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + Ok(()) + } } - pub fn use_xtal_clk_source(&self) { - self.registers - .ledc_conf - .modify(LEDC_CONF::APB_CLK_SEL::XTAL_CLK); + //The counter for each timer counts up to 2 ^ (LEDC_TIMERx_DUTY_RES) − 1, + //overflows and begins counting from 0 again + + //the new configuration takes effect upon next overflow; + //implement the wait here or make the user somehow wait for the interrupt to be triggered ? + //see if the Result is needed further + pub fn configure_counter_range(&self, timer: Timer, exponent: u32) -> Result<(), ErrorCode> { + if exponent >= 16 { + Err(ErrorCode::SIZE) + } else { + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::TIMER_DUTY_RES.val(exponent)); + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + Ok(()) + } } - pub fn configure_clk_div_timer( + pub fn configure_counter_overflow( &self, - timer_number: u32, - //actual max size of 'a' is 10bits and 'b' is 8bits, but declared as u32 to avoid type mismatch - //could use u16 and u8 respectively and use 'as u32' in context; worth it ? - a: u32, - b: u32, + pwm: Pwm, + timer: Timer, + counter_overflows: u32, + timer_range_exponent: u32, ) -> Result<(), ErrorCode> { - //^is the Result needed ? - if a >= 1024 && b >= 256 { - //implemented as to fit in the bitfield - //SIZE or INVAL error ? + if counter_overflows >= 1024 { Err(ErrorCode::SIZE) } else { - match timer_number { - 0 => { - self.registers - .ledc_timer0_conf - .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); - self.registers - .ledc_timer0_conf - .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); - Ok(()) - } - 1 => { - self.registers - .ledc_timer1_conf - .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); - self.registers - .ledc_timer1_conf - .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); - Ok(()) - } - 2 => { - self.registers - .ledc_timer2_conf - .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); - self.registers - .ledc_timer2_conf - .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); - Ok(()) - } - 3 => { - self.registers - .ledc_timer3_conf - .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); - self.registers - .ledc_timer3_conf - .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); - Ok(()) - } - _ => Err(ErrorCode::INVAL), - } + //set the given timer as timer for the given pwm + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); + //update the TIMER_SEL_CH field + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + + //enable the overflow counter + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::SET); + + //set the number of counter overflows to generate an interrupt - 1 + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::OVF_NUM_CH.val(counter_overflows)); + + //mask for indexing the counter overflow field for a specific channel + let mask: u32 = 0b0000_0000_0000_0000_0000_0100_0000_0000 << (pwm as usize); + //enable the counter overflow + self.registers + .ledc_int_ena + .set(self.registers.ledc_int_ena.get() | mask); + let _ = self.configure_counter_range(timer, timer_range_exponent); + Ok(()) } } } From 3e893112ba48d0eb78a4852c40d282c063d845e0 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Tue, 15 Nov 2022 21:35:47 +0200 Subject: [PATCH 03/10] finished Pwm part + board declaration --- boards/esp32-c3-devkitM-1/src/main.rs | 1 + chips/esp32-c3/src/chip.rs | 3 + chips/esp32-c3/src/led_pwm.rs | 179 ++++++++++++++++++++++++-- 3 files changed, 172 insertions(+), 11 deletions(-) diff --git a/boards/esp32-c3-devkitM-1/src/main.rs b/boards/esp32-c3-devkitM-1/src/main.rs index b3ff394e9f..f2ae60af0a 100644 --- a/boards/esp32-c3-devkitM-1/src/main.rs +++ b/boards/esp32-c3-devkitM-1/src/main.rs @@ -148,6 +148,7 @@ unsafe fn setup() -> ( peripherals.rtc_cntl.disable_super_wdt(); peripherals.sysreg.disable_timg0(); peripherals.sysreg.enable_timg0(); + peripherals.sysreg.enable_ledc(); peripherals .sysreg diff --git a/chips/esp32-c3/src/chip.rs b/chips/esp32-c3/src/chip.rs index 4a23cb7afc..c63b516d92 100644 --- a/chips/esp32-c3/src/chip.rs +++ b/chips/esp32-c3/src/chip.rs @@ -13,6 +13,7 @@ use rv32i::syscall::SysCall; use crate::intc::{Intc, IntcRegisters}; use crate::interrupts; +use crate::led_pwm; use crate::sysreg; use crate::timg; @@ -35,6 +36,7 @@ pub struct Esp32C3DefaultPeripherals<'a> { pub gpio: esp32::gpio::Port<'a>, pub rtc_cntl: esp32::rtc_cntl::RtcCntl, pub sysreg: sysreg::SysReg, + pub led_pwm: led_pwm::LedPwm, } impl<'a> Esp32C3DefaultPeripherals<'a> { @@ -46,6 +48,7 @@ impl<'a> Esp32C3DefaultPeripherals<'a> { gpio: esp32::gpio::Port::new(), rtc_cntl: esp32::rtc_cntl::RtcCntl::new(esp32::rtc_cntl::RTC_CNTL_BASE), sysreg: sysreg::SysReg::new(), + led_pwm: led_pwm::LedPwm::new(), } } } diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index ddc25e031a..52b7e39798 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -1,5 +1,3 @@ -use core::convert::From; -use core::panic; use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable}; use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; use kernel::utilities::StaticRef; @@ -51,25 +49,32 @@ register_bitfields! [u32, TIMER_SEL_CH OFFSET(0) NUMBITS(2) [], //set to enable signal output on channel n SIG_OUT_EN_CH OFFSET(2) NUMBITS(1) [], - //used to control the output value when channel n is inactive(LEDC_SIG_OUT_EN_CHn == 0) + //controls the output value when channel n is inactive(LEDC_SIG_OUT_EN_CHn == 0) IDLE_LV_CH OFFSET(3) NUMBITS(1) [], //used to update the listed fields below for channel n; is automatically cleared by hardware //HPOINT_CHn, DUTY_START_CHn, SIG_OUT_EN_CHn, TIMER_SEL_CHn, DUTY_NUM_CHn, DUTY_CYCLE_CHn, //DUTY_SCALE_CHn, DUTY_INC_CHn and OVF_CNT_EN_CHn PARA_UP_CH OFFSET(4) NUMBITS(1) [], - //used to configure maximum times of overflow - 1 + //configures maximum times of overflow - 1 //LEDC_OVF_CNT_CHn_INT will be triggered when channel n overflows for (LEDC_OVF_NUM_CHn + 1) times OVF_NUM_CH OFFSET(5) NUMBITS(10) [], - //used to count the number of times when the timer selected by channel n overflows + //counts the number of times when the timer selected by channel n overflows OVF_CNT_EN_CH OFFSET(15) NUMBITS(1) [], //set to reset timer-overflow counter of channel n OVF_CNT_RESET_CH OFFSET(16) NUMBITS(1) [], ], LEDC_CH_CONF1 [ + //configures the step size of the duty cucle change during fading DUTY_SCALE_CH OFFSET(0) NUMBITS(10) [], + //sets the number of counter overflow cycles for every Lpointn increment/decrement DUTY_CYCLE_CH OFFSET(10) NUMBITS(10) [], + //controls the number of times the duty cycle will be incremented/decremented DUTY_NUM_CH OFFSET(20) NUMBITS(10) [], + //determines the monotony of the duty cycle of the output signal on channel n + //0: Decreases + //1: Increases DUTY_INC_CH OFFSET(30) NUMBITS(1) [], + //set to enable duty cycle fading on channel n DUTY_START_CH OFFSET(31) NUMBITS(1) [], ], LEDC_CONF [ @@ -84,9 +89,13 @@ register_bitfields! [u32, CLK_EN OFFSET(31) NUMBITS(1) [], ], LEDC_CH_HPOINT [ + //signal output value changes to high when the selected timer + //has reached the value in this field HPOINT_CH OFFSET(0) NUMBITS(14) [], ], LEDC_CH_DUTY [ + //signal output value changes to low when the selected timer + //has reached the value specified in this field DUTY_CH OFFSET(0) NUMBITS(19) [], ], LEDC_CH_DUTY_R [ @@ -161,6 +170,7 @@ register_bitfields! [u32, ] ]; +#[derive(Copy, Clone)] pub enum Timer { Timer0 = 0, Timer1 = 1, @@ -184,7 +194,7 @@ pub enum ClockSource { Xtal = 3, } -struct LedPwm { +pub struct LedPwm { registers: StaticRef, //maybe add an array with the current specs for each timer and pwm ? } @@ -255,10 +265,6 @@ impl LedPwm { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); - //update the TIMER_SEL_CH field - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); //enable the overflow counter self.registers.ledc_ch[pwm as usize] @@ -276,7 +282,158 @@ impl LedPwm { self.registers .ledc_int_ena .set(self.registers.ledc_int_ena.get() | mask); - let _ = self.configure_counter_range(timer, timer_range_exponent); + + if let Err(e) = self.configure_counter_range(timer, timer_range_exponent) { + return Err(e); + }; + + //update the new configuration + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + Ok(()) + } + } + + pub fn set_pwm_timer(&self, pwm: Pwm, timer: Timer) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } + + //if Timerx_cnt == high_point => sig_outn = 1 + //if Timerx_cnt == lowpoint => sig_outn = 0 + pub fn configure_fixed_duty_cycle( + &self, + pwm: Pwm, + high_point: u32, + low_point: u32, + ) -> Result<(), ErrorCode> { + if high_point >= 16384 || low_point >= 32768 { + Err(ErrorCode::SIZE) + } else if high_point > low_point { + Err(ErrorCode::INVAL) + } else { + self.registers.ledc_ch[pwm as usize] + .hpoint + .modify(LEDC_CH_HPOINT::HPOINT_CH.val(high_point)); + self.registers.ledc_ch[pwm as usize] + .duty + .modify(LEDC_CH_DUTY::DUTY_CH.val(low_point << 4)); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + Ok(()) + } + } + + pub fn enable_signal_output(&self, pwm: Pwm) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::SIG_OUT_EN_CH::SET); + + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } + + pub fn disable_signal_output(&self, pwm: Pwm, idle_level: bool) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::SIG_OUT_EN_CH::CLEAR); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(if idle_level { + LEDC_CH_CONF0::IDLE_LV_CH::SET + } else { + LEDC_CH_CONF0::IDLE_LV_CH::CLEAR + }); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } + + pub fn set_special_cycles(&self, pwm: Pwm, cycles_number: u32) -> Result<(), ErrorCode> { + if cycles_number >= 16 { + Err(ErrorCode::SIZE) + } else { + self.registers.ledc_ch[pwm as usize] + .duty + .modify(LEDC_CH_DUTY::DUTY_CH.val(cycles_number)); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + Ok(()) + } + } + + pub fn enable_duty_cycle_fading(&self, pwm: Pwm) { + self.registers.ledc_ch[pwm as usize] + .conf1 + .modify(LEDC_CH_CONF1::DUTY_START_CH::SET); + + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } + + pub fn disable_duty_cycle_fading(&self, pwm: Pwm) { + self.registers.ledc_ch[pwm as usize] + .conf1 + .modify(LEDC_CH_CONF1::DUTY_START_CH::CLEAR); + + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } + + pub fn configure_fading_duty_cycle( + &self, + pwm: Pwm, + high_point: u32, + low_point: u32, + counter_overflow_cycles: u32, + step: u32, + steps_number: u32, + monotony: i8, + ) -> Result<(), ErrorCode> { + if counter_overflow_cycles >= 1024 || step >= 1024 || steps_number >= 1024 { + Err(ErrorCode::SIZE) + } else { + //set the high and low point of the soon-to-be fading duty cycle + if let Err(e) = self.configure_fixed_duty_cycle(pwm, high_point, low_point) { + return Err(e); + }; + + //Lpointn will be incremented/decremented after DUTY_CYCLE_CHn counter overflows + self.registers.ledc_ch[pwm as usize] + .conf1 + .modify(LEDC_CH_CONF1::DUTY_CYCLE_CH.val(counter_overflow_cycles)); + + self.registers.ledc_ch[pwm as usize] + .conf1 + .modify(if monotony > 0 { + LEDC_CH_CONF1::DUTY_INC_CH::SET + } else { + LEDC_CH_CONF1::DUTY_INC_CH::CLEAR + }); + + self.enable_duty_cycle_fading(pwm); + + self.registers.ledc_ch[pwm as usize] + .conf1 + .modify(LEDC_CH_CONF1::DUTY_SCALE_CH.val(step)); + + self.registers.ledc_ch[pwm as usize] + .conf1 + .modify(LEDC_CH_CONF1::DUTY_NUM_CH.val(steps_number)); + + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); Ok(()) } } From ec75dcbb23617ddfb3d1a6b64d01cd4d50f0ac27 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Fri, 18 Nov 2022 23:23:48 +0200 Subject: [PATCH 04/10] much cleaner implementation --- chips/esp32-c3/src/chip.rs | 5 + chips/esp32-c3/src/intc.rs | 7 +- chips/esp32-c3/src/led_pwm.rs | 303 +++++++++++++++++++++------------- 3 files changed, 200 insertions(+), 115 deletions(-) diff --git a/chips/esp32-c3/src/chip.rs b/chips/esp32-c3/src/chip.rs index c63b516d92..438507e9b9 100644 --- a/chips/esp32-c3/src/chip.rs +++ b/chips/esp32-c3/src/chip.rs @@ -68,6 +68,11 @@ impl<'a> InterruptService<()> for Esp32C3DefaultPeripherals<'a> { interrupts::IRQ_GPIO | interrupts::IRQ_GPIO_NMI => { self.gpio.handle_interrupt(); } + interrupts::IRQ_LEDC => { + //handler is unimplemented yet + //not sure exactly what to do :) + self.led_pwm.handle_interrupt(); + } _ => return false, } true diff --git a/chips/esp32-c3/src/intc.rs b/chips/esp32-c3/src/intc.rs index adb8425b7e..1d92439d28 100644 --- a/chips/esp32-c3/src/intc.rs +++ b/chips/esp32-c3/src/intc.rs @@ -16,16 +16,18 @@ register_structs! { (0x048 => _reserved1), (0x054 => uart0_intr_map: ReadWrite), (0x058 => _reserved2), + (0x05C => ledc_intr_map: ReadWrite), + (0x060 => _reserved3), (0x080 => timg0_intr_map: ReadWrite), (0x084 => timg1_intr_map: ReadWrite), - (0x088 => _reserved3), + (0x088 => _reserved4), (0x0f8 => status: [ReadWrite; 2]), (0x100 => clk_en: ReadWrite), (0x104 => enable: ReadWrite), (0x108 => type_reg: ReadWrite), (0x10C => clear: ReadWrite), (0x110 => eip: ReadWrite), - (0x114 => _reserved4), + (0x114 => _reserved5), (0x118 => priority: [ReadWrite; 31]), (0x194 => thresh: ReadWrite), (0x198 => @END), @@ -71,6 +73,7 @@ impl Intc { /// In Tock we map them ourselves so we don't need to call into the ROM. pub fn map_interrupts(&self) { self.registers.uart0_intr_map.set(interrupts::IRQ_UART0); + self.registers.ledc_intr_map.set(interrupts::IRQ_LEDC); self.registers.timg0_intr_map.set(interrupts::IRQ_TIMER1); self.registers.timg1_intr_map.set(interrupts::IRQ_TIMER2); self.registers diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index 52b7e39798..da7a212d81 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -50,7 +50,10 @@ register_bitfields! [u32, //set to enable signal output on channel n SIG_OUT_EN_CH OFFSET(2) NUMBITS(1) [], //controls the output value when channel n is inactive(LEDC_SIG_OUT_EN_CHn == 0) - IDLE_LV_CH OFFSET(3) NUMBITS(1) [], + IDLE_LV_CH OFFSET(3) NUMBITS(1) [ + Low = 0, + High = 1, + ], //used to update the listed fields below for channel n; is automatically cleared by hardware //HPOINT_CHn, DUTY_START_CHn, SIG_OUT_EN_CHn, TIMER_SEL_CHn, DUTY_NUM_CHn, DUTY_CYCLE_CHn, //DUTY_SCALE_CHn, DUTY_INC_CHn and OVF_CNT_EN_CHn @@ -194,6 +197,11 @@ pub enum ClockSource { Xtal = 3, } +pub enum DutyCycle { + Fixed(u32, u32), + Fading(u32, u32, u32, i32, u32, u32), +} + pub struct LedPwm { registers: StaticRef, //maybe add an array with the current specs for each timer and pwm ? @@ -206,113 +214,153 @@ impl LedPwm { } } - pub fn configure_clk_source(&self, clock: ClockSource) { + //functions that configure the timers + + pub fn set_clk_source(&self, clock: ClockSource) { self.registers .ledc_conf .modify(LEDC_CONF::APB_CLK_SEL.val(clock as u32)); } - //are the below Results needed ? panic or assert instead ? - - //LEDC_CLK_DIV_TIMERx = A + B / 256 - //A corresponds to the most significant 10bits of LEDC_CLK_DIV_TIMERx - //B corresponds to the least significant 8bits of LEDC_CLK_DIV_TIMERx - pub fn set_clock_diviser_value(&self, timer: Timer, a: u32, b: u32) -> Result<(), ErrorCode> { - if a >= 1024 && b >= 256 { + pub fn configure_timer_divider(&self, timer: Timer, a: u32, b: u32) -> Result<(), ErrorCode> { + if a >= 1024 || b >= 256 { Err(ErrorCode::SIZE) } else { self.registers.ledc_timer[timer as usize] .conf - .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 2) + b)); + .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 12) + (b << 4))); self.registers.ledc_timer[timer as usize] .conf .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + todo!("^This will cause the newly configured values to take effect upon the next overflow of the counter"); Ok(()) } } - //The counter for each timer counts up to 2 ^ (LEDC_TIMERx_DUTY_RES) − 1, - //overflows and begins counting from 0 again + pub fn suspend_timer_counter(&self, timer: Timer) { + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::TIMER_PAUSE::SET); + } + + pub fn resume_timer_counter(&self, timer: Timer) { + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::TIMER_PAUSE::CLEAR); + } - //the new configuration takes effect upon next overflow; - //implement the wait here or make the user somehow wait for the interrupt to be triggered ? - //see if the Result is needed further - pub fn configure_counter_range(&self, timer: Timer, exponent: u32) -> Result<(), ErrorCode> { - if exponent >= 16 { + pub fn reset_timer_counter(&self, timer: Timer) { + self.registers.ledc_timer[timer as usize] + .conf + .modify(LEDC_TIMER_CONF::TIMER_RST::SET); + todo!("is it cleared by hardware or should i do it manually ?"); + } + + pub fn configure_timer_counter( + &self, + timer: Timer, + overflow_value: u32, + ) -> Result<(), ErrorCode> { + if overflow_value >= 16 { Err(ErrorCode::SIZE) } else { self.registers.ledc_timer[timer as usize] .conf - .modify(LEDC_TIMER_CONF::TIMER_DUTY_RES.val(exponent)); + .modify(LEDC_TIMER_CONF::TIMER_DUTY_RES.val(overflow_value)); self.registers.ledc_timer[timer as usize] .conf .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); + todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); Ok(()) } } - pub fn configure_counter_overflow( + pub fn configure_timer( &self, - pwm: Pwm, timer: Timer, - counter_overflows: u32, - timer_range_exponent: u32, + a: u32, + b: u32, + overflow_value: u32, ) -> Result<(), ErrorCode> { - if counter_overflows >= 1024 { - Err(ErrorCode::SIZE) - } else { - //set the given timer as timer for the given pwm - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); + if let Err(e) = self.configure_timer_divider(timer, a, b) { + return Err(e); + } + if let Err(e) = self.configure_timer_counter(timer, overflow_value) { + return Err(e); + } + Ok(()) + } - //enable the overflow counter - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::SET); + //functions that configure the pwm generator - //set the number of counter overflows to generate an interrupt - 1 - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::OVF_NUM_CH.val(counter_overflows)); + pub fn set_pwm_timer(&self, pwm: Pwm, timer: Timer) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); + } - //mask for indexing the counter overflow field for a specific channel - let mask: u32 = 0b0000_0000_0000_0000_0000_0100_0000_0000 << (pwm as usize); - //enable the counter overflow - self.registers - .ledc_int_ena - .set(self.registers.ledc_int_ena.get() | mask); + pub fn enable_overflow_counting(&self, pwm: Pwm) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::SET); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } - if let Err(e) = self.configure_counter_range(timer, timer_range_exponent) { - return Err(e); - }; + pub fn disable_overflows_counting(&self, pwm: Pwm) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::CLEAR); + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + } + + pub fn reset_timer_overflow_counter(&self, pwm: Pwm) { + self.registers.ledc_ch[pwm as usize] + .conf0 + .modify(LEDC_CH_CONF0::OVF_CNT_RESET_CH::SET); + todo!("is it cleared by hardware or should i do it manually ?"); + } - //update the new configuration + pub fn set_overflows_to_generate_interrupt(&self, pwm: Pwm, nr: u32) -> Result<(), ErrorCode> { + if nr >= 1024 { + Err(ErrorCode::SIZE) + } else { self.registers.ledc_ch[pwm as usize] .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + .modify(LEDC_CH_CONF0::OVF_NUM_CH.val(nr)); Ok(()) } } - pub fn set_pwm_timer(&self, pwm: Pwm, timer: Timer) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + pub fn enable_overflows_interrupt(&self, pwm: Pwm) { + let mask: u32 = 0b0000_0000_0000_0000_0000_0100_0000_0000 << (pwm as u32); + self.registers + .ledc_int_ena + .set(self.registers.ledc_int_ena.get() | mask); + } + + pub fn disable_overflows_interrupt(&self, pwm: Pwm) { + let p = pwm as u32; + let mask: u32 = (!0b0000_0000_0000_0000_0000_0100_0000_0000 << p) + (u32::pow(2, p) - 1); + self.registers + .ledc_int_ena + .set(self.registers.ledc_int_ena.get() & mask); } - //if Timerx_cnt == high_point => sig_outn = 1 - //if Timerx_cnt == lowpoint => sig_outn = 0 - pub fn configure_fixed_duty_cycle( + pub fn configure_pwm_fixed_duty_cycle( &self, pwm: Pwm, high_point: u32, low_point: u32, ) -> Result<(), ErrorCode> { - if high_point >= 16384 || low_point >= 32768 { + if high_point >= 16384 || low_point >= 49151 { Err(ErrorCode::SIZE) } else if high_point > low_point { Err(ErrorCode::INVAL) @@ -322,119 +370,148 @@ impl LedPwm { .modify(LEDC_CH_HPOINT::HPOINT_CH.val(high_point)); self.registers.ledc_ch[pwm as usize] .duty - .modify(LEDC_CH_DUTY::DUTY_CH.val(low_point << 4)); + .modify(LEDC_CH_DUTY::DUTY_CH.val((low_point - high_point) << 4)); self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); Ok(()) } } - pub fn enable_signal_output(&self, pwm: Pwm) { + pub fn enable_pwm_signal_output(&self, pwm: Pwm) { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::SIG_OUT_EN_CH::SET); - self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); } - pub fn disable_signal_output(&self, pwm: Pwm, idle_level: bool) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::SIG_OUT_EN_CH::CLEAR); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(if idle_level { - LEDC_CH_CONF0::IDLE_LV_CH::SET - } else { - LEDC_CH_CONF0::IDLE_LV_CH::CLEAR - }); + pub fn disable_pwm_signal_output(&self, pwm: Pwm, lvl: bool) { + self.registers.ledc_ch[pwm as usize].conf0.modify( + LEDC_CH_CONF0::SIG_OUT_EN_CH::CLEAR + + if lvl { + LEDC_CH_CONF0::IDLE_LV_CH::Value::High.into() + } else { + LEDC_CH_CONF0::IDLE_LV_CH::Value::Low.into() + }, + ); self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn set_special_cycles(&self, pwm: Pwm, cycles_number: u32) -> Result<(), ErrorCode> { - if cycles_number >= 16 { + pub fn dither_pwm_duty_cycles(&self, pwm: Pwm, nr: u32) -> Result<(), ErrorCode> { + if nr >= 16 { Err(ErrorCode::SIZE) } else { self.registers.ledc_ch[pwm as usize] .duty - .modify(LEDC_CH_DUTY::DUTY_CH.val(cycles_number)); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + .modify(LEDC_CH_DUTY::DUTY_CH.val(nr)); Ok(()) } } - pub fn enable_duty_cycle_fading(&self, pwm: Pwm) { + pub fn enable_fading_duty(&self, pwm: Pwm) { self.registers.ledc_ch[pwm as usize] .conf1 .modify(LEDC_CH_CONF1::DUTY_START_CH::SET); - self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn disable_duty_cycle_fading(&self, pwm: Pwm) { + pub fn disable_fading_duty(&self, pwm: Pwm) { self.registers.ledc_ch[pwm as usize] .conf1 .modify(LEDC_CH_CONF1::DUTY_START_CH::CLEAR); - self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn configure_fading_duty_cycle( + pub fn configure_pwm_fading_duty_cycle( &self, pwm: Pwm, high_point: u32, low_point: u32, - counter_overflow_cycles: u32, - step: u32, + counter_overflows: u32, + duty_monotony: i32, + duty_step: u32, steps_number: u32, - monotony: i8, ) -> Result<(), ErrorCode> { - if counter_overflow_cycles >= 1024 || step >= 1024 || steps_number >= 1024 { + if counter_overflows >= 1024 || duty_step >= 1024 || steps_number >= 1024 { Err(ErrorCode::SIZE) } else { - //set the high and low point of the soon-to-be fading duty cycle - if let Err(e) = self.configure_fixed_duty_cycle(pwm, high_point, low_point) { + if let Err(e) = self.configure_pwm_fixed_duty_cycle(pwm, high_point, low_point) { return Err(e); - }; - - //Lpointn will be incremented/decremented after DUTY_CYCLE_CHn counter overflows + } + self.registers.ledc_ch[pwm as usize].conf1.modify( + LEDC_CH_CONF1::DUTY_CYCLE_CH.val(counter_overflows) + + if duty_monotony < 0 { + LEDC_CH_CONF1::DUTY_INC_CH::CLEAR + } else { + LEDC_CH_CONF1::DUTY_INC_CH::SET + } + + LEDC_CH_CONF1::DUTY_SCALE_CH.val(duty_step) + + LEDC_CH_CONF1::DUTY_NUM_CH.val(steps_number), + ); self.registers.ledc_ch[pwm as usize] - .conf1 - .modify(LEDC_CH_CONF1::DUTY_CYCLE_CH.val(counter_overflow_cycles)); + .conf0 + .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); + Ok(()) + } + } - self.registers.ledc_ch[pwm as usize] - .conf1 - .modify(if monotony > 0 { - LEDC_CH_CONF1::DUTY_INC_CH::SET + pub fn configure_pwm(&self, pwm: Pwm, duty_cycle: DutyCycle) -> Result<(), ErrorCode> { + match duty_cycle { + DutyCycle::Fixed(a, b) => { + if let Err(e) = self.configure_pwm_fixed_duty_cycle(pwm, a, b) { + Err(e) } else { - LEDC_CH_CONF1::DUTY_INC_CH::CLEAR - }); + Ok(()) + } + } + DutyCycle::Fading(a, b, c, d, e, f) => { + if let Err(e) = self.configure_pwm_fading_duty_cycle(pwm, a, b, c, d, e, f) { + Err(e) + } else { + Ok(()) + } + } + } + } - self.enable_duty_cycle_fading(pwm); + //functions regarding interrupts - self.registers.ledc_ch[pwm as usize] - .conf1 - .modify(LEDC_CH_CONF1::DUTY_SCALE_CH.val(step)); + pub fn handle_interrupt(&self) { + //how do i signal which interrupt to handle + todo!() + } - self.registers.ledc_ch[pwm as usize] - .conf1 - .modify(LEDC_CH_CONF1::DUTY_NUM_CH.val(steps_number)); + pub fn counter_overflow_interrupt_is_triggered(&self, timer: Timer) -> bool { + let mask: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0001 << (timer as usize); + self.registers.ledc_int_raw.get() & mask != 0 + } - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - Ok(()) - } + pub fn counter_overflow_interrupt_st_reg(&self, timer: Timer) { + todo!("not sure what a masked interrupt is and what to do with it") + } + + pub fn enable_counter_overflow_interrupt(&self, timer: Timer) { + let mask: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0001 << (timer as u32); + self.registers + .ledc_int_ena + .set(self.registers.ledc_int_ena.get() | mask); + } + + pub fn clear_counter_overflow_interrupt(&self, timer: Timer) { + let t = timer as u32; + let mask: u32 = (!0b0000_0000_0000_0000_0000_0000_0000_0001 << t) + (u32::pow(2, t) - 1); + self.registers + .ledc_int_clr + .set(self.registers.ledc_int_clr.get() & mask); } } From e2f6e4d50a21ff6f59a1264fd011d7f7b5c43093 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Tue, 20 Dec 2022 18:45:32 +0200 Subject: [PATCH 05/10] DutyCycle enum clarifications --- chips/esp32-c3/src/led_pwm.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index da7a212d81..487a70b390 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -197,9 +197,20 @@ pub enum ClockSource { Xtal = 3, } +//Duty cycles can be of two types pub enum DutyCycle { Fixed(u32, u32), + //with a payload used to store: + // - highpoint value + // - lowpoint value Fading(u32, u32, u32, i32, u32, u32), + //with a payload used to store: + // - highpoint value + // - lowpoint value + // - the number of counter overflows after which the lowpoint value will be incremented/decremented + // - (+/-)1 value that determines whether the duty cycle output signal increases or decreases + // - step size of the duty cycle change + // - the number of times the duty cycle will be changed } pub struct LedPwm { @@ -291,7 +302,7 @@ impl LedPwm { Ok(()) } - //functions that configure the pwm generator + // functions that configure the pwm generator pub fn set_pwm_timer(&self, pwm: Pwm, timer: Timer) { self.registers.ledc_ch[pwm as usize] From 8b1021ec9174aacb09cc5c9c9708504063179a1f Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Mon, 9 Jan 2023 21:23:55 +0200 Subject: [PATCH 06/10] max_frq_hz trait method implemented --- chips/esp32-c3/src/led_pwm.rs | 76 ++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index 487a70b390..05faa547c3 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -1,3 +1,4 @@ +use kernel::hil; use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable}; use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; use kernel::utilities::StaticRef; @@ -182,7 +183,7 @@ pub enum Timer { } #[derive(Copy, Clone)] -pub enum Pwm { +pub enum PwmChannel { Pwm0 = 0, Pwm1 = 1, Pwm2 = 2, @@ -193,7 +194,7 @@ pub enum Pwm { pub enum ClockSource { Apb = 1, - Fosc = 2, + RcFast = 2, Xtal = 3, } @@ -225,7 +226,7 @@ impl LedPwm { } } - //functions that configure the timers + // functions that configure the timers pub fn set_clk_source(&self, clock: ClockSource) { self.registers @@ -304,7 +305,7 @@ impl LedPwm { // functions that configure the pwm generator - pub fn set_pwm_timer(&self, pwm: Pwm, timer: Timer) { + pub fn set_pwm_timer(&self, pwm: PwmChannel, timer: Timer) { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); @@ -314,7 +315,7 @@ impl LedPwm { todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); } - pub fn enable_overflow_counting(&self, pwm: Pwm) { + pub fn enable_overflow_counting(&self, pwm: PwmChannel) { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::SET); @@ -323,7 +324,7 @@ impl LedPwm { .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn disable_overflows_counting(&self, pwm: Pwm) { + pub fn disable_overflows_counting(&self, pwm: PwmChannel) { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::CLEAR); @@ -332,14 +333,18 @@ impl LedPwm { .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn reset_timer_overflow_counter(&self, pwm: Pwm) { + pub fn reset_timer_overflow_counter(&self, pwm: PwmChannel) { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::OVF_CNT_RESET_CH::SET); todo!("is it cleared by hardware or should i do it manually ?"); } - pub fn set_overflows_to_generate_interrupt(&self, pwm: Pwm, nr: u32) -> Result<(), ErrorCode> { + pub fn set_overflows_to_generate_interrupt( + &self, + pwm: PwmChannel, + nr: u32, + ) -> Result<(), ErrorCode> { if nr >= 1024 { Err(ErrorCode::SIZE) } else { @@ -350,14 +355,14 @@ impl LedPwm { } } - pub fn enable_overflows_interrupt(&self, pwm: Pwm) { + pub fn enable_overflows_interrupt(&self, pwm: PwmChannel) { let mask: u32 = 0b0000_0000_0000_0000_0000_0100_0000_0000 << (pwm as u32); self.registers .ledc_int_ena .set(self.registers.ledc_int_ena.get() | mask); } - pub fn disable_overflows_interrupt(&self, pwm: Pwm) { + pub fn disable_overflows_interrupt(&self, pwm: PwmChannel) { let p = pwm as u32; let mask: u32 = (!0b0000_0000_0000_0000_0000_0100_0000_0000 << p) + (u32::pow(2, p) - 1); self.registers @@ -367,7 +372,7 @@ impl LedPwm { pub fn configure_pwm_fixed_duty_cycle( &self, - pwm: Pwm, + pwm: PwmChannel, high_point: u32, low_point: u32, ) -> Result<(), ErrorCode> { @@ -390,7 +395,7 @@ impl LedPwm { } } - pub fn enable_pwm_signal_output(&self, pwm: Pwm) { + pub fn enable_pwm_signal_output(&self, pwm: PwmChannel) { self.registers.ledc_ch[pwm as usize] .conf0 .modify(LEDC_CH_CONF0::SIG_OUT_EN_CH::SET); @@ -400,7 +405,7 @@ impl LedPwm { todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); } - pub fn disable_pwm_signal_output(&self, pwm: Pwm, lvl: bool) { + pub fn disable_pwm_signal_output(&self, pwm: PwmChannel, lvl: bool) { self.registers.ledc_ch[pwm as usize].conf0.modify( LEDC_CH_CONF0::SIG_OUT_EN_CH::CLEAR + if lvl { @@ -414,7 +419,7 @@ impl LedPwm { .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn dither_pwm_duty_cycles(&self, pwm: Pwm, nr: u32) -> Result<(), ErrorCode> { + pub fn dither_pwm_duty_cycles(&self, pwm: PwmChannel, nr: u32) -> Result<(), ErrorCode> { if nr >= 16 { Err(ErrorCode::SIZE) } else { @@ -425,7 +430,7 @@ impl LedPwm { } } - pub fn enable_fading_duty(&self, pwm: Pwm) { + pub fn enable_fading_duty(&self, pwm: PwmChannel) { self.registers.ledc_ch[pwm as usize] .conf1 .modify(LEDC_CH_CONF1::DUTY_START_CH::SET); @@ -434,7 +439,7 @@ impl LedPwm { .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); } - pub fn disable_fading_duty(&self, pwm: Pwm) { + pub fn disable_fading_duty(&self, pwm: PwmChannel) { self.registers.ledc_ch[pwm as usize] .conf1 .modify(LEDC_CH_CONF1::DUTY_START_CH::CLEAR); @@ -445,7 +450,7 @@ impl LedPwm { pub fn configure_pwm_fading_duty_cycle( &self, - pwm: Pwm, + pwm: PwmChannel, high_point: u32, low_point: u32, counter_overflows: u32, @@ -476,7 +481,7 @@ impl LedPwm { } } - pub fn configure_pwm(&self, pwm: Pwm, duty_cycle: DutyCycle) -> Result<(), ErrorCode> { + pub fn configure_pwm(&self, pwm: PwmChannel, duty_cycle: DutyCycle) -> Result<(), ErrorCode> { match duty_cycle { DutyCycle::Fixed(a, b) => { if let Err(e) = self.configure_pwm_fixed_duty_cycle(pwm, a, b) { @@ -495,7 +500,7 @@ impl LedPwm { } } - //functions regarding interrupts + // functions regarding interrupts pub fn handle_interrupt(&self) { //how do i signal which interrupt to handle @@ -520,9 +525,40 @@ impl LedPwm { pub fn clear_counter_overflow_interrupt(&self, timer: Timer) { let t = timer as u32; - let mask: u32 = (!0b0000_0000_0000_0000_0000_0000_0000_0001 << t) + (u32::pow(2, t) - 1); + let mask: u32 = (0b1111_1111_1111_1111_1111_1111_1111_1110 << t) + (u32::pow(2, t) - 1); self.registers .ledc_int_clr .set(self.registers.ledc_int_clr.get() & mask); } } + +impl hil::pwm::Pwm for LedPwm { + type Pin = u32; + + fn start( + &self, + pin: &Self::Pin, + frequency_hz: usize, + duty_cycle: usize, + ) -> Result<(), ErrorCode> { + todo!() + } + + fn stop(&self, pin: &Self::Pin) -> Result<(), ErrorCode> { + todo!() + } + + fn get_maximum_frequency_hz(&self) -> usize { + //frequency of a PWM generator output signal si given by: + // + // LEDC_CLKx / (LEDC_CLK_DIV_TIMERx * 2 ^ LEDC_TIMERx_DUTY_RES) + // + // highest LEDC_CLK freq is 80MHz, given by selecting APB_CLK as timer clock source + // when using PLL_CLK as clock source for the CPU + 80000000 + } + + fn get_maximum_duty_cycle(&self) -> usize { + todo!() + } +} From 1e8cb38a1947d69cee5685064024115034b2f168 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Fri, 13 Jan 2023 22:58:26 +0200 Subject: [PATCH 07/10] max_duty_cycle method implemented --- chips/esp32-c3/src/led_pwm.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index 05faa547c3..8955b3931f 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -549,7 +549,7 @@ impl hil::pwm::Pwm for LedPwm { } fn get_maximum_frequency_hz(&self) -> usize { - //frequency of a PWM generator output signal si given by: + // frequency of a PWM generator output signal is given by: // // LEDC_CLKx / (LEDC_CLK_DIV_TIMERx * 2 ^ LEDC_TIMERx_DUTY_RES) // @@ -559,6 +559,8 @@ impl hil::pwm::Pwm for LedPwm { } fn get_maximum_duty_cycle(&self) -> usize { - todo!() + // the PWM can be set to high for the entire period of the signal, + // resulting in a 100% duty cycle + 80000000 } } From 719cd382053e6479804284563dc53dfe34bcef39 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Tue, 17 Jan 2023 19:08:29 +0200 Subject: [PATCH 08/10] rising anew --- chips/esp32-c3/src/led_pwm.rs | 504 ++++++---------------------------- 1 file changed, 82 insertions(+), 422 deletions(-) diff --git a/chips/esp32-c3/src/led_pwm.rs b/chips/esp32-c3/src/led_pwm.rs index 8955b3931f..6baa98f155 100644 --- a/chips/esp32-c3/src/led_pwm.rs +++ b/chips/esp32-c3/src/led_pwm.rs @@ -4,7 +4,7 @@ use kernel::utilities::registers::{register_bitfields, register_structs, ReadWri use kernel::utilities::StaticRef; use kernel::ErrorCode; -pub const LED_PWM_BASE: StaticRef = +pub const LEDC_BASE: StaticRef = unsafe { StaticRef::new(0x6001_9000 as *const LedPwmRegisters) }; register_structs! { @@ -21,7 +21,7 @@ register_structs! { (0x0004 => value: ReadWrite), (0x0008 => @END), }, - pub LedPwmRegisters { //<- possible naming or "LedcRegisters" + pub LedPwmRegisters { (0x0000 => ledc_ch: [LedPwmChannel; 6]), (0x0078 => _reserved0), (0x00A0 => ledc_timer: [LedPwmTimer; 4]), @@ -37,130 +37,134 @@ register_structs! { } register_bitfields! [u32, - //x used for timers, value between [0, 3] - //n used for pwm generators, value between [0, 5] + // x used for timers, value between [0, 3] + // n used for pwm generators, value between [0, 5] - //naming suggestions are appreciated + // naming suggestions are appreciated LEDC_CH_CONF0 [ - //used to select one of the timers on channel n - //0: select Timer0 - //1: select Timer1 - //2: select Timer2 - //3: select Timer3 + // used to select one of the timers on channel n + // 0: select Timer0 + // 1: select Timer1 + // 2: select Timer2 + // 3: select Timer3 TIMER_SEL_CH OFFSET(0) NUMBITS(2) [], - //set to enable signal output on channel n + // set to enable signal output on channel n SIG_OUT_EN_CH OFFSET(2) NUMBITS(1) [], - //controls the output value when channel n is inactive(LEDC_SIG_OUT_EN_CHn == 0) + // controls the output value when channel n is inactive(LEDC_SIG_OUT_EN_CHn == 0) IDLE_LV_CH OFFSET(3) NUMBITS(1) [ Low = 0, High = 1, ], - //used to update the listed fields below for channel n; is automatically cleared by hardware - //HPOINT_CHn, DUTY_START_CHn, SIG_OUT_EN_CHn, TIMER_SEL_CHn, DUTY_NUM_CHn, DUTY_CYCLE_CHn, - //DUTY_SCALE_CHn, DUTY_INC_CHn and OVF_CNT_EN_CHn + // used to update the listed fields below for channel n; is automatically cleared by hardware + // HPOINT_CHn, DUTY_START_CHn, SIG_OUT_EN_CHn, TIMER_SEL_CHn, DUTY_NUM_CHn, DUTY_CYCLE_CHn, + // DUTY_SCALE_CHn, DUTY_INC_CHn and OVF_CNT_EN_CHn PARA_UP_CH OFFSET(4) NUMBITS(1) [], - //configures maximum times of overflow - 1 - //LEDC_OVF_CNT_CHn_INT will be triggered when channel n overflows for (LEDC_OVF_NUM_CHn + 1) times + // configures maximum times of overflow - 1 + // LEDC_OVF_CNT_CHn_INT will be triggered when channel n overflows for (LEDC_OVF_NUM_CHn + 1) times OVF_NUM_CH OFFSET(5) NUMBITS(10) [], - //counts the number of times when the timer selected by channel n overflows + // counts the number of times when the timer selected by channel n overflows OVF_CNT_EN_CH OFFSET(15) NUMBITS(1) [], - //set to reset timer-overflow counter of channel n + // set to reset timer-overflow counter of channel n OVF_CNT_RESET_CH OFFSET(16) NUMBITS(1) [], ], LEDC_CH_CONF1 [ - //configures the step size of the duty cucle change during fading + // configures the step size of the duty cucle change during fading DUTY_SCALE_CH OFFSET(0) NUMBITS(10) [], - //sets the number of counter overflow cycles for every Lpointn increment/decrement + // sets the number of counter overflow cycles for every Lpointn increment/decrement DUTY_CYCLE_CH OFFSET(10) NUMBITS(10) [], - //controls the number of times the duty cycle will be incremented/decremented + // controls the number of times the duty cycle will be incremented/decremented DUTY_NUM_CH OFFSET(20) NUMBITS(10) [], - //determines the monotony of the duty cycle of the output signal on channel n - //0: Decreases - //1: Increases + // determines the monotony of the duty cycle of the output signal on channel n + // 0: Decreases + // 1: Increases DUTY_INC_CH OFFSET(30) NUMBITS(1) [], - //set to enable duty cycle fading on channel n + // set to enable duty cycle fading on channel n DUTY_START_CH OFFSET(31) NUMBITS(1) [], ], LEDC_CONF [ - //used to select the clock source for all 4 timers - //01: APB_CLK - //10: FOSC_CLK - //11: XTAL_CLK - APB_CLK_SEL OFFSET(0) NUMBITS(2) [], - //used to control clock - //0: support clock only when application writes registers - //1: force clock on register + // used to select the clock source for all 4 timers + // 01: APB_CLK + // 10: RC_FAST_CLK + // 11: XTAL_CLK + APB_CLK_SEL OFFSET(0) NUMBITS(2) [ + APB_CLK = 0b01, + RC_FAST_CLK = 0b10, + XTAL_CLK = 0b11, + ], + // used to control clock + // 0: support clock only when application writes registers + // 1: force clock on register CLK_EN OFFSET(31) NUMBITS(1) [], ], LEDC_CH_HPOINT [ - //signal output value changes to high when the selected timer - //has reached the value in this field + // signal output value changes to high when the selected timer + // has reached the value in this field HPOINT_CH OFFSET(0) NUMBITS(14) [], ], LEDC_CH_DUTY [ - //signal output value changes to low when the selected timer - //has reached the value specified in this field + // signal output value changes to low when the selected timer + // has reached the value specified in this field DUTY_CH OFFSET(0) NUMBITS(19) [], ], LEDC_CH_DUTY_R [ DUTY_R_CH OFFSET(0) NUMBITS(19) [], ], LEDC_TIMER_CONF [ - //used to control range of counter of timer x + // used to control range of counter of timer x TIMER_DUTY_RES OFFSET(0) NUMBITS(4) [], - //used to configure the divisor for the divider of timer x + // used to configure the divisor for the divider of timer x CLK_DIV_TIMER OFFSET(4) NUMBITS(18) [], - //used to suspend the counter of timer x + // used to suspend the counter of timer x TIMER_PAUSE OFFSET(22) NUMBITS(1) [], - //used to reset timer x; counter will show 0 after reset + // used to reset timer x; counter will show 0 after reset TIMER_RST OFFSET(23) NUMBITS(1) [], - //set to update LEDC_CLK_DIV_TIMERx and LEDC_TIMERx_DUTY_RES + // set to update LEDC_CLK_DIV_TIMERx and LEDC_TIMERx_DUTY_RES TIMER_PARA_UP OFFSET(25) NUMBITS(1) [], ], LEDC_TIMER_VALUE [ - //stores the current counter value of timer x + // stores the current counter value of timer x TIMER_CNT OFFSET(0) NUMBITS(14) [], ], LEDC_INT [ - //RAW: - //triggered when timer x has reached its maximum counter value - //ST: - //masked interrupt status bit for the LEDC_TIMERx_OVF_INT interrupt - //when LEDC_TIMERx_OVF_INT_ENA is set to 1 - //ENA: - //set to enable LEDC_TIMERx_OVF_INT interrupt - //CLR: - //set to clear LEDC_TIMERx_OVF_INT interrupt + // RAW: + // triggered when timer x has reached its maximum counter value + // ST: + // masked interrupt status bit for the LEDC_TIMERx_OVF_INT interrupt + // when LEDC_TIMERx_OVF_INT_ENA is set to 1 + // ENA: + // set to enable LEDC_TIMERx_OVF_INT interrupt + // CLR: + // set to clear LEDC_TIMERx_OVF_INT interrupt TIMER0_OVF OFFSET(0) NUMBITS(1) [], TIMER1_OVF OFFSET(1) NUMBITS(1) [], TIMER2_OVF OFFSET(2) NUMBITS(1) [], TIMER3_OVF OFFSET(3) NUMBITS(1) [], - //RAW: - //interrupt raw bit for channel n - //triggered when the gradual change of duty has finished - //ST: - //masked interrupt status bit for LEDC_DUTY_CHNG_END_CHn_INT interrupt - //when LEDC_DUTY_CHNG_CHn_INT_ENA is set to 1 - //ENA: - //set to enable LEDC_DUTY_CHNG_END_CHn_INT interrupt - //CLR: - //set to clear LEDC_DUTY_CHNG_END_CHn_INT interrupt + // RAW: + // interrupt raw bit for channel n + // triggered when the gradual change of duty has finished + // ST: + // masked interrupt status bit for LEDC_DUTY_CHNG_END_CHn_INT interrupt + // when LEDC_DUTY_CHNG_CHn_INT_ENA is set to 1 + // ENA: + // set to enable LEDC_DUTY_CHNG_END_CHn_INT interrupt + // CLR: + // set to clear LEDC_DUTY_CHNG_END_CHn_INT interrupt DUTY_CHNG_END_CH0 OFFSET(4) NUMBITS(1) [], DUTY_CHNG_END_CH1 OFFSET(5) NUMBITS(1) [], DUTY_CHNG_END_CH2 OFFSET(6) NUMBITS(1) [], DUTY_CHNG_END_CH3 OFFSET(7) NUMBITS(1) [], DUTY_CHNG_END_CH4 OFFSET(8) NUMBITS(1) [], DUTY_CHNG_END_CH5 OFFSET(9) NUMBITS(1) [], - //RAW: - //interrupt raw bit for channel n - //triggered when overflow counter has reached value specified by LEDC_OVF_NUM_CHn - //ST: - //masked interrupt status bit for LEDC_DUTY_CNT_CHn_INT interrupt - //when LEDC_OVF_CNT_CHn_INT_ENA is set to 1 - //ENA: - //set to enable LEDC_OVF_CHn_INT interrupt - //CLR: - //set to clear LEDC_OVF_CNT_CHn_INT interrupt + // RAW: + // interrupt raw bit for channel n + // triggered when overflow counter has reached value specified by LEDC_OVF_NUM_CHn + // ST: + // masked interrupt status bit for LEDC_DUTY_CNT_CHn_INT interrupt + // when LEDC_OVF_CNT_CHn_INT_ENA is set to 1 + // ENA: + // set to enable LEDC_OVF_CHn_INT interrupt + // CLR: + // set to clear LEDC_OVF_CNT_CHn_INT interrupt OVF_CNT_CH0 OFFSET(10) NUMBITS(1) [], OVF_CNT_CH1 OFFSET(11) NUMBITS(1) [], OVF_CNT_CH2 OFFSET(12) NUMBITS(1) [], @@ -169,371 +173,29 @@ register_bitfields! [u32, OVF_CNT_CH5 OFFSET(15) NUMBITS(1) [], ], LEDC_DATE [ - //version control register + // version control register LEDC_DATE OFFSET(0) NUMBITS(32) [], ] ]; -#[derive(Copy, Clone)] -pub enum Timer { - Timer0 = 0, - Timer1 = 1, - Timer2 = 2, - Timer3 = 3, -} - -#[derive(Copy, Clone)] -pub enum PwmChannel { - Pwm0 = 0, - Pwm1 = 1, - Pwm2 = 2, - Pwm3 = 3, - Pwm4 = 4, - Pwm5 = 5, -} - -pub enum ClockSource { - Apb = 1, - RcFast = 2, - Xtal = 3, -} - -//Duty cycles can be of two types -pub enum DutyCycle { - Fixed(u32, u32), - //with a payload used to store: - // - highpoint value - // - lowpoint value - Fading(u32, u32, u32, i32, u32, u32), - //with a payload used to store: - // - highpoint value - // - lowpoint value - // - the number of counter overflows after which the lowpoint value will be incremented/decremented - // - (+/-)1 value that determines whether the duty cycle output signal increases or decreases - // - step size of the duty cycle change - // - the number of times the duty cycle will be changed -} - pub struct LedPwm { registers: StaticRef, - //maybe add an array with the current specs for each timer and pwm ? } impl LedPwm { pub const fn new() -> Self { LedPwm { - registers: LED_PWM_BASE, - } - } - - // functions that configure the timers - - pub fn set_clk_source(&self, clock: ClockSource) { - self.registers - .ledc_conf - .modify(LEDC_CONF::APB_CLK_SEL.val(clock as u32)); - } - - pub fn configure_timer_divider(&self, timer: Timer, a: u32, b: u32) -> Result<(), ErrorCode> { - if a >= 1024 || b >= 256 { - Err(ErrorCode::SIZE) - } else { - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::CLK_DIV_TIMER.val((a << 12) + (b << 4))); - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); - todo!("^This will cause the newly configured values to take effect upon the next overflow of the counter"); - Ok(()) - } - } - - pub fn suspend_timer_counter(&self, timer: Timer) { - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::TIMER_PAUSE::SET); - } - - pub fn resume_timer_counter(&self, timer: Timer) { - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::TIMER_PAUSE::CLEAR); - } - - pub fn reset_timer_counter(&self, timer: Timer) { - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::TIMER_RST::SET); - todo!("is it cleared by hardware or should i do it manually ?"); - } - - pub fn configure_timer_counter( - &self, - timer: Timer, - overflow_value: u32, - ) -> Result<(), ErrorCode> { - if overflow_value >= 16 { - Err(ErrorCode::SIZE) - } else { - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::TIMER_DUTY_RES.val(overflow_value)); - self.registers.ledc_timer[timer as usize] - .conf - .modify(LEDC_TIMER_CONF::TIMER_PARA_UP::SET); - todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); - Ok(()) - } - } - - pub fn configure_timer( - &self, - timer: Timer, - a: u32, - b: u32, - overflow_value: u32, - ) -> Result<(), ErrorCode> { - if let Err(e) = self.configure_timer_divider(timer, a, b) { - return Err(e); - } - if let Err(e) = self.configure_timer_counter(timer, overflow_value) { - return Err(e); - } - Ok(()) - } - - // functions that configure the pwm generator - - pub fn set_pwm_timer(&self, pwm: PwmChannel, timer: Timer) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::TIMER_SEL_CH.val(timer as u32)); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); - } - - pub fn enable_overflow_counting(&self, pwm: PwmChannel) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::SET); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - } - - pub fn disable_overflows_counting(&self, pwm: PwmChannel) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::OVF_CNT_EN_CH::CLEAR); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - } - - pub fn reset_timer_overflow_counter(&self, pwm: PwmChannel) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::OVF_CNT_RESET_CH::SET); - todo!("is it cleared by hardware or should i do it manually ?"); - } - - pub fn set_overflows_to_generate_interrupt( - &self, - pwm: PwmChannel, - nr: u32, - ) -> Result<(), ErrorCode> { - if nr >= 1024 { - Err(ErrorCode::SIZE) - } else { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::OVF_NUM_CH.val(nr)); - Ok(()) - } - } - - pub fn enable_overflows_interrupt(&self, pwm: PwmChannel) { - let mask: u32 = 0b0000_0000_0000_0000_0000_0100_0000_0000 << (pwm as u32); - self.registers - .ledc_int_ena - .set(self.registers.ledc_int_ena.get() | mask); - } - - pub fn disable_overflows_interrupt(&self, pwm: PwmChannel) { - let p = pwm as u32; - let mask: u32 = (!0b0000_0000_0000_0000_0000_0100_0000_0000 << p) + (u32::pow(2, p) - 1); - self.registers - .ledc_int_ena - .set(self.registers.ledc_int_ena.get() & mask); - } - - pub fn configure_pwm_fixed_duty_cycle( - &self, - pwm: PwmChannel, - high_point: u32, - low_point: u32, - ) -> Result<(), ErrorCode> { - if high_point >= 16384 || low_point >= 49151 { - Err(ErrorCode::SIZE) - } else if high_point > low_point { - Err(ErrorCode::INVAL) - } else { - self.registers.ledc_ch[pwm as usize] - .hpoint - .modify(LEDC_CH_HPOINT::HPOINT_CH.val(high_point)); - self.registers.ledc_ch[pwm as usize] - .duty - .modify(LEDC_CH_DUTY::DUTY_CH.val((low_point - high_point) << 4)); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); - Ok(()) - } - } - - pub fn enable_pwm_signal_output(&self, pwm: PwmChannel) { - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::SIG_OUT_EN_CH::SET); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - todo!("This will cause the newly configured values to take effect upon the next overflow of the counter."); - } - - pub fn disable_pwm_signal_output(&self, pwm: PwmChannel, lvl: bool) { - self.registers.ledc_ch[pwm as usize].conf0.modify( - LEDC_CH_CONF0::SIG_OUT_EN_CH::CLEAR - + if lvl { - LEDC_CH_CONF0::IDLE_LV_CH::Value::High.into() - } else { - LEDC_CH_CONF0::IDLE_LV_CH::Value::Low.into() - }, - ); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - } - - pub fn dither_pwm_duty_cycles(&self, pwm: PwmChannel, nr: u32) -> Result<(), ErrorCode> { - if nr >= 16 { - Err(ErrorCode::SIZE) - } else { - self.registers.ledc_ch[pwm as usize] - .duty - .modify(LEDC_CH_DUTY::DUTY_CH.val(nr)); - Ok(()) - } - } - - pub fn enable_fading_duty(&self, pwm: PwmChannel) { - self.registers.ledc_ch[pwm as usize] - .conf1 - .modify(LEDC_CH_CONF1::DUTY_START_CH::SET); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - } - - pub fn disable_fading_duty(&self, pwm: PwmChannel) { - self.registers.ledc_ch[pwm as usize] - .conf1 - .modify(LEDC_CH_CONF1::DUTY_START_CH::CLEAR); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - } - - pub fn configure_pwm_fading_duty_cycle( - &self, - pwm: PwmChannel, - high_point: u32, - low_point: u32, - counter_overflows: u32, - duty_monotony: i32, - duty_step: u32, - steps_number: u32, - ) -> Result<(), ErrorCode> { - if counter_overflows >= 1024 || duty_step >= 1024 || steps_number >= 1024 { - Err(ErrorCode::SIZE) - } else { - if let Err(e) = self.configure_pwm_fixed_duty_cycle(pwm, high_point, low_point) { - return Err(e); - } - self.registers.ledc_ch[pwm as usize].conf1.modify( - LEDC_CH_CONF1::DUTY_CYCLE_CH.val(counter_overflows) - + if duty_monotony < 0 { - LEDC_CH_CONF1::DUTY_INC_CH::CLEAR - } else { - LEDC_CH_CONF1::DUTY_INC_CH::SET - } - + LEDC_CH_CONF1::DUTY_SCALE_CH.val(duty_step) - + LEDC_CH_CONF1::DUTY_NUM_CH.val(steps_number), - ); - self.registers.ledc_ch[pwm as usize] - .conf0 - .modify(LEDC_CH_CONF0::PARA_UP_CH::SET); - Ok(()) - } - } - - pub fn configure_pwm(&self, pwm: PwmChannel, duty_cycle: DutyCycle) -> Result<(), ErrorCode> { - match duty_cycle { - DutyCycle::Fixed(a, b) => { - if let Err(e) = self.configure_pwm_fixed_duty_cycle(pwm, a, b) { - Err(e) - } else { - Ok(()) - } - } - DutyCycle::Fading(a, b, c, d, e, f) => { - if let Err(e) = self.configure_pwm_fading_duty_cycle(pwm, a, b, c, d, e, f) { - Err(e) - } else { - Ok(()) - } - } + registers: LEDC_BASE, } } - // functions regarding interrupts - pub fn handle_interrupt(&self) { - //how do i signal which interrupt to handle todo!() } - - pub fn counter_overflow_interrupt_is_triggered(&self, timer: Timer) -> bool { - let mask: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0001 << (timer as usize); - self.registers.ledc_int_raw.get() & mask != 0 - } - - pub fn counter_overflow_interrupt_st_reg(&self, timer: Timer) { - todo!("not sure what a masked interrupt is and what to do with it") - } - - pub fn enable_counter_overflow_interrupt(&self, timer: Timer) { - let mask: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0001 << (timer as u32); - self.registers - .ledc_int_ena - .set(self.registers.ledc_int_ena.get() | mask); - } - - pub fn clear_counter_overflow_interrupt(&self, timer: Timer) { - let t = timer as u32; - let mask: u32 = (0b1111_1111_1111_1111_1111_1111_1111_1110 << t) + (u32::pow(2, t) - 1); - self.registers - .ledc_int_clr - .set(self.registers.ledc_int_clr.get() & mask); - } } impl hil::pwm::Pwm for LedPwm { - type Pin = u32; + type Pin = esp32::gpio::GpioPin<'static>; fn start( &self, @@ -553,14 +215,12 @@ impl hil::pwm::Pwm for LedPwm { // // LEDC_CLKx / (LEDC_CLK_DIV_TIMERx * 2 ^ LEDC_TIMERx_DUTY_RES) // - // highest LEDC_CLK freq is 80MHz, given by selecting APB_CLK as timer clock source - // when using PLL_CLK as clock source for the CPU - 80000000 + todo!() } fn get_maximum_duty_cycle(&self) -> usize { // the PWM can be set to high for the entire period of the signal, // resulting in a 100% duty cycle - 80000000 + self.get_maximum_frequency_hz() } } From 780018d9f508d994d771c4925a155583425e5e37 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Tue, 17 Jan 2023 19:09:41 +0200 Subject: [PATCH 09/10] added RcFast CPU clock + prescaler option RcFast/Xtal --- boards/esp32-c3-devkitM-1/src/main.rs | 3 +- chips/esp32-c3/src/sysreg.rs | 53 +++++++++++++++++++-------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/boards/esp32-c3-devkitM-1/src/main.rs b/boards/esp32-c3-devkitM-1/src/main.rs index f2ae60af0a..476146dfbb 100644 --- a/boards/esp32-c3-devkitM-1/src/main.rs +++ b/boards/esp32-c3-devkitM-1/src/main.rs @@ -11,6 +11,7 @@ use capsules::virtual_alarm::{MuxAlarm, VirtualMuxAlarm}; use esp32_c3::chip::Esp32C3DefaultPeripherals; +use esp32_c3::sysreg::{ClockPrescaler, CpuClock}; use kernel::capabilities; use kernel::component::Component; use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState}; @@ -152,7 +153,7 @@ unsafe fn setup() -> ( peripherals .sysreg - .use_pll_clock_source(PllFrequency::MHz320, CpuFrequency::MHz160); + .set_clock_source(CpuClock::Pll(PllFrequency::MHz320, CpuFrequency::MHz160)); // initialise capabilities let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability); diff --git a/chips/esp32-c3/src/sysreg.rs b/chips/esp32-c3/src/sysreg.rs index e90d46a009..4aac099443 100644 --- a/chips/esp32-c3/src/sysreg.rs +++ b/chips/esp32-c3/src/sysreg.rs @@ -3,6 +3,7 @@ use kernel::utilities::registers::interfaces::{ReadWriteable, Readable}; use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite}; use kernel::utilities::StaticRef; +use kernel::ErrorCode; pub const SYS_REG_BASE: StaticRef = unsafe { StaticRef::new(0x600C_0000 as *const SysRegRegisters) }; @@ -47,7 +48,7 @@ register_bitfields![u32, SOC_CLK_SEL OFFSET(10) NUMBITS(2) [ Xtal = 0, Pll = 1, - Fosc = 2, + RcFast = 2, ], CLK_XTAL_FREQ OFFSET(12) NUMBITS(6) [], ], @@ -65,6 +66,23 @@ pub enum CpuFrequency { MHz160 = 1, } +pub struct ClockPrescaler(u16); + +impl ClockPrescaler { + pub fn new(prescaler: u16) -> Self { + ClockPrescaler(match prescaler >= 1024 { + true => 1, + false => prescaler, + }) + } +} + +pub enum CpuClock { + Xtal(ClockPrescaler), + Pll(PllFrequency, CpuFrequency), + RcFast(ClockPrescaler), +} + pub struct SysReg { registers: StaticRef, } @@ -76,20 +94,25 @@ impl SysReg { } } - pub fn use_xtal_clock_source(&self) { - self.registers - .sysclk_config - .modify(SYSCLK_CONFIG::SOC_CLK_SEL::Xtal); - } - - pub fn use_pll_clock_source(&self, pll_frequency: PllFrequency, cpu_frequency: CpuFrequency) { - self.registers - .sysclk_config - .modify(SYSCLK_CONFIG::SOC_CLK_SEL::Pll); - self.registers.cpu_per_conf.modify( - CPU_PER_CONF::PLL_FREQ_SEL.val(pll_frequency as u32) - + CPU_PER_CONF::CPUPERIOD_SEL.val(cpu_frequency as u32), - ); + pub fn set_clock_source(&self, clock: CpuClock) { + match clock { + CpuClock::Xtal(ClockPrescaler(prescaler)) => self.registers.sysclk_config.modify( + SYSCLK_CONFIG::SOC_CLK_SEL::Xtal + SYSCLK_CONFIG::PRE_DIV_CNT.val(prescaler as u32), + ), + CpuClock::Pll(pll_frequency, cpu_frequency) => { + self.registers + .sysclk_config + .modify(SYSCLK_CONFIG::SOC_CLK_SEL::Pll); + self.registers.cpu_per_conf.modify( + CPU_PER_CONF::PLL_FREQ_SEL.val(pll_frequency as u32) + + CPU_PER_CONF::CPUPERIOD_SEL.val(cpu_frequency as u32), + ); + } + CpuClock::RcFast(ClockPrescaler(prescaler)) => self.registers.sysclk_config.modify( + SYSCLK_CONFIG::SOC_CLK_SEL::RcFast + + SYSCLK_CONFIG::PRE_DIV_CNT.val(prescaler as u32), + ), + } } //Enable the APB_CLK signal to LED PWM From 93ec60795b68bfa3bfb4fb13d0f640e026ca4da8 Mon Sep 17 00:00:00 2001 From: CosminGGeorgescu Date: Wed, 18 Jan 2023 16:43:09 +0200 Subject: [PATCH 10/10] get_cpu_clock --- chips/esp32-c3/src/sysreg.rs | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/chips/esp32-c3/src/sysreg.rs b/chips/esp32-c3/src/sysreg.rs index 4aac099443..04b4f5e387 100644 --- a/chips/esp32-c3/src/sysreg.rs +++ b/chips/esp32-c3/src/sysreg.rs @@ -60,16 +60,36 @@ pub enum PllFrequency { MHz480 = 1, } +impl From for PllFrequency { + fn from(value: u32) -> Self { + match value { + 0 => PllFrequency::MHz320, + 1 => PllFrequency::MHz480, + _ => unreachable!(), + } + } +} + #[repr(u32)] pub enum CpuFrequency { MHz80 = 0, MHz160 = 1, } -pub struct ClockPrescaler(u16); +impl From for CpuFrequency { + fn from(value: u32) -> Self { + match value { + 0 => CpuFrequency::MHz80, + 1 => CpuFrequency::MHz160, + _ => unreachable!(), + } + } +} + +pub struct ClockPrescaler(u32); impl ClockPrescaler { - pub fn new(prescaler: u16) -> Self { + pub fn new(prescaler: u32) -> Self { ClockPrescaler(match prescaler >= 1024 { true => 1, false => prescaler, @@ -115,6 +135,36 @@ impl SysReg { } } + pub fn get_clock_source(&self) -> Option { + match self + .registers + .sysclk_config + .read_as_enum(SYSCLK_CONFIG::SOC_CLK_SEL) + { + Some(SYSCLK_CONFIG::SOC_CLK_SEL::Value::Xtal) => Some(CpuClock::Xtal(ClockPrescaler( + self.registers + .sysclk_config + .read(SYSCLK_CONFIG::PRE_DIV_CNT), + ))), + Some(SYSCLK_CONFIG::SOC_CLK_SEL::Value::Pll) => Some(CpuClock::Pll( + PllFrequency::from(self.registers.cpu_per_conf.read(CPU_PER_CONF::PLL_FREQ_SEL)), + CpuFrequency::from( + self.registers + .cpu_per_conf + .read(CPU_PER_CONF::CPUPERIOD_SEL), + ), + )), + Some(SYSCLK_CONFIG::SOC_CLK_SEL::Value::RcFast) => { + Some(CpuClock::RcFast(ClockPrescaler( + self.registers + .sysclk_config + .read(SYSCLK_CONFIG::PRE_DIV_CNT), + ))) + } + None => None, + } + } + //Enable the APB_CLK signal to LED PWM pub fn enable_ledc(&self) { self.registers