Pwm improvements (#61)

* Reimplement PWM driver using typestates
* Improve docs
This commit is contained in:
Hmvp 2021-09-02 01:37:17 +02:00 committed by GitHub
parent 216aad2c76
commit ffa39f65f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 827 additions and 299 deletions

View file

@ -15,8 +15,9 @@ use pico::{
pwm::*,
watchdog::Watchdog,
},
XOSC_CRYSTAL_FREQ,
Pins, XOSC_CRYSTAL_FREQ,
};
use rp2040_hal::sio::Sio;
#[link_section = ".boot2"]
#[used]
@ -44,32 +45,37 @@ fn main() -> ! {
.ok()
.unwrap();
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
let mut pwm_pin = Pwm4::new(25);
//Instead of having it take references to all of these pac objects, eventually this should just
//take ownership of a GPIO pin.
pwm_pin.default_config(
&mut pac.PWM,
&mut pac.PADS_BANK0,
&mut pac.IO_BANK0,
let sio = Sio::new(pac.SIO);
let pins = Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
pwm_pin.set_ph_correct();
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
pwm_pin.enable();
// Init PWMs
let mut pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
// Configure PWM4
let pwm = &mut pwm_slices.pwm4;
pwm.set_ph_correct();
pwm.enable();
// Use B channel (which outputs to GPIO 25)
let channel = &mut pwm.channel_b;
channel.output_to(pins.led);
loop {
for i in (LOW..=HIGH).skip(100) {
delay.delay_us(8);
pwm_pin.set_duty(i);
channel.set_duty(i);
}
for i in (LOW..=HIGH).rev().skip(100) {
delay.delay_us(8);
pwm_pin.set_duty(i);
channel.set_duty(i);
}
delay.delay_ms(500);

View file

@ -4,7 +4,7 @@
use cortex_m_rt::entry;
use embedded_hal::PwmPin;
use panic_halt as _;
use rp2040_hal::pwm::*;
use rp2040_hal::{gpio::Pins, pwm::*, sio::Sio};
#[link_section = ".boot2"]
#[used]
@ -14,26 +14,30 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
fn main() -> ! {
let mut pac = rp2040_pac::Peripherals::take().unwrap();
let mut pwm_pin = Pwm4::new(25);
//Instead of having it take references to all of these pac objects, eventually this should just
//take ownership of a GPIO pin.
pwm_pin.default_config(
&mut pac.PWM,
&mut pac.PADS_BANK0,
&mut pac.IO_BANK0,
let sio = Sio::new(pac.SIO);
let pins = Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
pwm_pin.set_ph_correct();
// Init PWMs
let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
pwm_pin.enable();
// Configure PWM4
let mut pwm = pwm_slices.pwm4;
pwm.set_ph_correct();
pwm.enable();
// Use B channel (which outputs to GPIO 25)
let mut channel = pwm.channel_b;
channel.output_to(pins.gpio25);
loop {
pwm_pin.set_duty(15000);
channel.set_duty(15000);
// TODO: Replace with proper delays once we have clocks working
cortex_m::asm::delay(5_000_000);
pwm_pin.set_duty(30000);
channel.set_duty(30000);
cortex_m::asm::delay(5_000_000);
}
}

View file

@ -1,271 +0,0 @@
//! Pulse Width Modulation (PWM)
//!
//! To access the PWM pins you must call the 'split' method on the PWM. This will return a
//! `_____` struct with access to each PWM pin:
//!
//! ```no_run
//! use embedded_hal::PwmPin;
//! use rp2040_hal::{pac, pwm::Pwm0};
//! let mut peripherals = pac::Peripherals::take().unwrap();
//! let pin_num = 0;
//! let mut pwm_pin = Pwm0::new(pin_num);
//! ```
//!
//! Once you have the PWM pins struct, you can take individual pins and configure them:
//!
//! ```no_run
//! # use embedded_hal::PwmPin;
//! # use rp2040_hal::{pac, pwm::Pwm0};
//! # let mut peripherals = pac::Peripherals::take().unwrap();
//! # let pin_num = 0;
//! # let mut pwm_pin = Pwm0::new(pin_num);
//! pwm_pin.default_config(&mut peripherals.PWM, &mut peripherals.PADS_BANK0, &mut peripherals.IO_BANK0, &mut peripherals.RESETS);
//! pwm_pin.set_duty(32767);
//! pwm_pin.enable();
//! ```
//!
//! The following configuration options are also available:
//!
//! ```no_run
//! # use embedded_hal::PwmPin;
//! # use rp2040_hal::{pac, pwm::Pwm0};
//! # let mut peripherals = pac::Peripherals::take().unwrap();
//! # let pin_num = 0;
//! # let mut pwm_pin = Pwm0::new(pin_num);
//! pwm_pin.min_config(&mut peripherals.PWM, &mut peripherals.PADS_BANK0, &mut peripherals.IO_BANK0, &mut peripherals.RESETS);
//!
//! pwm_pin.get_duty();
//! pwm_pin.get_max_duty();
//!
//! pwm_pin.set_ph_correct(); // Run in phase correct mode
//! pwm_pin.clr_ph_correct(); // Don't run in phase correct mode
//!
//! pwm_pin.set_div_int(1u8); // To set integer part of clock divider
//! pwm_pin.set_div_frac(0u8); // To set fractional part of clock divider
//!
//! pwm_pin.set_inv(); // Invert the output
//! pwm_pin.clr_inv(); // Don't invert the output
//!
//! pwm_pin.set_top(u16::MAX); // To set the TOP register
//!
//! pwm_pin.divmode_div(); // Default divmode. Counts up at a rate dictated by div.
//! pwm_pin.divmode_level(); // These 3 divmodes can be used with a PWM B pin to read PWM inputs.
//! pwm_pin.divmode_rise();
//! pwm_pin.divmode_fall();
//! ```
//!
//! default_config() sets ph_correct to false, the clock divider to 1, does not invert the output, sets top to 65535, and resets the counter.
//! min_config() leaves those registers in the state they were before it was called (Careful, this can lead to unexpected behavior)
//! It's recommended to only call min_config() after calling default_config() on a pin that shares a PWM block.
use super::*;
use crate::resets::SubsystemReset;
use embedded_hal::PwmPin;
macro_rules! pwm {
($PWMX:ident, $pwmx:ident, [
$($PXi:ident: ($pxi:ident, $pwms:expr, $pins:expr, $i:expr),)+]) => {
$(
#[doc = "Struct for any of the "]
#[doc = $pwms]
#[doc = " pins"]
pub struct $PXi {
pin: usize
}
impl $PXi {
#[doc = "Constructor for a PWM pin struct"]
pub fn new (pin: usize) -> Self {
let mut pin_num : usize = 255;
for i in 0..$pins.len() {
if (pin == $pins[i]) {
pin_num = pin;
}
}
if (pin_num == 255) {
pin_num = $pins[0];
}
Self {
pin: pin_num,
}
}
// TODO: This function contains all the methods that required the PWM to have an instance of PADS_BANK0, RESETS, or IO_BANK0.
// Since the GPIO pins take ownership of these, after the GPIO refactor, this method should be moved into gpio.rs, and the PWM
// will instead receive a single gpio pin.
fn init_io(&self, pwm: &mut pac::$PWMX, pad : &mut pac::PADS_BANK0, io : &mut pac::IO_BANK0, resets: &mut pac::RESETS) -> () {
//TODO: Merge these into gpio.rs split function after GPIO refactor. At the moment, this is here because these need to be reset for
// the PWM to work. However, because they're here, they'll be reset every time a new PWM pin is created (BAD).
pwm.reset_bring_up(resets);
io.reset_bring_up(resets);
pad.gpio[self.pin].modify(|_, w| w.ie().set_bit());
pad.gpio[self.pin].modify(|_, w| w.od().clear_bit());
unsafe {
io.gpio[self.pin].gpio_ctrl.write_with_zero(|w| w.funcsel().pwm_a_0());
}
}
fn cc(&self) -> &pac::$pwmx::ch::CC {
unsafe {
&(*pac::$PWMX::ptr()).ch[$i].cc
}
}
fn csr(&self) -> &pac::$pwmx::ch::CSR {
unsafe {
&(*pac::$PWMX::ptr()).ch[$i].csr
}
}
fn ctr(&self) -> &pac::$pwmx::ch::CTR {
unsafe {
&(*pac::$PWMX::ptr()).ch[$i].ctr
}
}
fn div(&self) -> &pac::$pwmx::ch::DIV {
unsafe {
&(*pac::$PWMX::ptr()).ch[$i].div
}
}
fn top(&self) -> &pac::$pwmx::ch::TOP {
unsafe {
&(*pac::$PWMX::ptr()).ch[$i].top
}
}
#[doc = "Sets up a pin with the default configurations"]
pub fn default_config(&mut self, pwm: &mut pac::$PWMX, pad: &mut pac::PADS_BANK0, io: &mut pac::IO_BANK0, resets: &mut pac::RESETS) -> () {
self.init_io(pwm, pad, io, resets);
self.clr_ph_correct();
self.set_div_int(1);
self.set_div_frac(0);
self.divmode_div();
self.set_top(0xffffu16);
self.ctr().write(|w| unsafe { w.ctr().bits(0x0000u16) }); //Reset the counter
self.set_duty(0); //Default duty cycle of 0%
self.clr_inv(); //Don't invert the channel
}
#[doc = "Sets up a pin with minimum configurations"]
pub fn min_config(&mut self, pwm: &mut pac::$PWMX, pad: &mut pac::PADS_BANK0, io: &mut pac::IO_BANK0, resets: &mut pac::RESETS) -> () {
self.init_io(pwm, pad, io, resets);
}
#[doc = "Enables phase correct mode"]
pub fn set_ph_correct(&self) {
self.csr().modify(|_, w| w.ph_correct().set_bit());
}
#[doc = "Disales phase correct mode"]
pub fn clr_ph_correct(&self) {
self.csr().modify(|_, w| w.ph_correct().clear_bit());
}
#[doc = "Sets the integer part of the clock divider"]
pub fn set_div_int(&self, value: u8) {
self.div().modify(|_, w| unsafe { w.int().bits(value) });
}
#[doc = "Sets the fractional part of the clock divider"]
pub fn set_div_frac(&self, value: u8) {
self.div().modify(|_, w| unsafe { w.frac().bits(value) });
}
#[doc = "Enables output inversion"]
pub fn set_inv(&self) {
if (self.pin % 2 == 0) {
self.csr().modify(|_, w| w.a_inv().set_bit());
} else {
self.csr().modify(|_, w| w.b_inv().set_bit());
}
}
#[doc = "Disables output inversion"]
pub fn clr_inv(&self) {
if (self.pin % 2 == 0) {
self.csr().modify(|_, w| w.a_inv().clear_bit());
} else {
self.csr().modify(|_, w| w.b_inv().clear_bit());
}
}
#[doc = "Sets the top register value"]
pub fn set_top(&self, value: u16) {
self.top().write(|w| unsafe { w.top().bits(value) });
}
#[doc = "Sets the divmode to div. Use this if you aren't reading a PWM input."]
pub fn divmode_div(&self) {
self.csr().modify(|_, w| w.divmode().div());
}
#[doc = "Sets the divmode to level."]
pub fn divmode_level(&self) {
self.csr().modify(|_, w| w.divmode().level());
}
#[doc = "Sets the divmode to rise."]
pub fn divmode_rise(&self) {
self.csr().modify(|_, w| w.divmode().rise());
}
#[doc = "Sets the divmode to fall."]
pub fn divmode_fall(&self) {
self.csr().modify(|_, w| w.divmode().div());
}
}
impl PwmPin for $PXi {
type Duty = u16;
fn disable(&mut self) -> () {
self.csr().modify(|_, w| w.en().clear_bit());
}
fn enable(&mut self) -> () {
self.csr().modify(|_, w| w.en().set_bit());
}
fn get_duty(&self) -> Self::Duty {
if (self.pin % 2 == 0) {
self.cc().read().a().bits()
} else {
self.cc().read().b().bits()
}
}
fn get_max_duty(&self) -> Self::Duty {
self.top().read().top().bits()
}
fn set_duty(&mut self, duty: Self::Duty) {
if (self.pin % 2 == 0) {
self.cc().modify(|_, w| unsafe { w.a().bits(duty) });
} else {
self.cc().modify(|_, w| unsafe { w.b().bits(duty) });
}
}
}
)+}}
pwm! {
PWM, pwm, [
Pwm0: (pwm0, "pwm0", [0, 1, 16, 18], 0),
Pwm1: (pwm1, "pwm1", [2, 3, 18, 19], 1),
Pwm2: (pwm2, "pwm2", [4, 5, 20, 21], 2),
Pwm3: (pwm3, "pwm3", [6, 7, 22, 23], 3),
Pwm4: (pwm4, "pwm4", [8, 9, 24, 25], 4),
Pwm5: (pwm5, "pwm5", [10, 11, 26, 27], 5),
Pwm6: (pwm6, "pwm6", [12, 13, 28, 29], 6),
Pwm7: (pwm7, "pwm7", [14, 15], 7),
]
}

View file

@ -0,0 +1,30 @@
//! Semi-internal enums mostly used in typelevel magic
/// Value-level `struct` representing slice IDs
#[derive(PartialEq, Clone, Copy)]
pub struct DynSliceId {
/// Slice id
pub num: u8,
}
/// Slice modes
#[derive(PartialEq, Clone, Copy)]
pub enum DynSliceMode {
/// Count continuously whenever the slice is enabled
FreeRunning,
/// Count continuously when a high level is detected on the B pin
InputHighRunning,
/// Count once with each rising edge detected on the B pin
CountRisingEdge,
/// Count once with each falling edge detected on the B pin
CountFallingEdge,
}
/// Channel ids
#[derive(PartialEq, Clone, Copy)]
pub enum DynChannelId {
/// Channel A
A,
/// Channel B
B,
}

661
rp2040-hal/src/pwm/mod.rs Normal file
View file

@ -0,0 +1,661 @@
//! Pulse Width Modulation (PWM)
//!
//! First you must create a Slices struct which contains all the pwm slices.
//!
//! ```no_run
//! use rp2040_hal::{prelude::*, pwm::{InputHighRunning, Slices}};
//!
//!
//! let mut pac = rp2040_pac::Peripherals::take().unwrap();
//!
//! // Init PWMs
//! let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
//!
//! // Configure PWM4
//! let mut pwm = pwm_slices.pwm4;
//! pwm.set_ph_correct();
//! pwm.enable();
//!
//! // Set to run when b channel is high
//! let pwm = pwm.into_mode::<InputHighRunning>();
//! ```
//!
//! Once you have the PWM slice struct, you can add individual pins:
//!
//! ```no_run
//! # use rp2040_hal::{prelude::*, gpio::Pins, sio::Sio, pwm::{InputHighRunning, Slices}};
//! # let mut pac = rp2040_pac::Peripherals::take().unwrap();
//! # let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
//! # let mut pwm = pwm_slices.pwm4.into_mode::<InputHighRunning>();
//! # let mut pac = rp2040_pac::Peripherals::take().unwrap();
//! #
//! # let sio = Sio::new(pac.SIO);
//! # let pins = Pins::new(
//! # pac.IO_BANK0,
//! # pac.PADS_BANK0,
//! # sio.gpio_bank0,
//! # &mut pac.RESETS,
//! # );
//! #
//! use embedded_hal::PwmPin;
//!
//! // Use B channel (which inputs from GPIO 25)
//! let mut channel_b = pwm.channel_b;
//! let channel_pin_b = channel_b.input_from(pins.gpio25);
//!
//! // Use A channel (which outputs to GPIO 24)
//! let mut channel_a = pwm.channel_a;
//! let channel_pin_a = channel_a.output_to(pins.gpio24);
//!
//! // Set duty cycle
//! channel_a.set_duty(0x00ff);
//! channel_a.get_duty();
//! channel_a.set_inverted(); // Invert the output
//! channel_a.clr_inverted(); // Don't invert the output
//! ```
//!
//! The following configuration options are also available:
//!
//! ```no_run
//! # use rp2040_hal::{prelude::*, pwm::Slices};
//! # let mut pac = rp2040_pac::Peripherals::take().unwrap();
//! # let pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
//! # let mut pwm = pwm_slices.pwm4;
//! pwm.set_ph_correct(); // Run in phase correct mode
//! pwm.clr_ph_correct(); // Don't run in phase correct mode
//!
//! pwm.set_div_int(1u8); // To set integer part of clock divider
//! pwm.set_div_frac(0u8); // To set fractional part of clock divider
//!
//! pwm.get_top(); // To set the TOP register
//! pwm.set_top(u16::MAX); // To set the TOP register
//!
//! ```
//!
//! default_config() sets ph_correct to false, the clock divider to 1, does not invert the output, sets top to 65535, and resets the counter.
//! min_config() leaves those registers in the state they were before it was called (Careful, this can lead to unexpected behavior)
//! It's recommended to only call min_config() after calling default_config() on a pin that shares a PWM block.
use core::marker::PhantomData;
use crate::{
gpio::{
bank0::*, FunctionClock, FunctionI2C, FunctionPio0, FunctionPio1, FunctionPwm, FunctionSpi,
FunctionUart, FunctionUsbAux, FunctionXip, Input, InputConfig, Output, OutputConfig, Pin,
PinId, PinMode, ValidPinMode,
},
resets::SubsystemReset,
typelevel::Sealed,
};
use embedded_hal::PwmPin;
use pac::PWM;
pub mod dyn_slice;
pub use dyn_slice::*;
mod reg;
use reg::RegisterInterface;
/// Used to pin traits to a specific channel (A or B)
pub trait ChannelId: Sealed {
/// Corresponding [`DynChannelId`](dyn_slice::DynChannelId)
const DYN: DynChannelId;
}
/// Channel A
///
/// These are attached to the even gpio pins and can only do PWM output
pub enum A {}
/// Channel B
///
/// These are attached to the odd gpio pins and can do PWM output and edge counting for input
pub enum B {}
impl ChannelId for A {
const DYN: DynChannelId = DynChannelId::A;
}
impl ChannelId for B {
const DYN: DynChannelId = DynChannelId::B;
}
impl Sealed for A {}
impl Sealed for B {}
/// Counter is free-running, and will count continuously whenever the slice is enabled
pub struct FreeRunning;
/// Count continuously when a high level is detected on the B pin
pub struct InputHighRunning;
/// Count once with each rising edge detected on the B pin
pub struct CountRisingEdge;
/// Count once with each falling edge detected on the B pin
pub struct CountFallingEdge;
/// Type-level marker for tracking which slice modes are valid for which slices
pub trait ValidSliceMode<I: SliceId>: Sealed {}
/// Type-level marker for tracking which slice modes are valid for which slices
pub trait ValidSliceInputMode<I: SliceId>: Sealed + ValidSliceMode<I> {}
/// Mode for slice
pub trait SliceMode: Sealed + Sized {
/// Corresponding [`DynSliceMode`](dyn_slice::DynSliceMode)
const DYN: DynSliceMode;
}
impl Sealed for FreeRunning {}
impl SliceMode for FreeRunning {
const DYN: DynSliceMode = DynSliceMode::FreeRunning;
}
impl Sealed for InputHighRunning {}
impl SliceMode for InputHighRunning {
const DYN: DynSliceMode = DynSliceMode::InputHighRunning;
}
impl Sealed for CountRisingEdge {}
impl SliceMode for CountRisingEdge {
const DYN: DynSliceMode = DynSliceMode::CountRisingEdge;
}
impl Sealed for CountFallingEdge {}
impl SliceMode for CountFallingEdge {
const DYN: DynSliceMode = DynSliceMode::CountFallingEdge;
}
impl<I: SliceId> ValidSliceMode<I> for FreeRunning {}
impl<I: SliceId> ValidSliceMode<I> for InputHighRunning {}
impl<I: SliceId> ValidSliceMode<I> for CountRisingEdge {}
impl<I: SliceId> ValidSliceMode<I> for CountFallingEdge {}
impl<I: SliceId> ValidSliceInputMode<I> for InputHighRunning {}
impl<I: SliceId> ValidSliceInputMode<I> for CountRisingEdge {}
impl<I: SliceId> ValidSliceInputMode<I> for CountFallingEdge {}
//==============================================================================
// Slice IDs
//==============================================================================
/// Type-level `enum` for slice IDs
pub trait SliceId: Sealed {
/// Corresponding [`DynSliceId`](dyn_slice::DynSliceId)
const DYN: DynSliceId;
/// [`SliceMode`] at reset
type Reset;
}
macro_rules! slice_id {
($Id:ident, $NUM:literal, $reset : ident) => {
$crate::paste::paste! {
#[doc = "Slice ID representing slice " $NUM]
pub enum $Id {}
impl Sealed for $Id {}
impl SliceId for $Id {
type Reset = $reset;
const DYN: DynSliceId = DynSliceId { num: $NUM };
}
}
};
}
//==============================================================================
// Registers
//==============================================================================
/// Provide a safe register interface for [`Slice`]s
///
/// This `struct` takes ownership of a [`SliceId`] and provides an API to
/// access the corresponding registers.
struct Registers<I: SliceId> {
id: PhantomData<I>,
}
// [`Registers`] takes ownership of the [`SliceId`], and [`Slice`] guarantees that
// each slice is a singleton, so this implementation is safe.
unsafe impl<I: SliceId> RegisterInterface for Registers<I> {
#[inline]
fn id(&self) -> DynSliceId {
I::DYN
}
}
impl<I: SliceId> Registers<I> {
/// Create a new instance of [`Registers`]
///
/// # Safety
///
/// Users must never create two simultaneous instances of this `struct` with
/// the same [`SliceId`]
#[inline]
unsafe fn new() -> Self {
Registers { id: PhantomData }
}
/// Provide a type-level equivalent for the
/// [`RegisterInterface::change_mode`] method.
#[inline]
fn change_mode<M: SliceMode + ValidSliceMode<I>>(&mut self) {
RegisterInterface::do_change_mode(self, M::DYN);
}
}
/// Pwm slice
pub struct Slice<I, M>
where
I: SliceId,
M: SliceMode + ValidSliceMode<I>,
{
regs: Registers<I>,
mode: PhantomData<M>,
/// Channel A (always output)
pub channel_a: Channel<I, M, A>,
/// Channel B (input or output)
pub channel_b: Channel<I, M, B>,
}
impl<I, M> Slice<I, M>
where
I: SliceId,
M: SliceMode + ValidSliceMode<I>,
{
/// Create a new [`Slice`]
///
/// # Safety
///
/// Each [`Slice`] must be a singleton. For a given [`SliceId`], there must be
/// at most one corresponding [`Slice`] in existence at any given time.
/// Violating this requirement is `unsafe`.
#[inline]
pub(crate) unsafe fn new() -> Slice<I, M> {
Slice {
regs: Registers::new(),
mode: PhantomData,
channel_a: Channel::new(0),
channel_b: Channel::new(0),
}
}
/// Convert the slice to the requested [`SliceMode`]
#[inline]
pub fn into_mode<N: SliceMode + ValidSliceMode<I>>(mut self) -> Slice<I, N> {
if N::DYN != M::DYN {
self.regs.change_mode::<N>();
}
// Safe because we drop the existing slice
unsafe { Slice::new() }
}
/// Set a default config for the slice
pub fn default_config(&mut self) {
self.regs.write_ph_correct(false);
self.regs.write_div_int(1); // No divisor
self.regs.write_div_frac(0); // No divisor
self.regs.write_inv_a(false); //Don't invert the channel
self.regs.write_inv_b(false); //Don't invert the channel
self.regs.write_top(0xffff); // Wrap at max
self.regs.write_ctr(0x0000); //Reset the counter
self.regs.write_cc_a(0); //Default duty cycle of 0%
self.regs.write_cc_b(0); //Default duty cycle of 0%
}
/// Advance the phase with one count
///
/// Counter must be running at less than full speed (div_int + div_frac / 16 > 1)
#[inline]
pub fn advance_phase(&mut self) {
self.regs.advance_phase()
}
/// Retard the phase with one count
///
/// Counter must be running at less than full speed (div_int + div_frac / 16 > 1)
#[inline]
pub fn retard_phase(&mut self) {
self.regs.retard_phase()
}
/// Enable phase correct mode
#[inline]
pub fn set_ph_correct(&mut self) {
self.regs.write_ph_correct(true)
}
/// Disables phase correct mode
#[inline]
pub fn clr_ph_correct(&mut self) {
self.regs.write_ph_correct(false)
}
/// Enable slice
#[inline]
pub fn enable(&mut self) {
self.regs.write_enable(true);
}
/// Disable slice
#[inline]
pub fn disable(&mut self) {
self.regs.write_enable(false)
}
/// Sets the integer part of the clock divider
#[inline]
pub fn set_div_int(&mut self, value: u8) {
self.regs.write_div_int(value)
}
/// Sets the fractional part of the clock divider
#[inline]
pub fn set_div_frac(&mut self, value: u8) {
self.regs.write_div_frac(value)
}
/// Get the counter register value
#[inline]
pub fn get_counter(&self) -> u16 {
self.regs.read_ctr()
}
/// Set the counter register value
#[inline]
pub fn set_counter(&mut self, value: u16) {
self.regs.write_ctr(value)
}
/// Get the top register value
#[inline]
pub fn get_top(&self) -> u16 {
self.regs.read_top()
}
/// Sets the top register value
#[inline]
pub fn set_top(&mut self, value: u16) {
self.regs.write_top(value)
}
}
macro_rules! pwm {
($PWMX:ident, [
$($SXi:ident: ($slice:literal, [$($pin_a:ident, $pin_b:ident),*], $i:expr)),+
]) => {
$(
slice_id!($SXi, $slice, FreeRunning);
$(
impl ValidPwmOutputPin<$SXi, A> for $pin_a {}
impl ValidPwmOutputPin<$SXi, B> for $pin_b {}
impl ValidPwmInputPin<$SXi> for $pin_b {}
)*
)+
$crate::paste::paste!{
/// Collection of all the individual [`Slices`]s
pub struct Slices {
_pwm: $PWMX,
$(
#[doc = "Slice " $SXi]
pub [<$SXi:lower>] : Slice<$SXi,<$SXi as SliceId>::Reset>,
)+
}
impl Slices {
/// Take ownership of the PAC peripheral and split it into discrete [`Slice`]s
pub fn new(pwm: $PWMX, reset : &mut pac::RESETS) -> Self {
pwm.reset_bring_up(reset);
unsafe {
Self {
_pwm: pwm,
$(
[<$SXi:lower>]: Slice::new(),
)+
}
}
}
}
}
}
}
pwm! {
PWM, [
Pwm0: (0, [Gpio0, Gpio1, Gpio16, Gpio17], 0),
Pwm1: (1, [Gpio2, Gpio3, Gpio18, Gpio19], 1),
Pwm2: (2, [Gpio4, Gpio5, Gpio20, Gpio21], 2),
Pwm3: (3, [Gpio6, Gpio7, Gpio22, Gpio23], 3),
Pwm4: (4, [Gpio8, Gpio9, Gpio24, Gpio25], 4),
Pwm5: (5, [Gpio10, Gpio11, Gpio26, Gpio27], 5),
Pwm6: (6, [Gpio12, Gpio13, Gpio28, Gpio29], 6),
Pwm7: (7, [Gpio14, Gpio15], 7)
]
}
/// Marker trait for valid output pins
pub trait ValidPwmInputPin<S: SliceId>: Sealed {}
/// Marker trait for valid input pins (Channel B only)
pub trait ValidPwmOutputPin<S: SliceId, C: ChannelId>: Sealed {}
/// Make sure we can't free an GPIO pin while still keeping it attached to pwm
/// TODO: Maybe FunctionPWM should be private?
pub trait NonPwmPinMode: Sealed {}
impl NonPwmPinMode for FunctionClock {}
impl NonPwmPinMode for FunctionI2C {}
impl NonPwmPinMode for FunctionPio0 {}
impl NonPwmPinMode for FunctionPio1 {}
impl NonPwmPinMode for FunctionSpi {}
impl NonPwmPinMode for FunctionUart {}
impl NonPwmPinMode for FunctionUsbAux {}
impl NonPwmPinMode for FunctionXip {}
impl<C: InputConfig> NonPwmPinMode for Input<C> {}
impl<C: OutputConfig> NonPwmPinMode for Output<C> {}
/// Stores the attached gpio pin.
///
/// This value can be ignored/dropped or stored to retrieve the original pin struct
pub struct PwmPinToken<G: PinId + BankPinId> {
pin: Pin<G, FunctionPwm>,
}
impl<G: PinId + BankPinId> PwmPinToken<G> {
/// Retrieve the original pin while disconnecting it from the pwm
pub fn into_mode<N: PinMode + ValidPinMode<G> + NonPwmPinMode>(self) -> Pin<G, N> {
self.pin.into_mode::<N>()
}
}
impl Slices {
/// Free the pwm registers from the pwm hal struct while consuming it.
pub fn free(self) -> PWM {
self._pwm
}
// /// Enable multiple slices at the same time to make their counters sync up.
// ///
// /// You still need to call `slice` to get an actual slice
// pub fn enable_simultaneous<S: SliceId>(&mut self, bits: u8) {
// // Enable all slices at the same time
// unsafe {
// &(*pac::PWM::ptr())
// .en
// .modify(|r, w| w.bits(((r.bits() as u8) | bits) as u32));
// }
// }
// /// Get pwm slice based on gpio pin
// pub fn borrow_mut_from_pin<
// S: SliceId,
// C: ChannelId,
// G: PinId + BankPinId + ValidPwmOutputPin<S, C>,
// PM: PinMode + ValidPinMode<G>,
// SM: SliceMode + ValidSliceMode<S>,
// >(&mut self, _: &Pin<G, PM>) -> &mut Slice<S, SM>{
// match S::DYN {
// DynSliceId{num} if num == 0 => &mut self.pwm0,
// DynSliceId{num} if num == 1 => &mut self.pwm1,
// DynSliceId{num} if num == 2 => &mut self.pwm2,
// DynSliceId{num} if num == 3 => &mut self.pwm3,
// DynSliceId{num} if num == 4 => &mut self.pwm4,
// DynSliceId{num} if num == 5 => &mut self.pwm5,
// DynSliceId{num} if num == 6 => &mut self.pwm6,
// DynSliceId{num} if num == 7 => &mut self.pwm7,
// _ => unreachable!()
// }
// }
}
/// A Channel from the Pwm subsystem.
///
/// Its attached to one of the eight slices and can be an A or B side channel
pub struct Channel<S: SliceId, M: SliceMode, C: ChannelId> {
regs: Registers<S>,
slice_mode: PhantomData<M>,
channel_id: PhantomData<C>,
duty_cycle: u16,
}
impl<S: SliceId, M: SliceMode, C: ChannelId> Channel<S, M, C> {
pub(super) unsafe fn new(duty_cycle: u16) -> Self {
Channel {
regs: Registers::new(),
slice_mode: PhantomData,
channel_id: PhantomData,
duty_cycle,
}
}
/// Invert channel output
#[inline]
pub fn set_inverted(&mut self) {
self.regs.write_inv_b(true)
}
/// Invert channel output or not
#[inline]
pub fn clr_inverted(&mut self) {
self.regs.write_inv_b(false)
}
}
impl<S: SliceId, M: SliceMode, C: ChannelId> Sealed for Channel<S, M, C> {}
impl<S: SliceId, M: SliceMode> PwmPin for Channel<S, M, A> {
type Duty = u16;
/// We cant disable the channel without disturbing the other channel.
/// So this just sets the duty cycle to zero
fn disable(&mut self) {
self.duty_cycle = self.regs.read_cc_a();
self.regs.write_cc_a(0)
}
fn enable(&mut self) {
self.regs.write_cc_a(self.duty_cycle)
}
fn get_duty(&self) -> Self::Duty {
self.regs.read_cc_a()
}
fn get_max_duty(&self) -> Self::Duty {
self.regs.read_top()
}
fn set_duty(&mut self, duty: Self::Duty) {
self.regs.write_cc_a(duty)
}
}
impl<S: SliceId, M: SliceMode> PwmPin for Channel<S, M, B> {
type Duty = u16;
/// We cant disable the channel without disturbing the other channel.
/// So this just sets the duty cycle to zero
fn disable(&mut self) {
self.duty_cycle = self.regs.read_cc_b();
self.regs.write_cc_b(0)
}
fn enable(&mut self) {
self.regs.write_cc_b(self.duty_cycle)
}
fn get_duty(&self) -> Self::Duty {
self.regs.read_cc_b()
}
fn get_max_duty(&self) -> Self::Duty {
self.regs.read_top()
}
fn set_duty(&mut self, duty: Self::Duty) {
self.regs.write_cc_b(duty)
}
}
impl<S: SliceId, M: SliceMode + ValidSliceMode<S>> Channel<S, M, A> {
/// Capture a gpio pin and use it as pwm output for channel A
pub fn output_to<
G: PinId + BankPinId + ValidPwmOutputPin<S, A>,
PM: PinMode + ValidPinMode<G>,
>(
&mut self,
pin: Pin<G, PM>,
) -> PwmPinToken<G> {
PwmPinToken {
pin: pin.into_mode(),
}
}
}
impl<S: SliceId, M: SliceMode + ValidSliceMode<S>> Channel<S, M, B> {
/// Capture a gpio pin and use it as pwm output for channel B
pub fn output_to<
G: PinId + BankPinId + ValidPwmOutputPin<S, B>,
PM: PinMode + ValidPinMode<G>,
>(
&mut self,
pin: Pin<G, PM>,
) -> PwmPinToken<G> {
PwmPinToken {
pin: pin.into_mode(),
}
}
}
impl<S: SliceId, M: SliceMode + ValidSliceInputMode<S>> Channel<S, M, B> {
/// Capture a gpio pin and use it as pwm input for channel B
pub fn input_from<G: PinId + BankPinId + ValidPwmInputPin<S>, PM: PinMode + ValidPinMode<G>>(
&mut self,
pin: Pin<G, PM>,
) -> PwmPinToken<G> {
PwmPinToken {
pin: pin.into_mode(),
}
}
}
impl<S: SliceId, M: SliceMode + ValidSliceMode<S>> Slice<S, M> {
/// Capture a gpio pin and use it as pwm output
pub fn output_to<
G: PinId + BankPinId + ValidPwmOutputPin<S, C>,
PM: PinMode + ValidPinMode<G>,
C: ChannelId,
>(
&mut self,
pin: Pin<G, PM>,
) -> PwmPinToken<G> {
PwmPinToken {
pin: pin.into_mode(),
}
}
}
impl<S: SliceId, M: SliceMode + ValidSliceInputMode<S>> Slice<S, M> {
/// Capture a gpio pin and use it as pwm input for channel B
pub fn input_from<G: PinId + BankPinId + ValidPwmInputPin<S>, PM: PinMode + ValidPinMode<G>>(
&mut self,
pin: Pin<G, PM>,
) -> PwmPinToken<G> {
PwmPinToken {
pin: pin.into_mode(),
}
}
}

98
rp2040-hal/src/pwm/reg.rs Normal file
View file

@ -0,0 +1,98 @@
use super::dyn_slice::{DynSliceId, DynSliceMode};
use pac::pwm::CH;
pub(super) unsafe trait RegisterInterface {
/// Provide a [`DynSliceId`] identifying the set of registers controlled by
/// this type.
fn id(&self) -> DynSliceId;
#[inline]
fn ch(&self) -> &CH {
let num = self.id().num as usize;
unsafe { &(*pac::PWM::ptr()).ch[num] }
}
#[inline]
fn advance_phase(&mut self) {
self.ch().csr.modify(|_, w| w.ph_adv().set_bit())
}
#[inline]
fn retard_phase(&mut self) {
self.ch().csr.modify(|_, w| w.ph_ret().set_bit())
}
#[inline]
fn do_change_mode(&mut self, mode: DynSliceMode) {
self.ch().csr.modify(|_, w| match mode {
DynSliceMode::FreeRunning => w.divmode().div(),
DynSliceMode::InputHighRunning => w.divmode().level(),
DynSliceMode::CountRisingEdge => w.divmode().rise(),
DynSliceMode::CountFallingEdge => w.divmode().fall(),
})
}
#[inline]
fn write_inv_a(&mut self, value: bool) {
self.ch().csr.modify(|_, w| w.a_inv().bit(value));
}
#[inline]
fn write_inv_b(&mut self, value: bool) {
self.ch().csr.modify(|_, w| w.b_inv().bit(value));
}
#[inline]
fn write_ph_correct(&mut self, value: bool) {
self.ch().csr.modify(|_, w| w.ph_correct().bit(value));
}
#[inline]
fn write_enable(&mut self, value: bool) {
self.ch().csr.modify(|_, w| w.en().bit(value));
}
#[inline]
fn write_div_int(&mut self, value: u8) {
self.ch().div.modify(|_, w| unsafe { w.int().bits(value) });
}
#[inline]
fn write_div_frac(&mut self, value: u8) {
self.ch().div.modify(|_, w| unsafe { w.frac().bits(value) });
}
#[inline]
fn write_ctr(&mut self, value: u16) {
self.ch().ctr.write(|w| unsafe { w.ctr().bits(value) });
}
#[inline]
fn read_ctr(&self) -> u16 {
self.ch().ctr.read().ctr().bits()
}
#[inline]
fn write_cc_a(&mut self, value: u16) {
self.ch().cc.modify(|_, w| unsafe { w.a().bits(value) });
}
#[inline]
fn read_cc_a(&self) -> u16 {
self.ch().cc.read().a().bits()
}
#[inline]
fn write_cc_b(&mut self, value: u16) {
self.ch().cc.modify(|_, w| unsafe { w.b().bits(value) });
}
#[inline]
fn read_cc_b(&self) -> u16 {
self.ch().cc.read().b().bits()
}
#[inline]
fn write_top(&mut self, value: u16) {
self.ch().top.write(|w| unsafe { w.top().bits(value) });
}
#[inline]
fn read_top(&self) -> u16 {
self.ch().top.read().top().bits()
}
}