1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
//! PWM Implementation
use core::marker::PhantomData;
use embedded_hal::pwm;
use embedded_hal::pwm::{ErrorKind, ErrorType, SetDutyCycle};
use crate::port::mode;
use crate::port::Pin;
/// Clock prescaler for PWM
///
/// The prescaler dictates the PWM frequency, together with the IO clock. The formula is as
/// follows:
///
/// ```text
/// F_pwm = CLK_io / (Prescaler * 256);
/// ```
///
/// | Prescaler | 16 MHz Clock | 8 MHz Clock |
/// | --- | --- | ---|
/// | `Direct` | 62.5 kHz | 31.3 kHz |
/// | `Prescale8` | 7.81 kHz | 3.91 kHz |
/// | `Prescale64` | 977 Hz | 488 Hz |
/// | `Prescale256` | 244 Hz | 122 Hz |
/// | `Prescale1024` | 61.0 Hz | 30.5 Hz |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Prescaler {
/// No prescaling, the IO clock drives the timer directly.
Direct,
/// Divide the IO clock by 8.
Prescale8,
/// Divide the IO clock by 64.
Prescale64,
/// Divide the IO clock by 256.
Prescale256,
/// Divide the IO clock by 1024.
Prescale1024,
}
/// Implement traits and types for PWM timers
pub trait PwmPinOps<TC> {
type Duty;
fn enable(&mut self);
fn disable(&mut self);
fn get_duty(&self) -> Self::Duty;
fn get_max_duty(&self) -> Self::Duty;
fn set_duty(&mut self, value: u8);
}
pub trait IntoPwmPin<TC, PIN> {
fn into_pwm(self, timer: &TC) -> Pin<mode::PwmOutput<TC>, PIN>;
}
impl<TC, PIN: PwmPinOps<TC>> IntoPwmPin<TC, PIN> for Pin<mode::Output, PIN> {
fn into_pwm(self, _timer: &TC) -> Pin<mode::PwmOutput<TC>, PIN> {
Pin {
pin: self.pin,
_mode: PhantomData,
}
}
}
impl<TC, PIN: PwmPinOps<TC>> Pin<mode::PwmOutput<TC>, PIN> {
pub fn enable(&mut self) {
self.pin.enable();
}
pub fn disable(&mut self) {
self.pin.disable();
}
pub fn get_duty(&self) -> <PIN as PwmPinOps<TC>>::Duty {
self.pin.get_duty()
}
pub fn get_max_duty(&self) -> <PIN as PwmPinOps<TC>>::Duty {
self.pin.get_max_duty()
}
pub fn set_duty(&mut self, duty: u8) {
self.pin.set_duty(duty);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum PwmError {
/// `embedded-hal` supports duty cycles up to `u16`, however `avr` devices only support up to `u8`.
/// Passing a duty cycle larger than [`u8::MAX`] will result in this error.
DutyCycleTooLarge,
}
impl pwm::Error for PwmError {
fn kind(&self) -> ErrorKind {
ErrorKind::Other
}
}
impl<TC, PIN: PwmPinOps<TC>> ErrorType for Pin<mode::PwmOutput<TC>, PIN> {
type Error = PwmError;
}
impl<TC, PIN: PwmPinOps<TC, Duty = u8>> SetDutyCycle for Pin<mode::PwmOutput<TC>, PIN> {
fn max_duty_cycle(&self) -> u16 {
self.get_max_duty() as u16
}
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
if duty > u8::MAX as u16 {
return Err(PwmError::DutyCycleTooLarge);
}
self.set_duty(duty as u8);
Ok(())
}
}
#[macro_export]
macro_rules! impl_simple_pwm {
(
$(#[$timer_pwm_attr:meta])*
pub struct $TimerPwm:ident {
timer: $TIMER:ty,
init: |$init_timer:ident, $prescaler:ident| $init_block:block,
pins: {$(
$PXi:ident: {
ocr: $ocr:ident,
$into_pwm:ident: |$pin_timer:ident| if enable
$pin_enable_block:block else $pin_disable_block:block,
},
)+},
}
) => {
$(#[$timer_pwm_attr])*
pub struct $TimerPwm {
timer: $TIMER,
}
impl $TimerPwm {
pub fn new(timer: $TIMER, prescaler: $crate::simple_pwm::Prescaler) -> $TimerPwm {
let mut t = $TimerPwm { timer };
{
let $init_timer = &mut t.timer;
let $prescaler = prescaler;
$init_block
}
t
}
}
$(
impl avr_hal_generic::simple_pwm::PwmPinOps<$TimerPwm> for $PXi {
type Duty = u8;
fn enable(&mut self) {
// SAFETY: This block will usually result in a read-modify-write sequence which
// is not concurrency safe. Thus, it is wrapped in a critical section which
// ensures we will never hit a race-condition here.
$crate::avr_device::interrupt::free(|_| {
let $pin_timer = unsafe { &*<$TIMER>::ptr() };
$pin_enable_block
});
}
fn disable(&mut self) {
// SAFETY: This block will usually result in a read-modify-write sequence which
// is not concurrency safe. Thus, it is wrapped in a critical section which
// ensures we will never hit a race-condition here.
$crate::avr_device::interrupt::free(|_| {
let $pin_timer = unsafe { &*<$TIMER>::ptr() };
$pin_disable_block
});
}
fn get_duty(&self) -> Self::Duty {
unsafe { (&*<$TIMER>::ptr()) }.$ocr.read().bits() as Self::Duty
}
fn get_max_duty(&self) -> Self::Duty {
u8::MAX
}
fn set_duty(&mut self, duty: Self::Duty) {
// SAFETY: This register is exclusively used here so there are no concurrency
// issues.
unsafe { (&*<$TIMER>::ptr()).$ocr.write(|w| w.bits(duty.into())); };
}
}
)+
}
}