rust: make PWM usable with every timer

This commit is contained in:
Wladimir J. van der Laan 2019-05-31 16:17:11 +00:00
parent f8166abd0f
commit 62b594b8dc
2 changed files with 81 additions and 41 deletions

View File

@ -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<Target = timer0::RegisterBlock> + 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<Self>;
}
/** 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<Self> { PWMImpl::<Self> { 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<Self> { PWMImpl::<Self> { 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<Self> { PWMImpl::<Self> { 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: TIMER,
}
impl<TIMER: TimerExt> PWM for PWMImpl<TIMER> {
/** 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
}

View File

@ -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);
}
}