mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-23 20:51:31 +11:00
Pwm improvements (#61)
* Reimplement PWM driver using typestates * Improve docs
This commit is contained in:
parent
216aad2c76
commit
ffa39f65f5
|
@ -15,8 +15,9 @@ use pico::{
|
||||||
pwm::*,
|
pwm::*,
|
||||||
watchdog::Watchdog,
|
watchdog::Watchdog,
|
||||||
},
|
},
|
||||||
XOSC_CRYSTAL_FREQ,
|
Pins, XOSC_CRYSTAL_FREQ,
|
||||||
};
|
};
|
||||||
|
use rp2040_hal::sio::Sio;
|
||||||
|
|
||||||
#[link_section = ".boot2"]
|
#[link_section = ".boot2"]
|
||||||
#[used]
|
#[used]
|
||||||
|
@ -44,32 +45,37 @@ fn main() -> ! {
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer());
|
let sio = Sio::new(pac.SIO);
|
||||||
|
let pins = Pins::new(
|
||||||
let mut pwm_pin = Pwm4::new(25);
|
pac.IO_BANK0,
|
||||||
|
pac.PADS_BANK0,
|
||||||
//Instead of having it take references to all of these pac objects, eventually this should just
|
sio.gpio_bank0,
|
||||||
//take ownership of a GPIO pin.
|
|
||||||
pwm_pin.default_config(
|
|
||||||
&mut pac.PWM,
|
|
||||||
&mut pac.PADS_BANK0,
|
|
||||||
&mut pac.IO_BANK0,
|
|
||||||
&mut pac.RESETS,
|
&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 {
|
loop {
|
||||||
for i in (LOW..=HIGH).skip(100) {
|
for i in (LOW..=HIGH).skip(100) {
|
||||||
delay.delay_us(8);
|
delay.delay_us(8);
|
||||||
pwm_pin.set_duty(i);
|
channel.set_duty(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in (LOW..=HIGH).rev().skip(100) {
|
for i in (LOW..=HIGH).rev().skip(100) {
|
||||||
delay.delay_us(8);
|
delay.delay_us(8);
|
||||||
pwm_pin.set_duty(i);
|
channel.set_duty(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
delay.delay_ms(500);
|
delay.delay_ms(500);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::PwmPin;
|
use embedded_hal::PwmPin;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use rp2040_hal::pwm::*;
|
use rp2040_hal::{gpio::Pins, pwm::*, sio::Sio};
|
||||||
|
|
||||||
#[link_section = ".boot2"]
|
#[link_section = ".boot2"]
|
||||||
#[used]
|
#[used]
|
||||||
|
@ -14,26 +14,30 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
let mut pac = rp2040_pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let mut pwm_pin = Pwm4::new(25);
|
let sio = Sio::new(pac.SIO);
|
||||||
|
let pins = Pins::new(
|
||||||
//Instead of having it take references to all of these pac objects, eventually this should just
|
pac.IO_BANK0,
|
||||||
//take ownership of a GPIO pin.
|
pac.PADS_BANK0,
|
||||||
pwm_pin.default_config(
|
sio.gpio_bank0,
|
||||||
&mut pac.PWM,
|
|
||||||
&mut pac.PADS_BANK0,
|
|
||||||
&mut pac.IO_BANK0,
|
|
||||||
&mut pac.RESETS,
|
&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 {
|
loop {
|
||||||
pwm_pin.set_duty(15000);
|
channel.set_duty(15000);
|
||||||
// TODO: Replace with proper delays once we have clocks working
|
// TODO: Replace with proper delays once we have clocks working
|
||||||
cortex_m::asm::delay(5_000_000);
|
cortex_m::asm::delay(5_000_000);
|
||||||
pwm_pin.set_duty(30000);
|
channel.set_duty(30000);
|
||||||
cortex_m::asm::delay(5_000_000);
|
cortex_m::asm::delay(5_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
|
||||||
]
|
|
||||||
}
|
|
30
rp2040-hal/src/pwm/dyn_slice.rs
Normal file
30
rp2040-hal/src/pwm/dyn_slice.rs
Normal 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
661
rp2040-hal/src/pwm/mod.rs
Normal 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
98
rp2040-hal/src/pwm/reg.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue