Skip to main content

vorago_shared_hal/
pwm.rs

1use core::convert::Infallible;
2use core::marker::PhantomData;
3
4use crate::gpio::{DynPinId, IoPeriphPin};
5use crate::timer::enable_tim_clk;
6use crate::timer::regs::{EnableControl, StatusSelect};
7use crate::{FunctionSelect, PeripheralSelect, enable_peripheral_clock};
8
9use crate::time::Hertz;
10use crate::timer::{self, TimId};
11use crate::timer::{
12    Tim0Instance, Tim0Pin, Tim1Instance, Tim1Pin, Tim2Instance, Tim2Pin, Tim3Instance, Tim3Pin,
13    Tim4Instance, Tim4Pin, Tim5Instance, Tim5Pin, Tim6Instance, Tim6Pin, Tim7Instance, Tim7Pin,
14    Tim8Instance, Tim8Pin, Tim9Instance, Tim9Pin, Tim10Instance, Tim10Pin, Tim11Instance, Tim11Pin,
15    Tim12Instance, Tim12Pin, Tim13Instance, Tim13Pin, Tim14Instance, Tim14Pin, Tim15Instance,
16    Tim15Pin, Tim16Instance, Tim16Pin, Tim17Instance, Tim17Pin, Tim18Instance, Tim18Pin,
17    Tim19Instance, Tim19Pin, Tim20Instance, Tim20Pin, Tim21Instance, Tim21Pin, Tim22Instance,
18    Tim22Pin, Tim23Instance, Tim23Pin,
19};
20
21const DUTY_MAX: u16 = u16::MAX;
22
23#[derive(Debug)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub enum PwmA {}
26#[derive(Debug)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub enum PwmB {}
29
30//==================================================================================================
31// PWM pin
32//==================================================================================================
33
34/// Reduced version where type information is deleted
35pub struct PwmPin<Mode = PwmA> {
36    tim_id: TimId,
37    regs: timer::regs::MmioTimer<'static>,
38    ref_clk: Hertz,
39    /// For PWMB, this is the upper limit
40    current_duty: u16,
41    /// For PWMA, this value will not be used
42    current_lower_limit: u16,
43    current_period: Hertz,
44    current_rst_val: u32,
45    mode: PhantomData<Mode>,
46}
47
48macro_rules! impl_pwm_new_methods {
49    (
50        $(
51            ($tim_name:ident, $pin_trait:ident, $instance_trait:ident)
52        ),*
53        $(,)?
54    ) => {
55        paste::paste! {
56            $(
57                #[doc = concat!("Create a new PWM pin using ", stringify!($tim_name))]
58                pub fn [<new_with_ $tim_name:lower>] <Pin: $pin_trait, Tim: $instance_trait>(
59                    _pin: Pin,
60                    _tim: Tim,
61                    #[cfg(feature = "vor1x")] sys_clk: Hertz,
62                    #[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
63                    initial_frequency: Hertz,
64                ) -> Self {
65                    Self::new(
66                        Pin::PIN_ID,
67                        Pin::FUNC_SEL,
68                        Tim::ID,
69                        #[cfg(feature = "vor1x")]
70                        sys_clk,
71                        #[cfg(feature = "vor4x")]
72                        clks,
73                        initial_frequency,
74                    )
75                }
76            )*
77        }
78    };
79}
80
81impl<Mode> PwmPin<Mode> {
82    impl_pwm_new_methods! {
83        (Tim0,  Tim0Pin,  Tim0Instance),
84        (Tim1,  Tim1Pin,  Tim1Instance),
85        (Tim2,  Tim2Pin,  Tim2Instance),
86        (Tim3,  Tim3Pin,  Tim3Instance),
87        (Tim4,  Tim4Pin,  Tim4Instance),
88        (Tim5,  Tim5Pin,  Tim5Instance),
89        (Tim6,  Tim6Pin,  Tim6Instance),
90        (Tim7,  Tim7Pin,  Tim7Instance),
91        (Tim8,  Tim8Pin,  Tim8Instance),
92        (Tim9,  Tim9Pin,  Tim9Instance),
93        (Tim10, Tim10Pin, Tim10Instance),
94        (Tim11, Tim11Pin, Tim11Instance),
95        (Tim12, Tim12Pin, Tim12Instance),
96        (Tim13, Tim13Pin, Tim13Instance),
97        (Tim14, Tim14Pin, Tim14Instance),
98        (Tim15, Tim15Pin, Tim15Instance),
99        (Tim16, Tim16Pin, Tim16Instance),
100        (Tim17, Tim17Pin, Tim17Instance),
101        (Tim18, Tim18Pin, Tim18Instance),
102        (Tim19, Tim19Pin, Tim19Instance),
103        (Tim20, Tim20Pin, Tim20Instance),
104        (Tim21, Tim21Pin, Tim21Instance),
105        (Tim22, Tim22Pin, Tim22Instance),
106        (Tim23, Tim23Pin, Tim23Instance),
107    }
108
109    fn new(
110        pin_id: DynPinId,
111        func_sel: FunctionSelect,
112        tim_id: TimId,
113        #[cfg(feature = "vor1x")] sys_clk: Hertz,
114        #[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
115        initial_frequency: Hertz,
116    ) -> Self {
117        IoPeriphPin::new(pin_id, func_sel, None);
118        let mut pin = PwmPin {
119            tim_id,
120            regs: timer::regs::Timer::new_mmio(tim_id),
121            current_duty: 0,
122            current_lower_limit: 0,
123            current_period: initial_frequency,
124            current_rst_val: 0,
125            #[cfg(feature = "vor1x")]
126            ref_clk: sys_clk,
127            #[cfg(feature = "vor4x")]
128            ref_clk: clks.apb1(),
129            mode: PhantomData,
130        };
131        // For Vorago 4x, the presence of the pin structure ensures that its respective peripheral
132        // clock was already enabled.
133        #[cfg(feature = "vor1x")]
134        enable_peripheral_clock(PeripheralSelect::Gpio);
135        enable_peripheral_clock(PeripheralSelect::IoConfig);
136        enable_tim_clk(tim_id);
137        pin.enable_pwm_a();
138        pin.set_period(initial_frequency);
139        pin
140    }
141
142    #[inline]
143    fn enable_pwm_a(&mut self) {
144        self.regs.modify_control(|mut value| {
145            value.set_status_sel(StatusSelect::PwmaOutput);
146            value
147        });
148    }
149
150    #[inline]
151    fn enable_pwm_b(&mut self) {
152        self.regs.modify_control(|mut value| {
153            value.set_status_sel(StatusSelect::PwmbOutput);
154            value
155        });
156    }
157
158    #[inline]
159    pub fn get_period(&self) -> Hertz {
160        self.current_period
161    }
162
163    #[inline]
164    pub fn set_period(&mut self, period: impl Into<Hertz>) {
165        self.current_period = period.into();
166        // Avoid division by 0
167        if self.current_period.to_raw() == 0 {
168            return;
169        }
170        self.current_rst_val = self.ref_clk.to_raw() / self.current_period.to_raw();
171        self.regs.write_reset_value(self.current_rst_val);
172    }
173
174    #[inline]
175    pub fn disable(&mut self) {
176        self.regs.write_enable_control(EnableControl::new_disable());
177    }
178
179    #[inline]
180    pub fn enable(&mut self) {
181        self.regs.write_enable_control(EnableControl::new_enable());
182    }
183
184    #[inline]
185    pub fn period(&self) -> Hertz {
186        self.current_period
187    }
188
189    #[inline(always)]
190    pub fn duty(&self) -> u16 {
191        self.current_duty
192    }
193}
194
195impl From<PwmPin<PwmA>> for PwmPin<PwmB> {
196    fn from(other: PwmPin<PwmA>) -> Self {
197        let mut pwmb = Self {
198            mode: PhantomData,
199            regs: other.regs,
200            tim_id: other.tim_id,
201            ref_clk: other.ref_clk,
202            current_duty: other.current_duty,
203            current_lower_limit: other.current_lower_limit,
204            current_period: other.current_period,
205            current_rst_val: other.current_rst_val,
206        };
207        pwmb.enable_pwm_b();
208        pwmb
209    }
210}
211
212impl From<PwmPin<PwmB>> for PwmPin<PwmA> {
213    fn from(other: PwmPin<PwmB>) -> Self {
214        let mut pwmb = Self {
215            mode: PhantomData,
216            tim_id: other.tim_id,
217            regs: other.regs,
218            ref_clk: other.ref_clk,
219            current_duty: other.current_duty,
220            current_lower_limit: other.current_lower_limit,
221            current_period: other.current_period,
222            current_rst_val: other.current_rst_val,
223        };
224        pwmb.enable_pwm_a();
225        pwmb
226    }
227}
228
229//==================================================================================================
230// PWMB implementations
231//==================================================================================================
232
233impl PwmPin<PwmB> {
234    #[inline(always)]
235    pub fn pwmb_lower_limit(&self) -> u16 {
236        self.current_lower_limit
237    }
238
239    #[inline(always)]
240    pub fn pwmb_upper_limit(&self) -> u16 {
241        self.current_duty
242    }
243
244    /// Set the lower limit for PWMB
245    ///
246    /// The PWM signal will be 1 as long as the current RST counter is larger than
247    /// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
248    /// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
249    /// state
250    #[inline(always)]
251    pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
252        self.current_lower_limit = duty;
253        let pwmb_val: u64 =
254            (self.current_rst_val as u64 * self.current_lower_limit as u64) / DUTY_MAX as u64;
255        self.regs.write_pwmb_value(pwmb_val as u32);
256    }
257
258    /// Set the higher limit for PWMB
259    ///
260    /// The PWM signal will be 1 as long as the current RST counter is smaller than
261    /// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
262    /// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
263    /// state
264    pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
265        self.current_duty = duty;
266        let pwma_val: u64 =
267            (self.current_rst_val as u64 * self.current_duty as u64) / DUTY_MAX as u64;
268        self.regs.write_pwma_value(pwma_val as u32);
269    }
270}
271
272//==================================================================================================
273// Embedded HAL implementation: PWMA only
274//==================================================================================================
275
276impl embedded_hal::pwm::ErrorType for PwmPin {
277    type Error = Infallible;
278}
279
280impl embedded_hal::pwm::SetDutyCycle for PwmPin {
281    #[inline]
282    fn max_duty_cycle(&self) -> u16 {
283        DUTY_MAX
284    }
285
286    #[inline]
287    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
288        self.current_duty = duty;
289        let pwma_val: u64 = (self.current_rst_val as u64
290            * (DUTY_MAX as u64 - self.current_duty as u64))
291            / DUTY_MAX as u64;
292        self.regs.write_pwma_value(pwma_val as u32);
293        Ok(())
294    }
295}
296
297/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0.
298///
299/// Please note that this might load a lot of floating point code because this processor does not
300/// have a FPU
301pub fn get_duty_from_percent(percent: f32) -> u16 {
302    if percent > 1.0 {
303        DUTY_MAX
304    } else if percent <= 0.0 {
305        0
306    } else {
307        (percent * DUTY_MAX as f32) as u16
308    }
309}