Merge pull request #2584 from ohrlabs/fix-pdm-gain
embassy-nrf: Fix PDM gain register value derivation
This commit is contained in:
commit
6b0e4dfb2d
1 changed files with 26 additions and 29 deletions
|
@ -1,4 +1,4 @@
|
||||||
//! Pulse Density Modulation (PDM) mirophone driver.
|
//! Pulse Density Modulation (PDM) mirophone driver
|
||||||
|
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
|
||||||
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
|
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
|
||||||
use crate::{interrupt, Peripheral};
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
/// Interrupt handler.
|
/// Interrupt handler
|
||||||
pub struct InterruptHandler<T: Instance> {
|
pub struct InterruptHandler<T: Instance> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
@ -56,12 +56,12 @@ pub struct Pdm<'d, T: Instance> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PDM error.
|
/// PDM error
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Buffer is too long.
|
/// Buffer is too long
|
||||||
BufferTooLong,
|
BufferTooLong,
|
||||||
/// Buffer is empty
|
/// Buffer is empty
|
||||||
BufferZeroLength,
|
BufferZeroLength,
|
||||||
|
@ -75,13 +75,13 @@ static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||||
|
|
||||||
/// The state of a continuously running sampler. While it reflects
|
/// The state of a continuously running sampler. While it reflects
|
||||||
/// the progress of a sampler, it also signals what should be done
|
/// the progress of a sampler, it also signals what should be done
|
||||||
/// next. For example, if the sampler has stopped then the Pdm implementation
|
/// next. For example, if the sampler has stopped then the PDM implementation
|
||||||
/// can then tear down its infrastructure.
|
/// can then tear down its infrastructure
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum SamplerState {
|
pub enum SamplerState {
|
||||||
/// The sampler processed the samples and is ready for more.
|
/// The sampler processed the samples and is ready for more
|
||||||
Sampled,
|
Sampled,
|
||||||
/// The sampler is done processing samples.
|
/// The sampler is done processing samples
|
||||||
Stopped,
|
Stopped,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,15 +145,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
|
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
|
||||||
let gain_left = gain_left
|
let gain_to_bits = |gain: I7F1| -> u8 {
|
||||||
.saturating_add(I7F1::from_bits(40))
|
let gain = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50);
|
||||||
.saturating_to_num::<u8>()
|
unsafe { core::mem::transmute(gain) }
|
||||||
.clamp(0, 0x50);
|
};
|
||||||
let gain_right = gain_right
|
let gain_left = gain_to_bits(gain_left);
|
||||||
.saturating_add(I7F1::from_bits(40))
|
let gain_right = gain_to_bits(gain_right);
|
||||||
.saturating_to_num::<u8>()
|
|
||||||
.clamp(0, 0x50);
|
|
||||||
|
|
||||||
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
|
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
|
||||||
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
|
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
|
||||||
}
|
}
|
||||||
|
@ -163,12 +160,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
Self::_set_gain(T::regs(), gain_left, gain_right)
|
Self::_set_gain(T::regs(), gain_left, gain_right)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start sampling microphon data into a dummy buffer
|
/// Start sampling microphone data into a dummy buffer.
|
||||||
/// Usefull to start the microphon and keep it active between recording samples
|
/// Useful to start the microphone and keep it active between recording samples.
|
||||||
pub async fn start(&mut self) {
|
pub async fn start(&mut self) {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
// start dummy sampling because microphon needs some setup time
|
// start dummy sampling because microphone needs some setup time
|
||||||
r.sample
|
r.sample
|
||||||
.ptr
|
.ptr
|
||||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||||
|
@ -179,14 +176,14 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop sampling microphon data inta a dummy buffer
|
/// Stop sampling microphone data inta a dummy buffer
|
||||||
pub async fn stop(&mut self) {
|
pub async fn stop(&mut self) {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
r.events_started.reset();
|
r.events_started.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sample data into the given buffer.
|
/// Sample data into the given buffer
|
||||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||||
if buffer.len() == 0 {
|
if buffer.len() == 0 {
|
||||||
return Err(Error::BufferZeroLength);
|
return Err(Error::BufferZeroLength);
|
||||||
|
@ -303,7 +300,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't reorder the start event before the previous writes. Hopefully self
|
// Don't reorder the start event before the previous writes. Hopefully self
|
||||||
// wouldn't happen anyway.
|
// wouldn't happen anyway
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||||
|
@ -314,11 +311,11 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
|
|
||||||
let drop = OnDrop::new(|| {
|
let drop = OnDrop::new(|| {
|
||||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
// N.B. It would be better if this were async, but Drop only support sync code.
|
// N.B. It would be better if this were async, but Drop only support sync code
|
||||||
while r.events_stopped.read().bits() != 0 {}
|
while r.events_stopped.read().bits() != 0 {}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for events and complete when the sampler indicates it has had enough.
|
// Wait for events and complete when the sampler indicates it has had enough
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
|
@ -331,7 +328,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
r.intenset.write(|w| w.end().set());
|
r.intenset.write(|w| w.end().set());
|
||||||
|
|
||||||
if !done {
|
if !done {
|
||||||
// Discard the last buffer after the user requested a stop.
|
// Discard the last buffer after the user requested a stop
|
||||||
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
|
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
|
||||||
let next_buffer = 1 - current_buffer;
|
let next_buffer = 1 - current_buffer;
|
||||||
current_buffer = next_buffer;
|
current_buffer = next_buffer;
|
||||||
|
@ -405,7 +402,7 @@ impl Default for Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PDM operation mode.
|
/// PDM operation mode
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum OperationMode {
|
pub enum OperationMode {
|
||||||
/// Mono (1 channel)
|
/// Mono (1 channel)
|
||||||
|
@ -476,9 +473,9 @@ pub(crate) mod sealed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PDM peripheral instance.
|
/// PDM peripheral instance
|
||||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||||
/// Interrupt for this peripheral.
|
/// Interrupt for this peripheral
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type Interrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue