use crate::bdma::BdmaSai4Rx;
use crate::pdm_filter::CicFilter;
use crate::sai4_pdm::Sai4Pdm;
#[derive(Clone, Copy, PartialEq, Eq)]
enum State {
Idle,
Ready,
Recording,
}
pub struct MicCapture {
sai: Sai4Pdm,
bdma: BdmaSai4Rx,
filter: CicFilter,
state: State,
last_processed: u8,
pdm_buf_len: usize,
}
impl MicCapture {
pub fn new() -> Self {
Self {
sai: Sai4Pdm::new(),
bdma: BdmaSai4Rx::new(),
filter: CicFilter::new(64),
state: State::Idle,
last_processed: 1, pdm_buf_len: 0,
}
}
pub fn init(
&mut self,
pdm_buf0: &mut [u16],
pdm_buf1: &mut [u16],
decimation: u32,
sai_clock_source: u8,
mckdiv: u8,
) {
assert_eq!(pdm_buf0.len(), pdm_buf1.len());
self.pdm_buf_len = pdm_buf0.len();
self.sai.enable_clock(sai_clock_source);
self.bdma.enable_clock();
self.sai.configure(mckdiv);
let buf_bytes = pdm_buf0.len() * 2;
self.bdma.configure(
self.sai.data_register_addr(),
pdm_buf0.as_mut_ptr(),
pdm_buf1.as_mut_ptr(),
buf_bytes,
);
self.filter = CicFilter::new(decimation);
self.state = State::Ready;
self.last_processed = 1;
}
pub fn start(&mut self) {
if self.state != State::Ready && self.state != State::Recording {
return;
}
self.filter.reset();
self.last_processed = 1;
self.sai.enable_dma();
self.bdma.start();
self.sai.enable();
self.state = State::Recording;
}
pub fn stop(&mut self) {
self.sai.disable();
self.sai.disable_dma();
self.bdma.stop();
self.state = State::Ready;
}
pub fn poll_with_buffer(&mut self, pdm_buf: &[u16], pcm_out: &mut [i16]) -> Option<usize> {
if self.state != State::Recording {
return None;
}
let tc = self.bdma.transfer_complete();
let ht = self.bdma.half_transfer();
if !tc && !ht {
return None;
}
if tc {
self.bdma.clear_transfer_complete();
}
if ht {
self.bdma.clear_half_transfer();
}
self.sai.clear_overrun();
let count = self.filter.process(pdm_buf, pcm_out);
Some(count)
}
pub fn poll_ready(&mut self) -> Option<u8> {
if self.state != State::Recording {
return None;
}
let tc = self.bdma.transfer_complete();
let ht = self.bdma.half_transfer();
if tc {
self.bdma.clear_transfer_complete();
self.sai.clear_overrun();
let completed = if self.bdma.current_target() == 0 {
1
} else {
0
};
if completed != self.last_processed {
self.last_processed = completed;
return Some(completed);
}
}
if ht {
self.bdma.clear_half_transfer();
}
None
}
pub fn filter(&mut self) -> &mut CicFilter {
&mut self.filter
}
pub fn is_recording(&self) -> bool {
self.state == State::Recording
}
}