const SAI4_BASE: u32 = 0x5800_5400;
const ACR1: u32 = 0x04;
const ACR2: u32 = 0x08;
const AFRCR: u32 = 0x0C;
const ASLOTR: u32 = 0x10;
const AIM: u32 = 0x14;
const ASR: u32 = 0x18;
const ACLRFR: u32 = 0x1C;
const ADR: u32 = 0x20;
const PDMCR: u32 = 0x44;
const PDMDLY: u32 = 0x48;
const CR1_SAIEN: u32 = 1 << 16;
const CR1_DMAEN: u32 = 1 << 17;
const CR1_NODIV: u32 = 1 << 19;
const CR1_MODE_MASTER_RX: u32 = 0b01;
const CR1_DS_16BIT: u32 = 0b100 << 5;
const CR1_SYNCEN_ASYNC: u32 = 0b00 << 10;
const CR1_MCKDIV_SHIFT: u32 = 20;
const CR2_FFLUSH: u32 = 1 << 3;
const CR2_FTH_QUARTER: u32 = 0b001;
const FRCR_FSDEF: u32 = 1 << 16;
const FRCR_FSPOL: u32 = 1 << 17;
const PDMCR_PDMEN: u32 = 1 << 0;
const PDMCR_MICNBR_SHIFT: u32 = 4;
const PDMCR_CKEN1: u32 = 1 << 8;
const RCC_APB4ENR: u32 = 0x5802_44F4;
const RCC_D3CCIPR: u32 = 0x5802_4D2C;
const SR_OVRUDR: u32 = 1 << 0;
pub struct Sai4Pdm {
base: u32,
}
impl Sai4Pdm {
pub fn new() -> Self {
Self { base: SAI4_BASE }
}
pub fn enable_clock(&self, clock_source: u8) {
unsafe {
let apb4 = RCC_APB4ENR as *mut u32;
apb4.write_volatile(apb4.read_volatile() | (1 << 21));
let _ = (apb4 as *const u32).read_volatile();
let d3ccipr = RCC_D3CCIPR as *mut u32;
let val = d3ccipr.read_volatile();
d3ccipr
.write_volatile((val & !(0b111 << 21)) | (((clock_source as u32) & 0b111) << 21));
}
}
pub fn configure(&self, mckdiv: u8) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() & !CR1_SAIEN);
while acr1.read_volatile() & CR1_SAIEN != 0 {}
let acr2 = self.reg(ACR2);
acr2.write_volatile(CR2_FTH_QUARTER | CR2_FFLUSH);
let cr1 = CR1_MODE_MASTER_RX
| CR1_DS_16BIT
| CR1_SYNCEN_ASYNC
| CR1_NODIV
| ((mckdiv as u32) << CR1_MCKDIV_SHIFT);
acr1.write_volatile(cr1);
acr2.write_volatile(CR2_FTH_QUARTER);
let frcr = 15u32 | FRCR_FSDEF | FRCR_FSPOL;
self.reg(AFRCR).write_volatile(frcr);
let slotr = (0u32 << 8) | (1u32 << 16);
self.reg(ASLOTR).write_volatile(slotr);
self.reg(AIM).write_volatile(0);
self.reg(ACLRFR).write_volatile(0x77);
self.reg(PDMDLY).write_volatile(0);
let pdmcr = PDMCR_PDMEN | (0b00 << PDMCR_MICNBR_SHIFT) | PDMCR_CKEN1;
self.reg(PDMCR).write_volatile(pdmcr);
}
}
pub fn enable(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() | CR1_SAIEN);
}
}
pub fn disable(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() & !CR1_SAIEN);
while acr1.read_volatile() & CR1_SAIEN != 0 {}
}
}
pub fn enable_dma(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() | CR1_DMAEN);
}
}
pub fn disable_dma(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() & !CR1_DMAEN);
}
}
pub fn data_register_addr(&self) -> u32 {
self.base + ADR
}
pub fn clear_overrun(&self) -> bool {
unsafe {
let sr = self.reg(ASR).read_volatile();
if sr & SR_OVRUDR != 0 {
self.reg(ACLRFR).write_volatile(SR_OVRUDR);
true
} else {
false
}
}
}
#[inline(always)]
unsafe fn reg(&self, offset: u32) -> *mut u32 {
(self.base + offset) as *mut u32
}
}