Added PWM implementation and example
This commit is contained in:
parent
baec0980b7
commit
9af01f3067
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Basic API for EDAC functionality
|
- Basic API for EDAC functionality
|
||||||
|
- PWM implementation and example
|
||||||
|
|
||||||
## [0.2.2]
|
## [0.2.2]
|
||||||
|
|
||||||
|
@ -47,3 +47,7 @@ required-features = ["rt"]
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "tests"
|
name = "tests"
|
||||||
required-features = ["rt"]
|
required-features = ["rt"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "pwm"
|
||||||
|
required-features = ["rt"]
|
||||||
|
78
examples/pwm.rs
Normal file
78
examples/pwm.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//! Simple PWM example
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::PwmPin;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
pwm::{self, get_duty_from_percent, ReducedPwmPin, PWMA, PWMB},
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_timer, Delay},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx PWM example application--");
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA);
|
||||||
|
let mut pwm = pwm::PwmPin::new(
|
||||||
|
(pinsa.pa3.into_funsel_1(), dp.TIM3),
|
||||||
|
50.mhz(),
|
||||||
|
&mut dp.SYSCONFIG,
|
||||||
|
10.hz(),
|
||||||
|
);
|
||||||
|
let timer = set_up_ms_timer(
|
||||||
|
&mut dp.SYSCONFIG,
|
||||||
|
&mut dp.IRQSEL,
|
||||||
|
50.mhz().into(),
|
||||||
|
dp.TIM0,
|
||||||
|
pac::Interrupt::OC0,
|
||||||
|
);
|
||||||
|
let mut delay = Delay::new(timer);
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
|
}
|
||||||
|
let mut current_duty_cycle = 0.0;
|
||||||
|
PwmPin::set_duty(&mut pwm, get_duty_from_percent(current_duty_cycle));
|
||||||
|
PwmPin::enable(&mut pwm);
|
||||||
|
|
||||||
|
// Delete type information, increased code readibility for the rest of the code
|
||||||
|
let mut reduced_pin = ReducedPwmPin::from(pwm);
|
||||||
|
loop {
|
||||||
|
// Increase duty cycle continuously
|
||||||
|
while current_duty_cycle < 1.0 {
|
||||||
|
delay.delay_ms(200);
|
||||||
|
current_duty_cycle += 0.02;
|
||||||
|
PwmPin::set_duty(&mut reduced_pin, get_duty_from_percent(current_duty_cycle));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to PWMB and decrease the window with a high signal from 100 % to 0 %
|
||||||
|
// continously
|
||||||
|
current_duty_cycle = 0.0;
|
||||||
|
let mut upper_limit = 1.0;
|
||||||
|
let mut lower_limit = 0.0;
|
||||||
|
let mut pwmb: ReducedPwmPin<PWMB> = ReducedPwmPin::from(reduced_pin);
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
while lower_limit < 0.5 {
|
||||||
|
delay.delay_ms(200);
|
||||||
|
lower_limit += 0.01;
|
||||||
|
upper_limit -= 0.01;
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||||
|
rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||||
|
}
|
||||||
|
reduced_pin = ReducedPwmPin::<PWMA>::from(pwmb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
@ -731,7 +731,7 @@ impl<I: PinId> InputPin for Pin<I, OutputReadablePushPull> {
|
|||||||
/// Provide a safe register interface for [`Pin`]s
|
/// Provide a safe register interface for [`Pin`]s
|
||||||
///
|
///
|
||||||
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
||||||
/// access the corresponding regsiters.
|
/// access the corresponding registers.
|
||||||
pub(in crate::gpio) struct Registers<I: PinId> {
|
pub(in crate::gpio) struct Registers<I: PinId> {
|
||||||
id: PhantomData<I>,
|
id: PhantomData<I>,
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ impl From<DynPinMode> for ModeFields {
|
|||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Register Interface
|
// Register Interface
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Blocking I2C example]()
|
//! - [REB1 I2C temperature sensor example](https://github.com/robamu-org/vorago-reb1-rs/blob/main/examples/temp-sensor.rs)
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
pac::{I2CA, I2CB, SYSCONFIG},
|
pac::{I2CA, I2CB, SYSCONFIG},
|
||||||
|
@ -7,6 +7,7 @@ pub mod clock;
|
|||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
pub mod pwm;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
457
src/pwm.rs
Normal file
457
src/pwm.rs
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
|
//!
|
||||||
|
//! The Vorago VA108xx devices use the TIM peripherals to perform PWM related tasks
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PWM example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/pwm.rs)
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
||||||
|
pub use crate::{gpio::PinId, prelude::*, time::Hertz, timer::*};
|
||||||
|
|
||||||
|
use va108xx::SYSCONFIG;
|
||||||
|
|
||||||
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
|
pub struct PwmBase {
|
||||||
|
sys_clk: Hertz,
|
||||||
|
/// For PWMB, this is the upper limit
|
||||||
|
current_duty: u16,
|
||||||
|
/// For PWMA, this value will not be used
|
||||||
|
current_lower_limit: u16,
|
||||||
|
current_period: Hertz,
|
||||||
|
current_rst_val: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatusSelPwm {
|
||||||
|
PwmA = 3,
|
||||||
|
PwmB = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PWMA {}
|
||||||
|
pub struct PWMB {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Common
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pwm_common_func {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_a(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_b(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
||||||
|
self.pwm_base.current_period = period.into();
|
||||||
|
// Avoid division by 0
|
||||||
|
if self.pwm_base.current_period.0 == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.pwm_base.current_rst_val =
|
||||||
|
self.pwm_base.sys_clk.0 / self.pwm_base.current_period.0;
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.rst_value
|
||||||
|
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pwmb_func {
|
||||||
|
() => {
|
||||||
|
pub fn pwmb_lower_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_lower_limit
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwmb_upper_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the lower limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is larger than
|
||||||
|
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_lower_limit = duty;
|
||||||
|
let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_lower_limit as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.pwmb_value
|
||||||
|
.write(|w| unsafe { w.bits(pwmb_val as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the higher limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is smaller than
|
||||||
|
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_duty as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Strongly typed PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct PwmPin<PIN: TimPin, TIM: ValidTim, MODE = PWMA> {
|
||||||
|
reg: TimRegister<PIN, TIM>,
|
||||||
|
pwm_base: PwmBase,
|
||||||
|
_mode: PhantomData<MODE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim, MODE> PwmPin<PIN, TIM, MODE>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
/// Create a new stronlgy typed PWM pin
|
||||||
|
pub fn new(
|
||||||
|
vtp: (PIN, TIM),
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
sys_cfg: &mut SYSCONFIG,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin = PwmPin {
|
||||||
|
pwm_base: PwmBase {
|
||||||
|
current_duty: 0,
|
||||||
|
current_lower_limit: 0,
|
||||||
|
current_period: initial_period.into(),
|
||||||
|
current_rst_val: 0,
|
||||||
|
sys_clk: sys_clk.into(),
|
||||||
|
},
|
||||||
|
reg: unsafe { TimRegister::new(vtp.0, vtp.1) },
|
||||||
|
_mode: PhantomData,
|
||||||
|
};
|
||||||
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||||
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
||||||
|
sys_cfg
|
||||||
|
.tim_clk_enable
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.mask_32()) });
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin.set_period(initial_period);
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
pub fn release(self) -> (PIN, TIM) {
|
||||||
|
self.reg.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm_common_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PWMA>> for PwmPin<PIN, TIM, PWMB>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<PIN, TIM, PWMA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
_mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PWMB>> for PwmPin<PIN, TIM, PWMA>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<PIN, TIM, PWMB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
_mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PWMA>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
pub fn pwma(
|
||||||
|
vtp: (PIN, TIM),
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
sys_cfg: &mut SYSCONFIG,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<PIN, TIM, PWMA> = Self::new(vtp, sys_clk, sys_cfg, initial_period);
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PWMB>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
pub fn pwmb(
|
||||||
|
vtp: (PIN, TIM),
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
sys_cfg: &mut SYSCONFIG,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<PIN, TIM, PWMB> = Self::new(vtp, sys_clk, sys_cfg, initial_period);
|
||||||
|
pin.enable_pwm_b();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Reduced PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Reduced version where type information is deleted
|
||||||
|
pub struct ReducedPwmPin<MODE = PWMA> {
|
||||||
|
reg: TimDynRegister,
|
||||||
|
pwm_base: PwmBase,
|
||||||
|
_pin_id: DynPinId,
|
||||||
|
_mode: PhantomData<MODE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PWMA> {
|
||||||
|
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
|
||||||
|
ReducedPwmPin {
|
||||||
|
reg: TimDynRegister::from(pwm_pin.reg),
|
||||||
|
pwm_base: pwm_pin.pwm_base,
|
||||||
|
_pin_id: PIN::DYN,
|
||||||
|
_mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<MODE> ReducedPwmPin<MODE> {
|
||||||
|
pwm_common_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PWMA>> for ReducedPwmPin<PWMB> {
|
||||||
|
fn from(other: ReducedPwmPin<PWMA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
_pin_id: other._pin_id,
|
||||||
|
_mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PWMB>> for ReducedPwmPin<PWMA> {
|
||||||
|
fn from(other: ReducedPwmPin<PWMB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
_pin_id: other._pin_id,
|
||||||
|
_mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// PWMB implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PWMB>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
pwmb_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReducedPwmPin<PWMB> {
|
||||||
|
pwmb_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL implementation: PWMA only
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pwm_pin_impl {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn disable(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty(&mut self, duty: Self::Duty) {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_duty(&self) -> Self::Duty {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_max_duty(&self) -> Self::Duty {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pwm_impl {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn disable(&mut self, _channel: Self::Channel) {
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable(&mut self, _channel: Self::Channel) {
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.ctrl
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_period(&self) -> Self::Time {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty(&mut self, _channel: Self::Channel, duty: Self::Duty) {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.get_reg_block()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_period<P>(&mut self, period: P)
|
||||||
|
where
|
||||||
|
P: Into<Self::Time>,
|
||||||
|
{
|
||||||
|
self.pwm_base.current_period = period.into();
|
||||||
|
// Avoid division by 0
|
||||||
|
if self.pwm_base.current_period.0 == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.pwm_base.current_rst_val =
|
||||||
|
self.pwm_base.sys_clk.0 / self.pwm_base.current_period.0;
|
||||||
|
let reg_block = self.reg.get_reg_block();
|
||||||
|
reg_block
|
||||||
|
.rst_value
|
||||||
|
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
|
||||||
|
reg_block
|
||||||
|
.cnt_value
|
||||||
|
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_duty(&self, _channel: Self::Channel) -> Self::Duty {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_max_duty(&self) -> Self::Duty {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> embedded_hal::Pwm for PwmPin<PIN, TIM> {
|
||||||
|
type Channel = ();
|
||||||
|
type Duty = u16;
|
||||||
|
type Time = Hertz;
|
||||||
|
|
||||||
|
pwm_impl!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::Pwm for ReducedPwmPin<PWMA> {
|
||||||
|
type Channel = ();
|
||||||
|
type Duty = u16;
|
||||||
|
type Time = Hertz;
|
||||||
|
|
||||||
|
pwm_impl!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> embedded_hal::PwmPin for PwmPin<PIN, TIM> {
|
||||||
|
type Duty = u16;
|
||||||
|
|
||||||
|
pwm_pin_impl!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::PwmPin for ReducedPwmPin<PWMA> {
|
||||||
|
type Duty = u16;
|
||||||
|
|
||||||
|
pwm_pin_impl!();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0.
|
||||||
|
///
|
||||||
|
/// Please note that this might load a lot of floating point code because this processor does not
|
||||||
|
/// have a FPU
|
||||||
|
pub fn get_duty_from_percent(percent: f32) -> u16 {
|
||||||
|
if percent > 1.0 {
|
||||||
|
DUTY_MAX
|
||||||
|
} else if percent <= 0.0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(percent * DUTY_MAX as f32) as u16
|
||||||
|
}
|
||||||
|
}
|
249
src/timer.rs
249
src/timer.rs
@ -5,8 +5,15 @@
|
|||||||
//! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs)
|
//! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs)
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
pac,
|
gpio::{
|
||||||
|
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
|
||||||
|
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
|
||||||
|
PA9, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB20, PB21,
|
||||||
|
PB22, PB23, PB3, PB4, PB5, PB6,
|
||||||
|
},
|
||||||
|
pac::{self, tim0},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
private::Sealed,
|
||||||
time::Hertz,
|
time::Hertz,
|
||||||
timer,
|
timer,
|
||||||
};
|
};
|
||||||
@ -22,15 +29,9 @@ use void::Void;
|
|||||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||||
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
/// Hardware timers
|
//==================================================================================================
|
||||||
pub struct CountDownTimer<TIM> {
|
// Defintions
|
||||||
tim: TIM,
|
//==================================================================================================
|
||||||
curr_freq: Hertz,
|
|
||||||
sys_clk: Hertz,
|
|
||||||
rst_val: u32,
|
|
||||||
last_cnt: u32,
|
|
||||||
listening: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interrupt events
|
/// Interrupt events
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
@ -42,6 +43,234 @@ pub enum TimerErrors {
|
|||||||
Canceled,
|
Canceled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Valid TIM and PIN combinations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait TimPin {
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ValidTim {
|
||||||
|
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
|
||||||
|
const TIM_ID: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! tim_marker {
|
||||||
|
($TIMX:ident, $ID:expr) => {
|
||||||
|
impl ValidTim for $TIMX {
|
||||||
|
const TIM_ID: u8 = $ID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tim_marker!(TIM0, 0);
|
||||||
|
tim_marker!(TIM1, 1);
|
||||||
|
tim_marker!(TIM2, 2);
|
||||||
|
tim_marker!(TIM3, 3);
|
||||||
|
tim_marker!(TIM4, 4);
|
||||||
|
tim_marker!(TIM5, 5);
|
||||||
|
tim_marker!(TIM6, 6);
|
||||||
|
tim_marker!(TIM7, 7);
|
||||||
|
tim_marker!(TIM8, 8);
|
||||||
|
tim_marker!(TIM9, 9);
|
||||||
|
tim_marker!(TIM10, 10);
|
||||||
|
tim_marker!(TIM11, 11);
|
||||||
|
tim_marker!(TIM12, 12);
|
||||||
|
tim_marker!(TIM13, 13);
|
||||||
|
tim_marker!(TIM14, 14);
|
||||||
|
tim_marker!(TIM15, 15);
|
||||||
|
tim_marker!(TIM16, 16);
|
||||||
|
tim_marker!(TIM17, 17);
|
||||||
|
tim_marker!(TIM18, 18);
|
||||||
|
tim_marker!(TIM19, 19);
|
||||||
|
tim_marker!(TIM20, 20);
|
||||||
|
tim_marker!(TIM21, 21);
|
||||||
|
tim_marker!(TIM22, 22);
|
||||||
|
tim_marker!(TIM23, 23);
|
||||||
|
|
||||||
|
pub trait ValidTimAndPin<PIN: TimPin, TIM: ValidTim>: Sealed {}
|
||||||
|
|
||||||
|
macro_rules! pin_and_tim {
|
||||||
|
($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:ident) => {
|
||||||
|
impl TimPin for Pin<$PAX, $ALTFUNC>
|
||||||
|
where
|
||||||
|
$PAX: PinId,
|
||||||
|
{
|
||||||
|
const DYN: DynPinId = $PAX::DYN;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> ValidTimAndPin<PIN, TIM> for (Pin<$PAX, $ALTFUNC>, $TIMX)
|
||||||
|
where
|
||||||
|
Pin<$PAX, $ALTFUNC>: TimPin,
|
||||||
|
$PAX: PinId,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_and_tim!(PA31, AltFunc2, 23, TIM23);
|
||||||
|
pin_and_tim!(PA30, AltFunc2, 22, TIM22);
|
||||||
|
pin_and_tim!(PA29, AltFunc2, 21, TIM21);
|
||||||
|
pin_and_tim!(PA28, AltFunc2, 20, TIM20);
|
||||||
|
pin_and_tim!(PA27, AltFunc2, 19, TIM19);
|
||||||
|
pin_and_tim!(PA26, AltFunc2, 18, TIM18);
|
||||||
|
pin_and_tim!(PA25, AltFunc2, 17, TIM17);
|
||||||
|
pin_and_tim!(PA24, AltFunc2, 16, TIM16);
|
||||||
|
|
||||||
|
pin_and_tim!(PA15, AltFunc1, 15, TIM15);
|
||||||
|
pin_and_tim!(PA14, AltFunc1, 14, TIM14);
|
||||||
|
pin_and_tim!(PA13, AltFunc1, 13, TIM13);
|
||||||
|
pin_and_tim!(PA12, AltFunc1, 12, TIM12);
|
||||||
|
pin_and_tim!(PA11, AltFunc1, 11, TIM11);
|
||||||
|
pin_and_tim!(PA10, AltFunc1, 10, TIM10);
|
||||||
|
pin_and_tim!(PA9, AltFunc1, 9, TIM9);
|
||||||
|
pin_and_tim!(PA8, AltFunc1, 8, TIM8);
|
||||||
|
pin_and_tim!(PA7, AltFunc1, 7, TIM7);
|
||||||
|
pin_and_tim!(PA6, AltFunc1, 6, TIM6);
|
||||||
|
pin_and_tim!(PA5, AltFunc1, 5, TIM5);
|
||||||
|
pin_and_tim!(PA4, AltFunc1, 4, TIM4);
|
||||||
|
pin_and_tim!(PA3, AltFunc1, 3, TIM3);
|
||||||
|
pin_and_tim!(PA2, AltFunc1, 2, TIM2);
|
||||||
|
pin_and_tim!(PA1, AltFunc1, 1, TIM1);
|
||||||
|
pin_and_tim!(PA0, AltFunc1, 0, TIM0);
|
||||||
|
|
||||||
|
pin_and_tim!(PB23, AltFunc3, 23, TIM23);
|
||||||
|
pin_and_tim!(PB22, AltFunc3, 22, TIM22);
|
||||||
|
pin_and_tim!(PB21, AltFunc3, 21, TIM21);
|
||||||
|
pin_and_tim!(PB20, AltFunc3, 20, TIM20);
|
||||||
|
pin_and_tim!(PB19, AltFunc3, 19, TIM19);
|
||||||
|
pin_and_tim!(PB18, AltFunc3, 18, TIM18);
|
||||||
|
pin_and_tim!(PB17, AltFunc3, 17, TIM17);
|
||||||
|
pin_and_tim!(PB16, AltFunc3, 16, TIM16);
|
||||||
|
pin_and_tim!(PB15, AltFunc3, 15, TIM15);
|
||||||
|
pin_and_tim!(PB14, AltFunc3, 14, TIM14);
|
||||||
|
pin_and_tim!(PB13, AltFunc3, 13, TIM13);
|
||||||
|
pin_and_tim!(PB12, AltFunc3, 12, TIM12);
|
||||||
|
pin_and_tim!(PB11, AltFunc3, 11, TIM11);
|
||||||
|
pin_and_tim!(PB10, AltFunc3, 10, TIM10);
|
||||||
|
|
||||||
|
pin_and_tim!(PB6, AltFunc3, 6, TIM6);
|
||||||
|
pin_and_tim!(PB5, AltFunc3, 5, TIM5);
|
||||||
|
pin_and_tim!(PB4, AltFunc3, 4, TIM4);
|
||||||
|
pin_and_tim!(PB3, AltFunc3, 3, TIM3);
|
||||||
|
pin_and_tim!(PB2, AltFunc3, 2, TIM2);
|
||||||
|
pin_and_tim!(PB1, AltFunc3, 1, TIM1);
|
||||||
|
pin_and_tim!(PB0, AltFunc3, 0, TIM0);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type TimRegBlock = tim0::RegisterBlock;
|
||||||
|
|
||||||
|
/// Register interface.
|
||||||
|
///
|
||||||
|
/// This interface provides valid TIM pins a way to access their corresponding TIM
|
||||||
|
/// registers
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [`id`] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
pub(super) unsafe trait TimRegInterface {
|
||||||
|
fn tim_id(&self) -> u8;
|
||||||
|
fn pin_id(&self) -> DynPinId;
|
||||||
|
|
||||||
|
const PORT_BASE: *const tim0::RegisterBlock = TIM0::ptr() as *const _;
|
||||||
|
|
||||||
|
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
||||||
|
/// memory mapped peripheral depending on the TIM ID.
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_reg_block(&self) -> &TimRegBlock {
|
||||||
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.tim_id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`ValidTimAndPin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(super) struct TimRegister<PIN: TimPin, TIM: ValidTim> {
|
||||||
|
pin: PIN,
|
||||||
|
tim: TIM,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> TimRegister<PIN, TIM>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(pin: PIN, tim: TIM) -> Self {
|
||||||
|
TimRegister { pin, tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> (PIN, TIM) {
|
||||||
|
(self.pin, self.tim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<PIN: TimPin, TIM: ValidTim> TimRegInterface for TimRegister<PIN, TIM> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pin_id(&self) -> DynPinId {
|
||||||
|
PIN::DYN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimDynRegister {
|
||||||
|
tim_id: u8,
|
||||||
|
pin_id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<TimRegister<PIN, TIM>> for TimDynRegister {
|
||||||
|
fn from(_reg: TimRegister<PIN, TIM>) -> Self {
|
||||||
|
Self {
|
||||||
|
tim_id: TIM::TIM_ID,
|
||||||
|
pin_id: PIN::DYN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl TimRegInterface for TimDynRegister {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
self.tim_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn pin_id(&self) -> DynPinId {
|
||||||
|
self.pin_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Timers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Hardware timers
|
||||||
|
pub struct CountDownTimer<TIM> {
|
||||||
|
tim: TIM,
|
||||||
|
curr_freq: Hertz,
|
||||||
|
sys_clk: Hertz,
|
||||||
|
rst_val: u32,
|
||||||
|
last_cnt: u32,
|
||||||
|
listening: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn enable_tim_clk(syscfg: &mut SYSCONFIG, idx: u8) {
|
fn enable_tim_clk(syscfg: &mut SYSCONFIG, idx: u8) {
|
||||||
syscfg
|
syscfg
|
||||||
.tim_clk_enable
|
.tim_clk_enable
|
||||||
|
Reference in New Issue
Block a user