const SAI1_BASE: u32 = 0x4001_5800;
const GCR: u32 = 0x00;
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 BCR1: u32 = 0x24;
const BCR2: u32 = 0x28;
const BFRCR: u32 = 0x2C;
const BSLOTR: u32 = 0x30;
const BIM: u32 = 0x34;
const BSR: u32 = 0x38;
const BCLRFR: u32 = 0x3C;
const BDR: u32 = 0x40;
const PDMCR: u32 = 0x44;
const CR1_SAIEN: u32 = 1 << 16;
const CR1_DMAEN: u32 = 1 << 17;
const CR1_NODIV: u32 = 1 << 19;
const CR1_MCKEN: u32 = 1 << 27;
const CR1_MODE_MASTER_TX: u32 = 0b00;
const CR1_MODE_MASTER_RX: u32 = 0b01;
const CR1_MODE_SLAVE_TX: u32 = 0b10;
const CR1_MODE_SLAVE_RX: u32 = 0b11;
const CR1_DS_16BIT: u32 = 0b100 << 5;
const CR1_SYNCEN_ASYNC: u32 = 0b00 << 10;
const CR1_SYNCEN_INTERNAL: u32 = 0b01 << 10;
const CR1_OUTDRIV: u32 = 1 << 13;
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 FRCR_FSOFF: u32 = 1 << 18;
const SR_FREQ: u32 = 1 << 3; const SR_OVRUDR: u32 = 1 << 0;
const RCC_APB2ENR: u32 = 0x5802_44F0;
const RCC_D2CCIP1R: u32 = 0x5802_4D28;
pub struct Sai1Audio {
base: u32,
}
impl Sai1Audio {
pub fn new() -> Self {
Self { base: SAI1_BASE }
}
pub fn enable_clock(&self, clock_source: u8) {
unsafe {
let apb2 = RCC_APB2ENR as *mut u32;
apb2.write_volatile(apb2.read_volatile() | (1 << 22));
let _ = (apb2 as *const u32).read_volatile();
let d2ccip1r = RCC_D2CCIP1R as *mut u32;
let val = d2ccip1r.read_volatile();
d2ccip1r.write_volatile((val & !0b111) | ((clock_source as u32) & 0b111));
}
}
pub fn configure_tx(&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);
self.reg(GCR).write_volatile(0);
let cr1 = CR1_MODE_MASTER_TX
| CR1_DS_16BIT
| CR1_SYNCEN_ASYNC
| CR1_OUTDRIV
| CR1_MCKEN
| ((mckdiv as u32) << CR1_MCKDIV_SHIFT);
acr1.write_volatile(cr1);
acr2.write_volatile(CR2_FTH_QUARTER);
let frcr = 31u32 | (15u32 << 8) | FRCR_FSDEF
| FRCR_FSOFF;
self.reg(AFRCR).write_volatile(frcr);
let slotr = (1u32 << 8) | (0b11u32 << 16); self.reg(ASLOTR).write_volatile(slotr);
self.reg(AIM).write_volatile(0);
self.reg(ACLRFR).write_volatile(0x77);
}
}
pub fn configure_rx(&self) {
unsafe {
let bcr1 = self.reg(BCR1);
bcr1.write_volatile(bcr1.read_volatile() & !CR1_SAIEN);
while bcr1.read_volatile() & CR1_SAIEN != 0 {}
let bcr2 = self.reg(BCR2);
bcr2.write_volatile(CR2_FTH_QUARTER | CR2_FFLUSH);
let cr1 = CR1_MODE_SLAVE_RX | CR1_DS_16BIT | CR1_SYNCEN_INTERNAL;
bcr1.write_volatile(cr1);
bcr2.write_volatile(CR2_FTH_QUARTER);
let frcr = 31u32 | (15u32 << 8) | FRCR_FSDEF | FRCR_FSOFF;
self.reg(BFRCR).write_volatile(frcr);
let slotr = (1u32 << 8) | (0b11u32 << 16);
self.reg(BSLOTR).write_volatile(slotr);
self.reg(BIM).write_volatile(0);
self.reg(BCLRFR).write_volatile(0x77);
}
}
pub fn enable_tx(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() | CR1_SAIEN);
}
}
pub fn enable_rx(&self) {
unsafe {
let bcr1 = self.reg(BCR1);
bcr1.write_volatile(bcr1.read_volatile() | CR1_SAIEN);
}
}
pub fn disable_tx(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() & !CR1_SAIEN);
while acr1.read_volatile() & CR1_SAIEN != 0 {}
}
}
pub fn disable_rx(&self) {
unsafe {
let bcr1 = self.reg(BCR1);
bcr1.write_volatile(bcr1.read_volatile() & !CR1_SAIEN);
while bcr1.read_volatile() & CR1_SAIEN != 0 {}
}
}
pub fn tx_fifo_ready(&self) -> bool {
unsafe { self.reg(ASR).read_volatile() & SR_FREQ != 0 }
}
pub fn rx_fifo_ready(&self) -> bool {
unsafe { self.reg(BSR).read_volatile() & SR_FREQ != 0 }
}
pub fn write_sample(&self, sample: u32) {
while !self.tx_fifo_ready() {}
unsafe {
self.reg(ADR).write_volatile(sample);
}
}
pub fn read_sample(&self) -> u32 {
while !self.rx_fifo_ready() {}
unsafe { self.reg(BDR).read_volatile() }
}
pub fn enable_dma_tx(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() | CR1_DMAEN);
}
}
pub fn disable_dma_tx(&self) {
unsafe {
let acr1 = self.reg(ACR1);
acr1.write_volatile(acr1.read_volatile() & !CR1_DMAEN);
}
}
pub fn tx_data_register_addr(&self) -> u32 {
self.base + ADR
}
pub fn rx_data_register_addr(&self) -> u32 {
self.base + BDR
}
#[inline(always)]
unsafe fn reg(&self, offset: u32) -> *mut u32 {
(self.base + offset) as *mut u32
}
}