Skip to content

Commit

Permalink
PWM example
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Oct 19, 2024
1 parent 5216882 commit a6bd57b
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 92 deletions.
1 change: 1 addition & 0 deletions e310x-hal/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use embedded_hal::{
delay::DelayNs,
digital::{InputPin, OutputPin, StatefulOutputPin},
i2c::I2c,
pwm::SetDutyCycle,
spi::{SpiBus, SpiDevice},
};

Expand Down
208 changes: 116 additions & 92 deletions e310x-hal/src/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
//! You can use the `PWM` with these [Pwm] instances
//!
//! # PWM0 - 8 bit period and duty
//! - Channel 1: Pin 9 IOF1
//! - Channel 2: Pin 10 IOF1
//! - Channel 3: Pin 11 IOF1
//! - Channel 1: Pin 1 IOF1
//! - Channel 2: Pin 2 IOF1
//! - Channel 3: Pin 3 IOF1
//!
//! # PWM1 - 16 bit period and duty
//! - Channel 1: Pin 3 IOF1
//! - Channel 2: Pin 5 IOF1
//! - Channel 3: Pin 6 IOF1
//! - Channel 1: Pin 19 IOF1
//! - Channel 2: Pin 21 IOF1
//! - Channel 3: Pin 22 IOF1
//!
//! # PWM2 - 16 bit period and duty
//! - Channel 1: Pin 17 IOF1
//! - Channel 2: Pin 18 IOF1
//! - Channel 3: Pin 19 IOF1

use core::{marker::PhantomData, ops::Deref};
//! - Channel 1: Pin 11 IOF1
//! - Channel 2: Pin 12 IOF1
//! - Channel 3: Pin 13 IOF1

use core::ops::Deref;
use e310x::{pwm0, Pwm0, Pwm1, Pwm2};
use embedded_hal::pwm::{ErrorKind, ErrorType, SetDutyCycle};

/// PWM comparator index
#[derive(Copy, Clone)]
Expand All @@ -33,71 +33,53 @@ pub enum CmpIndex {
}

