simple_playback api from nrf sdk
This commit is contained in:
parent
dfccb84fcb
commit
eb0bf1fd7a
@ -9,7 +9,8 @@ use embassy_hal_common::unborrow;
|
|||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
use crate::gpio::OptionalPin as GpioOptionalPin;
|
use crate::gpio::OptionalPin as GpioOptionalPin;
|
||||||
use crate::interrupt::Interrupt;
|
use crate::interrupt::Interrupt;
|
||||||
use crate::pac;
|
use crate::util::slice_in_ram_or;
|
||||||
|
use crate::{pac, EASY_DMA_SIZE};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum Prescaler {
|
pub enum Prescaler {
|
||||||
@ -23,11 +24,57 @@ pub enum Prescaler {
|
|||||||
Div128,
|
Div128,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interface to the UARTE peripheral
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
|
pub enum SequenceLoad {
|
||||||
|
Common,
|
||||||
|
Grouped,
|
||||||
|
Individual,
|
||||||
|
Waveform,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
|
pub enum CounterMode {
|
||||||
|
Up,
|
||||||
|
UpAndDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interface to the PWM peripheral
|
||||||
pub struct Pwm<'d, T: Instance> {
|
pub struct Pwm<'d, T: Instance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure an infinite looping sequence for `simple_playback`
|
||||||
|
pub struct LoopingConfig<'a> {
|
||||||
|
/// Selects up mode or up-and-down mode for the counter
|
||||||
|
pub counter_mode: CounterMode,
|
||||||
|
// top value to be compared against buffer values
|
||||||
|
pub top: u16,
|
||||||
|
/// Configuration for PWM_CLK
|
||||||
|
pub prescaler: Prescaler,
|
||||||
|
/// In ram buffer to be played back
|
||||||
|
pub sequence: &'a [u16],
|
||||||
|
/// Common Mode means seq in buffer will be used across all channels
|
||||||
|
/// Individual Mode buffer holds [ch0_0, ch1_0, ch2_0, ch3_0, ch0_1, ch1_1,
|
||||||
|
/// ch2_1, ch3_1 ... ch0_n, ch1_n, ch2_n, ch3_n]
|
||||||
|
pub sequence_load: SequenceLoad,
|
||||||
|
/// will instruct a new RAM stored pulse width value on every (N+1)th PWM
|
||||||
|
/// period. Setting the register to zero will result in a new duty cycle
|
||||||
|
/// update every PWM period as long as the minimum PWM period is observed.
|
||||||
|
pub repeats: u32,
|
||||||
|
/// enddelay PWM period delays between last period on sequence 0 before repeating
|
||||||
|
pub enddelay: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
Seq0BufferTooLong,
|
||||||
|
Seq1BufferTooLong,
|
||||||
|
/// EasyDMA can only read from data memory, read only buffers in flash will fail.
|
||||||
|
DMABufferNotInDataMemory,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Pwm<'d, T> {
|
impl<'d, T: Instance> Pwm<'d, T> {
|
||||||
/// Creates the interface to a UARTE instance.
|
/// Creates the interface to a UARTE instance.
|
||||||
/// Sets the baud rate, parity and assigns the pins to the UARTE peripheral.
|
/// Sets the baud rate, parity and assigns the pins to the UARTE peripheral.
|
||||||
@ -99,6 +146,120 @@ impl<'d, T: Instance> Pwm<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simple_playback(
|
||||||
|
_pwm: impl Unborrow<Target = T> + 'd,
|
||||||
|
ch0: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
|
||||||
|
ch1: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
|
||||||
|
ch2: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
|
||||||
|
ch3: impl Unborrow<Target = impl GpioOptionalPin> + 'd,
|
||||||
|
config: LoopingConfig,
|
||||||
|
count: u16,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
slice_in_ram_or(config.sequence, Error::DMABufferNotInDataMemory)?;
|
||||||
|
|
||||||
|
if config.sequence.len() > EASY_DMA_SIZE {
|
||||||
|
return Err(Error::Seq0BufferTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
unborrow!(ch0, ch1, ch2, ch3);
|
||||||
|
|
||||||
|
let odd: bool = count & 1 == 1;
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
if let Some(pin) = ch0.pin_mut() {
|
||||||
|
pin.set_low();
|
||||||
|
pin.conf().write(|w| w.dir().output());
|
||||||
|
r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) });
|
||||||
|
}
|
||||||
|
if let Some(pin) = ch1.pin_mut() {
|
||||||
|
pin.set_low();
|
||||||
|
pin.conf().write(|w| w.dir().output());
|
||||||
|
r.psel.out[1].write(|w| unsafe { w.bits(ch1.psel_bits()) });
|
||||||
|
}
|
||||||
|
if let Some(pin) = ch2.pin_mut() {
|
||||||
|
pin.set_low();
|
||||||
|
pin.conf().write(|w| w.dir().output());
|
||||||
|
r.psel.out[2].write(|w| unsafe { w.bits(ch2.psel_bits()) });
|
||||||
|
}
|
||||||
|
if let Some(pin) = ch3.pin_mut() {
|
||||||
|
pin.set_low();
|
||||||
|
pin.conf().write(|w| w.dir().output());
|
||||||
|
r.psel.out[3].write(|w| unsafe { w.bits(ch3.psel_bits()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
r.enable.write(|w| w.enable().enabled());
|
||||||
|
|
||||||
|
r.mode
|
||||||
|
.write(|w| unsafe { w.bits(config.counter_mode as u32) });
|
||||||
|
r.prescaler
|
||||||
|
.write(|w| w.prescaler().bits(config.prescaler as u8));
|
||||||
|
r.countertop
|
||||||
|
.write(|w| unsafe { w.countertop().bits(config.top) });
|
||||||
|
|
||||||
|
r.decoder.write(|w| {
|
||||||
|
w.load().bits(config.sequence_load as u8);
|
||||||
|
w.mode().refresh_count()
|
||||||
|
});
|
||||||
|
|
||||||
|
r.seq0
|
||||||
|
.ptr
|
||||||
|
.write(|w| unsafe { w.bits(config.sequence.as_ptr() as u32) });
|
||||||
|
r.seq0
|
||||||
|
.cnt
|
||||||
|
.write(|w| unsafe { w.bits(config.sequence.len() as u32) });
|
||||||
|
r.seq0.refresh.write(|w| unsafe { w.bits(config.repeats) });
|
||||||
|
r.seq0
|
||||||
|
.enddelay
|
||||||
|
.write(|w| unsafe { w.bits(config.enddelay) });
|
||||||
|
|
||||||
|
r.seq1
|
||||||
|
.ptr
|
||||||
|
.write(|w| unsafe { w.bits(config.sequence.as_ptr() as u32) });
|
||||||
|
r.seq1
|
||||||
|
.cnt
|
||||||
|
.write(|w| unsafe { w.bits(config.sequence.len() as u32) });
|
||||||
|
r.seq1.refresh.write(|w| unsafe { w.bits(config.repeats) });
|
||||||
|
r.seq1
|
||||||
|
.enddelay
|
||||||
|
.write(|w| unsafe { w.bits(config.enddelay) });
|
||||||
|
|
||||||
|
let mut loop_: u16 = count / 2;
|
||||||
|
if odd {
|
||||||
|
loop_ += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.loop_.write(|w| unsafe { w.cnt().bits(loop_) });
|
||||||
|
|
||||||
|
if odd {
|
||||||
|
r.shorts.write(|w| w.loopsdone_seqstart1().set_bit());
|
||||||
|
} else {
|
||||||
|
r.shorts.write(|w| w.loopsdone_seqstart0().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// tasks_seqstart doesnt exist in all svds so write its bit instead
|
||||||
|
if odd {
|
||||||
|
r.tasks_seqstart[1].write(|w| unsafe { w.bits(0x01) });
|
||||||
|
} else {
|
||||||
|
r.tasks_seqstart[0].write(|w| unsafe { w.bits(0x01) });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop playback
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn stop(&self) {
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
r.shorts.write(|w| unsafe { w.bits(0x0) });
|
||||||
|
|
||||||
|
// tasks_stop doesnt exist in all svds so write its bit instead
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(0x01) });
|
||||||
|
}
|
||||||
|
|
||||||
/// Enables the PWM generator.
|
/// Enables the PWM generator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable(&self) {
|
pub fn enable(&self) {
|
||||||
@ -128,7 +289,7 @@ impl<'d, T: Instance> Pwm<'d, T> {
|
|||||||
T::regs().prescaler.write(|w| w.prescaler().bits(div as u8));
|
T::regs().prescaler.write(|w| w.prescaler().bits(div as u8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the PWM clock prescaler.
|
/// Gets the PWM clock prescaler.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn prescaler(&self) -> Prescaler {
|
pub fn prescaler(&self) -> Prescaler {
|
||||||
match T::regs().prescaler.read().prescaler().bits() {
|
match T::regs().prescaler.read().prescaler().bits() {
|
||||||
|
@ -2,14 +2,14 @@ const SRAM_LOWER: usize = 0x2000_0000;
|
|||||||
const SRAM_UPPER: usize = 0x3000_0000;
|
const SRAM_UPPER: usize = 0x3000_0000;
|
||||||
|
|
||||||
/// Does this slice reside entirely within RAM?
|
/// Does this slice reside entirely within RAM?
|
||||||
pub(crate) fn slice_in_ram(slice: &[u8]) -> bool {
|
pub(crate) fn slice_in_ram<T>(slice: &[T]) -> bool {
|
||||||
let ptr = slice.as_ptr() as usize;
|
let ptr = slice.as_ptr() as usize;
|
||||||
ptr >= SRAM_LOWER && (ptr + slice.len()) < SRAM_UPPER
|
ptr >= SRAM_LOWER && (ptr + slice.len()) < SRAM_UPPER
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an error if slice is not in RAM.
|
/// Return an error if slice is not in RAM.
|
||||||
#[cfg(not(feature = "nrf51"))]
|
#[cfg(not(feature = "nrf51"))]
|
||||||
pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> {
|
pub(crate) fn slice_in_ram_or<T, E>(slice: &[T], err: E) -> Result<(), E> {
|
||||||
if slice.len() == 0 || slice_in_ram(slice) {
|
if slice.len() == 0 || slice_in_ram(slice) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
41
examples/nrf/src/bin/pwm_sequence.rs
Normal file
41
examples/nrf/src/bin/pwm_sequence.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
use defmt::*;
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_nrf::gpio::NoPin;
|
||||||
|
use embassy_nrf::pwm::{CounterMode, LoopingConfig, Prescaler, Pwm, SequenceLoad};
|
||||||
|
use embassy_nrf::Peripherals;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
let seq_values: [u16; 2] = [0, 0x8000];
|
||||||
|
|
||||||
|
let config = LoopingConfig {
|
||||||
|
counter_mode: CounterMode::Up,
|
||||||
|
top: 31250,
|
||||||
|
prescaler: Prescaler::Div128,
|
||||||
|
sequence: &seq_values,
|
||||||
|
sequence_load: SequenceLoad::Common,
|
||||||
|
repeats: 1,
|
||||||
|
enddelay: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pwm = unwrap!(Pwm::simple_playback(
|
||||||
|
p.PWM0, p.P0_13, NoPin, NoPin, NoPin, config, 1
|
||||||
|
));
|
||||||
|
info!("pwm started!");
|
||||||
|
|
||||||
|
Timer::after(Duration::from_millis(10000)).await;
|
||||||
|
|
||||||
|
pwm.stop();
|
||||||
|
info!("pwm stopped!");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
Timer::after(Duration::from_millis(1000)).await;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user