Skip to content

Commit f17fb43

Browse files
committed
Add PWM driver
1 parent ba39078 commit f17fb43

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
## [Unreleased]
99

1010
- Refactored `e310x-hal::spi` module, splitting the abstraction into `SpiBus` and `SpiExclusiveDevice/SpiSharedDevice` to allow multiple devices on a single SPI bus to co-exist
11+
- Added Pulse Width Modulation interface implementing `embedded_hal::Pwm`
1112

1213
## [v0.9.4] - 2022-07-10
1314

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod device;
1515
pub mod gpio;
1616
pub mod pmu;
1717
pub mod prelude;
18+
pub mod pwm;
1819
pub mod rtc;
1920
pub mod serial;
2021
pub mod spi;

src/pwm.rs

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
//! # Pulse Width Modulation Interface
2+
//!
3+
//! You can use the `PWM` with these [Pwm] instances
4+
//!
5+
//! # PWM0 - 8 bit period and duty
6+
//! - Channel 1: Pin 9 IOF1
7+
//! - Channel 2: Pin 10 IOF1
8+
//! - Channel 3: Pin 11 IOF1
9+
//!
10+
//! # PWM1 - 16 bit period and duty
11+
//! - Channel 1: Pin 3 IOF1
12+
//! - Channel 2: Pin 5 IOF1
13+
//! - Channel 3: Pin 6 IOF1
14+
//!
15+
//! # PWM2 - 16 bit period and duty
16+
//! - Channel 1: Pin 17 IOF1
17+
//! - Channel 2: Pin 18 IOF1
18+
//! - Channel 3: Pin 19 IOF1
19+
20+
use core::marker::PhantomData;
21+
use core::ops::Deref;
22+
23+
use e310x::{pwm0, PWM0, PWM1, PWM2};
24+
25+
/// PWM comparator index
26+
#[derive(Copy, Clone)]
27+
pub enum CmpIndex {
28+
/// PWM comparator 1
29+
Cmp1,
30+
/// PWM comparator 1
31+
Cmp2,
32+
/// PWM comparator 1
33+
Cmp3,
34+
}
35+
36+
/// PWM pin - DO NOT IMPLEMENT THIS TRAIT
37+
pub trait Pin<PWM> {
38+
#[doc(hidden)]
39+
const CMP_INDEX: CmpIndex;
40+
}
41+
42+
mod pwm0_impl {
43+
use super::{CmpIndex, Pin, PWM0};
44+
use crate::gpio::{gpio0, NoInvert, IOF1};
45+
46+
impl Pin<PWM0> for gpio0::Pin1<IOF1<NoInvert>> {
47+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp1;
48+
}
49+
50+
impl Pin<PWM0> for gpio0::Pin2<IOF1<NoInvert>> {
51+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp2;
52+
}
53+
54+
impl Pin<PWM0> for gpio0::Pin3<IOF1<NoInvert>> {
55+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp3;
56+
}
57+
}
58+
59+
mod pwm1_impl {
60+
use super::{CmpIndex, Pin, PWM1};
61+
use crate::gpio::{gpio0, NoInvert, IOF1};
62+
63+
impl Pin<PWM1> for gpio0::Pin19<IOF1<NoInvert>> {
64+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp1;
65+
}
66+
67+
impl Pin<PWM1> for gpio0::Pin21<IOF1<NoInvert>> {
68+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp2;
69+
}
70+
71+
impl Pin<PWM1> for gpio0::Pin22<IOF1<NoInvert>> {
72+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp3;
73+
}
74+
}
75+
76+
mod pwm2_impl {
77+
use super::{CmpIndex, Pin, PWM2};
78+
use crate::gpio::{gpio0, NoInvert, IOF1};
79+
80+
impl Pin<PWM2> for gpio0::Pin11<IOF1<NoInvert>> {
81+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp1;
82+
}
83+
84+
impl Pin<PWM2> for gpio0::Pin12<IOF1<NoInvert>> {
85+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp2;
86+
}
87+
88+
impl Pin<PWM2> for gpio0::Pin13<IOF1<NoInvert>> {
89+
const CMP_INDEX: CmpIndex = CmpIndex::Cmp3;
90+
}
91+
}
92+
93+
/// PWM channel
94+
pub struct Channel<PWM> {
95+
_pwm: PhantomData<PWM>,
96+
cmp_index: CmpIndex,
97+
}
98+
99+
impl<PWM> Channel<PWM> {
100+
/// Constructs a PWM channel from a PWM pin for use with [Pwm]
101+
pub fn from<PIN>(_: PIN) -> Channel<PWM>
102+
where
103+
PIN: Pin<PWM>,
104+
{
105+
Channel {
106+
_pwm: PhantomData,
107+
cmp_index: PIN::CMP_INDEX,
108+
}
109+
}
110+
}
111+
112+
impl<PWM> Clone for Channel<PWM> {
113+
fn clone(&self) -> Self {
114+
Self {
115+
_pwm: self._pwm.clone(),
116+
cmp_index: self.cmp_index.clone(),
117+
}
118+
}
119+
}
120+
121+
impl<PWM> Copy for Channel<PWM> {}
122+
123+
#[doc(hidden)]
124+
pub trait PwmX: Deref<Target = pwm0::RegisterBlock> {
125+
type CmpWidth: Ord;
126+
fn bits_from_cmp_width(other: Self::CmpWidth) -> u32;
127+
fn bits_into_cmp_width(other: u32) -> Self::CmpWidth;
128+
}
129+
130+
macro_rules! pwmx_impl {
131+
($PWM:ident,$CMP_WIDTH:ident) => {
132+
impl PwmX for $PWM {
133+
type CmpWidth = $CMP_WIDTH;
134+
fn bits_from_cmp_width(other: Self::CmpWidth) -> u32 {
135+
other as u32
136+
}
137+
fn bits_into_cmp_width(other: u32) -> Self::CmpWidth {
138+
other as Self::CmpWidth
139+
}
140+
}
141+
};
142+
}
143+
144+
pwmx_impl!(PWM0, u8);
145+
pwmx_impl!(PWM1, u16);
146+
pwmx_impl!(PWM2, u16);
147+
148+
/// PWM abstraction
149+
///
150+
/// # Notes
151+
///
152+
/// [PWM0] has a max period of 255, as it only has an 8 bit comparison register,
153+
/// the rest of them have a max value of 2^16 as they have 16 bit registers.
154+
pub struct Pwm<PWM> {
155+
pwm: PWM,
156+
}
157+
158+
impl<PWM: PwmX> Pwm<PWM> {
159+
/// Configures a PWM device
160+
pub fn new(pwm: PWM) -> Self {
161+
pwm.cfg.reset();
162+
pwm.cfg.write(|w| {
163+
w.zerocmp()
164+
.set_bit()
165+
.enalways()
166+
.set_bit()
167+
.deglitch()
168+
.set_bit()
169+
});
170+
pwm.cmp0.reset();
171+
pwm.cmp1.reset();
172+
pwm.cmp2.reset();
173+
pwm.cmp3.reset();
174+
Self { pwm }
175+
}
176+
}
177+
178+
impl<PWM: PwmX> embedded_hal::Pwm for Pwm<PWM> {
179+
type Channel = Channel<PWM>;
180+
181+
type Time = PWM::CmpWidth;
182+
183+
type Duty = PWM::CmpWidth;
184+
185+
fn enable(&mut self, channel: Self::Channel) {
186+
match channel.cmp_index {
187+
CmpIndex::Cmp1 => self.pwm.cmp1.write(|w| unsafe { w.bits(u32::MAX) }),
188+
CmpIndex::Cmp2 => self.pwm.cmp2.write(|w| unsafe { w.bits(u32::MAX) }),
189+
CmpIndex::Cmp3 => self.pwm.cmp3.write(|w| unsafe { w.bits(u32::MAX) }),
190+
}
191+
}
192+
193+
fn disable(&mut self, channel: Self::Channel) {
194+
match channel.cmp_index {
195+
CmpIndex::Cmp1 => self.pwm.cmp1.reset(),
196+
CmpIndex::Cmp2 => self.pwm.cmp2.reset(),
197+
CmpIndex::Cmp3 => self.pwm.cmp3.reset(),
198+
}
199+
}
200+
201+
fn get_period(&self) -> Self::Time {
202+
PWM::bits_into_cmp_width(self.pwm.cmp0.read().bits())
203+
}
204+
205+
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
206+
let duty = match channel.cmp_index {
207+
CmpIndex::Cmp1 => self.pwm.cmp1.read().bits(),
208+
CmpIndex::Cmp2 => self.pwm.cmp2.read().bits(),
209+
CmpIndex::Cmp3 => self.pwm.cmp3.read().bits(),
210+
};
211+
PWM::bits_into_cmp_width(duty)
212+
}
213+
214+
fn get_max_duty(&self) -> Self::Duty {
215+
self.get_period()
216+
}
217+
218+
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
219+
let duty = PWM::bits_from_cmp_width(duty.min(self.get_max_duty()));
220+
match channel.cmp_index {
221+
CmpIndex::Cmp1 => self.pwm.cmp1.write(|w| unsafe { w.bits(duty) }),
222+
CmpIndex::Cmp2 => self.pwm.cmp2.write(|w| unsafe { w.bits(duty) }),
223+
CmpIndex::Cmp3 => self.pwm.cmp3.write(|w| unsafe { w.bits(duty) }),
224+
}
225+
}
226+
227+
fn set_period<P>(&mut self, period: P)
228+
where
229+
P: Into<Self::Time>,
230+
{
231+
let period = PWM::bits_from_cmp_width(period.into());
232+
self.pwm.count.reset();
233+
self.pwm.cmp0.write(|w| unsafe { w.bits(period) });
234+
}
235+
}

0 commit comments

Comments
 (0)