diff --git a/rust/k210-shared/src/soc/pwm.rs b/rust/k210-shared/src/soc/pwm.rs index f284abb..d2830c8 100644 --- a/rust/k210-shared/src/soc/pwm.rs +++ b/rust/k210-shared/src/soc/pwm.rs @@ -1,6 +1,6 @@ use k210_hal::pac; -// TODO: generalize over other timers than TIMER0 -// use pac::{timer0,TIMER0,TIMER1,TIMER2}; +use core::ops::Deref; +use pac::{timer0,TIMER0,TIMER1,TIMER2}; use crate::soc::sysctl; @@ -12,45 +12,84 @@ pub enum Channel { CH4, } -/** Start a PWM channel */ -pub fn pwm_start(ch: Channel) { - unsafe { - let ptr = pac::TIMER0::ptr(); - use pac::timer0::channel::control::MODEW; +pub trait TimerExt: Deref + Sized { + #[doc(hidden)] + const CLK: sysctl::clock; + #[doc(hidden)] + const DIV: sysctl::threshold; - // set a deterministic value for load counts - (*ptr).channel[ch as usize].load_count.write(|w| w.bits(1)); - (*ptr).load_count2[ch as usize].write(|w| w.bits(1)); - // start channel - (*ptr).channel[ch as usize].control.write( - |w| w.interrupt().set_bit() - .pwm_enable().set_bit() - .mode().variant(MODEW::USER) - .enable().set_bit()); - } + /// Constrains TIMER peripheral for PWM use + /// A timer channel can either be used for PWM or as a normal timer (say, for interrupt + /// generation). Currently this has a larger granularity than needed and + /// constrains the entire peripheral for PWM use. + fn constrain_pwm(self) -> PWMImpl; } -/** Stop a PWM channel */ -pub fn pwm_stop(ch: Channel) { - unsafe { - let ptr = pac::TIMER0::ptr(); +impl TimerExt for TIMER0 { + const CLK: sysctl::clock = sysctl::clock::TIMER0; + const DIV: sysctl::threshold = sysctl::threshold::TIMER0; - (*ptr).channel[ch as usize].control.write( + fn constrain_pwm(self) -> PWMImpl { PWMImpl:: { timer: self } } +} +impl TimerExt for TIMER1 { + const CLK: sysctl::clock = sysctl::clock::TIMER1; + const DIV: sysctl::threshold = sysctl::threshold::TIMER1; + + fn constrain_pwm(self) -> PWMImpl { PWMImpl:: { timer: self } } +} +impl TimerExt for TIMER2 { + const CLK: sysctl::clock = sysctl::clock::TIMER2; + const DIV: sysctl::threshold = sysctl::threshold::TIMER2; + + fn constrain_pwm(self) -> PWMImpl { PWMImpl:: { timer: self } } +} + +/** Trait for PWM control */ +pub trait PWM { + // TODO: make this per channel, and use the PWM trait from Rust embedded + fn start(&self, ch: Channel); + fn stop(&self, ch: Channel); + fn set(&self, ch: Channel, freq: u32, value: f32) -> u32; +} + +pub struct PWMImpl { + timer: TIMER, +} + +impl PWM for PWMImpl { + /** Start a PWM channel */ + fn start(&self, ch: Channel) { + unsafe { + use pac::timer0::channel::control::MODEW; + + // set a deterministic value for load counts + self.timer.channel[ch as usize].load_count.write(|w| w.bits(1)); + self.timer.load_count2[ch as usize].write(|w| w.bits(1)); + // start channel + self.timer.channel[ch as usize].control.write( + |w| w.interrupt().set_bit() + .pwm_enable().set_bit() + .mode().variant(MODEW::USER) + .enable().set_bit()); + } + } + + /** Stop a PWM channel */ + fn stop(&self, ch: Channel) { + self.timer.channel[ch as usize].control.write( |w| w.interrupt().set_bit()); } -} -/** Set frequency and value for a PWM channel */ -pub fn pwm_set(ch: Channel, freq: u32, value: f32) -> u32 { - let clk_freq = sysctl::clock_get_freq(sysctl::clock::TIMER0); - let periods = clk_freq / freq; - let percent = (value * (periods as f32)) as u32; - unsafe { - let ptr = pac::TIMER0::ptr(); - (*ptr).channel[ch as usize].load_count.write(|w| w.bits(periods - percent)); - (*ptr).load_count2[ch as usize].write(|w| w.bits(percent)); + /** Set frequency and value for a PWM channel */ + fn set(&self, ch: Channel, freq: u32, value: f32) -> u32 { + let clk_freq = sysctl::clock_get_freq(TIMER::CLK); + let periods = clk_freq / freq; + let percent = (value * (periods as f32)) as u32; + unsafe { + self.timer.channel[ch as usize].load_count.write(|w| w.bits(periods - percent)); + self.timer.load_count2[ch as usize].write(|w| w.bits(percent)); + } + clk_freq / periods } - clk_freq / periods } - diff --git a/rust/rgbcontrol/src/main.rs b/rust/rgbcontrol/src/main.rs index 4039e83..6593ad4 100644 --- a/rust/rgbcontrol/src/main.rs +++ b/rust/rgbcontrol/src/main.rs @@ -17,7 +17,7 @@ use k210_shared::board::lcd_render::render_image; use k210_shared::board::ns2009::TouchScreen; use k210_shared::soc::fpioa; use k210_shared::soc::i2c::{I2CExt, I2C}; -use k210_shared::soc::pwm::{pwm_set, pwm_start, Channel}; +use k210_shared::soc::pwm::{TimerExt, PWM, Channel}; use k210_shared::soc::sleep::usleep; use k210_shared::soc::spi::SPIExt; use k210_shared::soc::sysctl; @@ -99,10 +99,11 @@ fn main() -> ! { }); writeln!(stdout, "start PWM").unwrap(); + let pwm = p.TIMER0.constrain_pwm(); sysctl::clock_enable(sysctl::clock::TIMER0); - pwm_start(Channel::CH1); - pwm_start(Channel::CH2); - pwm_start(Channel::CH3); + pwm.start(Channel::CH1); + pwm.start(Channel::CH2); + pwm.start(Channel::CH3); let freq = 10000; @@ -114,9 +115,9 @@ fn main() -> ! { //writeln!(stdout, "{:?}", ev).unwrap(); let (r, g, b) = color_from_xy(x, y, clampf(ev.z as f32 / 1000.0)); - pwm_set(Channel::CH1, freq, 1.0 - r * R_SCALE); - pwm_set(Channel::CH2, freq, 1.0 - g * G_SCALE); - pwm_set(Channel::CH3, freq, 1.0 - b * B_SCALE); + pwm.set(Channel::CH1, freq, 1.0 - r * R_SCALE); + pwm.set(Channel::CH2, freq, 1.0 - g * G_SCALE); + pwm.set(Channel::CH3, freq, 1.0 - b * B_SCALE); } }