/// PWM pin
pub trait Pin<PWM>: private::Sealed {
pub trait Pin<PWM: PwmX>: private::Sealed {
/// Channel index associated with the pin
const CMP_INDEX: CmpIndex;
}

mod pwm_impl {
use super::{CmpIndex, Pin, Pwm0, Pwm1, Pwm2};
use crate::gpio::{gpio0, NoInvert, IOF1};
use crate::gpio::{gpio0, Invert, IOF1};

// PWM0
impl Pin<Pwm0> for gpio0::Pin1<IOF1<NoInvert>> {
impl Pin<Pwm0> for gpio0::Pin1<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp1;
}
impl Pin<Pwm0> for gpio0::Pin2<IOF1<NoInvert>> {
impl Pin<Pwm0> for gpio0::Pin2<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp2;
}
impl Pin<Pwm0> for gpio0::Pin3<IOF1<NoInvert>> {
impl Pin<Pwm0> for gpio0::Pin3<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp3;
}

// PWM1
impl Pin<Pwm1> for gpio0::Pin19<IOF1<NoInvert>> {
impl Pin<Pwm1> for gpio0::Pin19<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp1;
}
impl Pin<Pwm1> for gpio0::Pin21<IOF1<NoInvert>> {
impl Pin<Pwm1> for gpio0::Pin21<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp2;
}
impl Pin<Pwm1> for gpio0::Pin22<IOF1<NoInvert>> {
impl Pin<Pwm1> for gpio0::Pin22<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp3;
}

// PWM2
impl Pin<Pwm2> for gpio0::Pin11<IOF1<NoInvert>> {
impl Pin<Pwm2> for gpio0::Pin11<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp1;
}
impl Pin<Pwm2> for gpio0::Pin12<IOF1<NoInvert>> {
impl Pin<Pwm2> for gpio0::Pin12<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp2;
}
impl Pin<Pwm2> for gpio0::Pin13<IOF1<NoInvert>> {
impl Pin<Pwm2> for gpio0::Pin13<IOF1<Invert>> {
const CMP_INDEX: CmpIndex = CmpIndex::Cmp3;
}
}

/// PWM channel
#[derive(Copy, Clone)]
pub struct Channel<PWM> {
_pwm: PhantomData<PWM>,
cmp_index: CmpIndex,
}

impl<PWM> Channel<PWM> {
/// Constructs a PWM channel from a PWM pin for use with [Pwm]
pub fn from<PIN: Pin<PWM>>(_: PIN) -> Channel<PWM> {
Channel {
_pwm: PhantomData,
cmp_index: PIN::CMP_INDEX,
}
}
}

/// PwmX trait extends the PWM peripherals
#[doc(hidden)]
pub trait PwmX: Deref<Target = pwm0::RegisterBlock> + private::Sealed {
type CmpWidth: Ord;
fn peripheral() -> Self;
fn bits_from_cmp_width(other: Self::CmpWidth) -> u32;
fn bits_into_cmp_width(other: u32) -> Self::CmpWidth;
}
Expand All @@ -106,9 +88,6 @@ macro_rules! pwmx_impl {
($PWM:ident,$CMP_WIDTH:ident) => {
impl PwmX for $PWM {
type CmpWidth = $CMP_WIDTH;
fn peripheral() -> Self {
unsafe { Self::steal() }
}
fn bits_from_cmp_width(other: Self::CmpWidth) -> u32 {
other as u32
}
Expand Down Expand Up @@ -152,103 +131,148 @@ impl<PWM: PwmX> Pwm<PWM> {
Self { pwm }
}

/// Frees the PWM device
pub fn free(self) -> PWM {
self.pwm
}

/// Returns the period of the PWM
pub fn get_period(&self) -> PWM::CmpWidth {
PWM::bits_into_cmp_width(self.pwm.cmp0().read().bits())
}

/// Sets the period of the PWM
pub fn set_period(&mut self, period: PWM::CmpWidth) {
let period = PWM::bits_from_cmp_width(period);
self.pwm.count().reset();
self.pwm.cmp0().write(|w| unsafe { w.bits(period) });
}

/// Returns the duty cycle of the PWM
fn get_duty(&self, cmp_index: CmpIndex) -> PWM::CmpWidth {
let duty = match cmp_index {
CmpIndex::Cmp1 => self.pwm.cmp1().read().bits(),
CmpIndex::Cmp2 => self.pwm.cmp2().read().bits(),
CmpIndex::Cmp3 => self.pwm.cmp3().read().bits(),
};
PWM::bits_into_cmp_width(duty)
}

/// Sets the duty cycle of the PWM
fn set_duty(&self, cmp_index: CmpIndex, duty: PWM::CmpWidth) {
let duty = PWM::bits_from_cmp_width(duty.min(self.get_period()));
match cmp_index {
CmpIndex::Cmp1 => self.pwm.cmp1().write(|w| unsafe { w.bits(duty) }),
CmpIndex::Cmp2 => self.pwm.cmp2().write(|w| unsafe { w.bits(duty) }),
CmpIndex::Cmp3 => self.pwm.cmp3().write(|w| unsafe { w.bits(duty) }),
}
}

/// Enables the PWM channel
pub fn enable(&mut self, channel: &Channel<PWM>) {
match channel.cmp_index {
fn enable(&self, cmp_index: CmpIndex) {
match cmp_index {
CmpIndex::Cmp1 => self.pwm.cmp1().write(|w| unsafe { w.bits(u32::MAX) }),
CmpIndex::Cmp2 => self.pwm.cmp2().write(|w| unsafe { w.bits(u32::MAX) }),
CmpIndex::Cmp3 => self.pwm.cmp3().write(|w| unsafe { w.bits(u32::MAX) }),
}
}

/// Disables the PWM channel
pub fn disable(&mut self, channel: &Channel<PWM>) {
match channel.cmp_index {
fn disable(&self, cmp_index: CmpIndex) {
match cmp_index {
CmpIndex::Cmp1 => self.pwm.cmp1().reset(),
CmpIndex::Cmp2 => self.pwm.cmp2().reset(),
CmpIndex::Cmp3 => self.pwm.cmp3().reset(),
}
}

/// Returns the PWM channel associated with the given index
pub fn channel<PIN: Pin<PWM>>(&self, pin: PIN) -> Channel<'_, PWM, PIN> {
Channel { pwm: self, pin }
}
}

/// PWM channel
pub struct Channel<'a, PWM, PIN> {
pwm: &'a Pwm<PWM>,
pin: PIN,
}

impl<'a, PWM, PIN> Channel<'a, PWM, PIN> {
/// Frees the PWM channel
pub fn free(self) -> PIN {
self.pin
}
}

impl<'a, PWM: PwmX, PIN: Pin<PWM>> Channel<'a, PWM, PIN> {
/// Returns the period of the PWM
pub fn get_period(&self) -> PWM::CmpWidth {
PWM::bits_into_cmp_width(self.pwm.cmp0().read().bits())
self.pwm.get_period()
}

/// Returns the maximum duty cycle of the PWM (equal to the period)
pub fn max_duty(&self) -> PWM::CmpWidth {
self.pwm.get_period()
}

/// Returns the duty cycle of the PWM
pub fn get_duty(&self, channel: &Channel<PWM>) -> PWM::CmpWidth {
let duty = match channel.cmp_index {
CmpIndex::Cmp1 => self.pwm.cmp1().read().bits(),
CmpIndex::Cmp2 => self.pwm.cmp2().read().bits(),
CmpIndex::Cmp3 => self.pwm.cmp3().read().bits(),
};
PWM::bits_into_cmp_width(duty)
pub fn get_duty(&self) -> PWM::CmpWidth {
self.pwm.get_duty(PIN::CMP_INDEX)
}

/// Returns the maximum duty cycle of the PWM (equal to the period)
pub fn get_max_duty(&self) -> PWM::CmpWidth {
self.get_period()
/// Sets the duty cycle to 100%
pub fn enable(&mut self) {
self.pwm.enable(PIN::CMP_INDEX);
}

/// Sets the duty cycle of the PWM
pub fn set_duty(&mut self, channel: &Channel<PWM>, duty: PWM::CmpWidth) {
let duty = PWM::bits_from_cmp_width(duty.min(self.get_max_duty()));
match channel.cmp_index {
CmpIndex::Cmp1 => self.pwm.cmp1().write(|w| unsafe { w.bits(duty) }),
CmpIndex::Cmp2 => self.pwm.cmp2().write(|w| unsafe { w.bits(duty) }),
CmpIndex::Cmp3 => self.pwm.cmp3().write(|w| unsafe { w.bits(duty) }),
}
/// Sets the duty cycle to 0%
pub fn disable(&mut self) {
self.pwm.disable(PIN::CMP_INDEX);
}

/// Sets the period of the PWM
pub fn set_period(&mut self, period: PWM::CmpWidth) {
let period = PWM::bits_from_cmp_width(period);
self.pwm.count().reset();
self.pwm.cmp0().write(|w| unsafe { w.bits(period) });
/// Sets the duty cycle of the PWM
pub fn set_duty(&mut self, duty: PWM::CmpWidth) {
self.pwm.set_duty(PIN::CMP_INDEX, duty);
}
}

impl<PWM: PwmX> embedded_hal::pwm::ErrorType for Channel<PWM> {
type Error = embedded_hal::pwm::ErrorKind;
impl<'a, PWM: PwmX, PIN: Pin<PWM>> ErrorType for Channel<'a, PWM, PIN> {
type Error = ErrorKind;
}

impl<PWM: PwmX> embedded_hal::pwm::SetDutyCycle for Channel<PWM> {
impl<'a, PWM: PwmX, PIN: Pin<PWM>> SetDutyCycle for Channel<'a, PWM, PIN> {
fn max_duty_cycle(&self) -> u16 {
let pwm: Pwm<PWM> = Pwm {
pwm: PWM::peripheral(),
};
PWM::bits_from_cmp_width(pwm.get_max_duty()) as _
PWM::bits_from_cmp_width(self.max_duty()) as _
}

fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
let mut pwm: Pwm<PWM> = Pwm {
pwm: PWM::peripheral(),
};
pwm.set_duty(self, PWM::bits_into_cmp_width(duty as _));
self.pwm
.set_duty(PIN::CMP_INDEX, PWM::bits_into_cmp_width(duty as _));
Ok(())
}
}

mod private {
use super::{Pwm0, Pwm1, Pwm2};
use crate::gpio::{gpio0, NoInvert, IOF1};
use crate::gpio::{gpio0, Invert, IOF1};
pub trait Sealed {}

// PWM0
impl Sealed for Pwm0 {}
impl Sealed for gpio0::Pin1<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin2<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin3<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin1<IOF1<Invert>> {}
impl Sealed for gpio0::Pin2<IOF1<Invert>> {}
impl Sealed for gpio0::Pin3<IOF1<Invert>> {}

// PWM1
impl Sealed for Pwm1 {}
impl Sealed for gpio0::Pin19<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin21<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin22<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin19<IOF1<Invert>> {}
impl Sealed for gpio0::Pin21<IOF1<Invert>> {}
impl Sealed for gpio0::Pin22<IOF1<Invert>> {}

// PWM2
impl Sealed for Pwm2 {}
impl Sealed for gpio0::Pin11<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin12<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin13<IOF1<NoInvert>> {}
impl Sealed for gpio0::Pin11<IOF1<Invert>> {}
impl Sealed for gpio0::Pin12<IOF1<Invert>> {}
impl Sealed for gpio0::Pin13<IOF1<Invert>> {}
}
5 changes: 5 additions & 0 deletions hifive1-examples/examples/button_poll.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//! Example of polling a button and turning on a LED when the button is pressed.
//!
//! # Hardware
//!
//! - HiFive1 or RED-V board
//! - A button connected to pin 9

#![no_std]
#![no_main]
Expand Down
Loading

0 comments on commit a6bd57b

Please sign in to comment.