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::OptionalPin as GpioOptionalPin;
|
||||
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)]
|
||||
pub enum Prescaler {
|
||||
@ -23,11 +24,57 @@ pub enum Prescaler {
|
||||
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> {
|
||||
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> {
|
||||
/// Creates the interface to a UARTE instance.
|
||||
/// 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.
|
||||
#[inline(always)]
|
||||
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));
|
||||
}
|
||||
|
||||
/// Sets the PWM clock prescaler.
|
||||
/// Gets the PWM clock prescaler.
|
||||
#[inline(always)]
|
||||
pub fn prescaler(&self) -> Prescaler {
|
||||
match T::regs().prescaler.read().prescaler().bits() {
|
||||
|
@ -2,14 +2,14 @@ const SRAM_LOWER: usize = 0x2000_0000;
|
||||
const SRAM_UPPER: usize = 0x3000_0000;
|
||||
|
||||
/// 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;
|
||||
ptr >= SRAM_LOWER && (ptr + slice.len()) < SRAM_UPPER
|
||||
}
|
||||
|
||||
/// Return an error if slice is not in RAM.
|
||||
#[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) {
|
||||
Ok(())
|
||||
} 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