Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
a36eab3aa1 WIP: Started microphone support 2025-07-16 08:10:24 +01:00
5 changed files with 234 additions and 0 deletions

1
Cargo.lock generated
View file

@ -115,6 +115,7 @@ dependencies = [
"bitmask-enum",
"embedded-hal-bus",
"esp-hal",
"esp32s3",
"mipidsi",
"thiserror",
]

View file

@ -21,6 +21,7 @@ bitflags = "2.9.1"
bitmask-enum = "2.2.5"
embedded-hal-bus = "0.3.0"
esp-hal = {version = "=1.0.0-beta.1", features = ["esp32s3", "unstable"]}
esp32s3 = "0.32.0"
mipidsi = "0.9.0"
thiserror = {version = "2.0.11", default-features = false}

View file

@ -2,6 +2,7 @@
pub mod display;
pub mod keyboard;
pub mod microphone;
#[cfg(test)]
fn main() {}

38
src/microphone.rs Normal file
View file

@ -0,0 +1,38 @@
use crate::microphone::clock_calculation::{calculate_clock, set_clock};
mod clock_calculation;
fn mic(i2s0: esp_hal::peripherals::I2S0) {
drop(i2s0);
let i2s0 = unsafe { esp32s3::I2S0::steal() };
// clear clock config register
// i2s0.rx_clkm_conf().reset();
// i2s0.rx_conf().reset();
set_clock(
&i2s0,
calculate_clock(esp_hal::time::Rate::from_mhz(2), 1, 16),
);
let gpio = unsafe { esp32s3::GPIO::steal() };
gpio.func46_in_sel_cfg().modify(|_, w| w.)
// let io_mux = unsafe { esp32s3::IO_MUX::steal() };
// io_mux.gpio(46).modify(|_, w| w.)
i2s0.rx_conf()
.modify(|_, w| w.rx_reset().set_bit().rx_fifo_reset().set_bit());
i2s0.rx_conf()
.modify(|_, w| w.rx_reset().clear_bit().rx_fifo_reset().clear_bit());
i2s0.rx_conf().modify(|_, w| w.rx_ws_idle_pol().clear_bit());
i2s0.rx_conf1()
.modify(|_, w| unsafe { w.rx_half_sample_bits().bits(15).rx_bits_mod().bits(15) });
// Enable PDM mode
i2s0.rx_conf().modify(|_, w| w.rx_pdm_en().set_bit());
}

View file

@ -0,0 +1,193 @@
//! This module contains code lifted from esp-rs/esp-hal,
//! which has been slightly modified for use in this library.
//!
//! ## License
//!
//! Copyright 2021 esp-rs
//!
//! Permission is hereby granted, free of charge, to any
//! person obtaining a copy of this software and associated
//! documentation files (the "Software"), to deal in the
//! Software without restriction, including without
//! limitation the rights to use, copy, modify, merge,
//! publish, distribute, sublicense, and/or sell copies of
//! the Software, and to permit persons to whom the Software
//! is furnished to do so, subject to the following
//! conditions:
//!
//! The above copyright notice and this permission notice
//! shall be included in all copies or substantial portions
//! of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//! DEALINGS IN THE SOFTWARE.
use esp_hal::time::Rate;
pub(super) struct I2sClockDividers {
mclk_divider: u32,
bclk_divider: u32,
denominator: u32,
numerator: u32,
}
/// The default clock source for I2S operations.
const I2S_DEFAULT_CLK_SRC: u8 = 2;
const I2S_SCLK: u32 = 160_000_000;
const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 6;
const I2S_LL_MCLK_DIVIDER_MAX: usize = (1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1;
pub(super) fn calculate_clock(sample_rate: Rate, channels: u8, data_bits: u8) -> I2sClockDividers {
// this loosely corresponds to `i2s_std_calculate_clock` and
// `i2s_ll_tx_set_mclk` in esp-idf
//
// main difference is we are using fixed-point arithmetic here
// If data_bits is a power of two, use 256 as the mclk_multiple
// If data_bits is 24, use 192 (24 * 8) as the mclk_multiple
let mclk_multiple = if data_bits == 24 { 192 } else { 256 };
let sclk = I2S_SCLK; // for now it's fixed 160MHz
let rate = sample_rate.as_hz();
let bclk = rate * channels as u32 * data_bits as u32;
let mclk = rate * mclk_multiple;
let bclk_divider = mclk / bclk;
let mut mclk_divider = sclk / mclk;
let mut ma: u32;
let mut mb: u32;
let mut denominator: u32 = 0;
let mut numerator: u32 = 0;
let freq_diff = sclk.abs_diff(mclk * mclk_divider);
if freq_diff != 0 {
let decimal = freq_diff as u64 * 10000 / mclk as u64;
// Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 /
// 126.0
if decimal > 1250000 / 126 {
mclk_divider += 1;
} else {
let mut min: u32 = !0;
for a in 2..=I2S_LL_MCLK_DIVIDER_MAX {
let b = (a as u64) * (freq_diff as u64 * 10000u64 / mclk as u64) + 5000;
ma = ((freq_diff as u64 * 10000u64 * a as u64) / 10000) as u32;
mb = (mclk as u64 * (b / 10000)) as u32;
if ma == mb {
denominator = a as u32;
numerator = (b / 10000) as u32;
break;
}
if mb.abs_diff(ma) < min {
denominator = a as u32;
numerator = b as u32;
min = mb.abs_diff(ma);
}
}
}
}
I2sClockDividers {
mclk_divider,
bclk_divider,
denominator,
numerator,
}
}
pub(super) fn set_clock(i2s0: &esp32s3::I2S0, clock_settings: I2sClockDividers) {
let clkm_div_x: u32;
let clkm_div_y: u32;
let clkm_div_z: u32;
let clkm_div_yn1: u32;
if clock_settings.denominator == 0 || clock_settings.numerator == 0 {
clkm_div_x = 0;
clkm_div_y = 0;
clkm_div_z = 0;
clkm_div_yn1 = 1;
} else if clock_settings.numerator > clock_settings.denominator / 2 {
clkm_div_x = clock_settings
.denominator
.overflowing_div(
clock_settings
.denominator
.overflowing_sub(clock_settings.numerator)
.0,
)
.0
.overflowing_sub(1)
.0;
clkm_div_y = clock_settings.denominator
% (clock_settings
.denominator
.overflowing_sub(clock_settings.numerator)
.0);
clkm_div_z = clock_settings
.denominator
.overflowing_sub(clock_settings.numerator)
.0;
clkm_div_yn1 = 1;
} else {
clkm_div_x = clock_settings.denominator / clock_settings.numerator - 1;
clkm_div_y = clock_settings.denominator % clock_settings.numerator;
clkm_div_z = clock_settings.numerator;
clkm_div_yn1 = 0;
}
i2s0.tx_clkm_div_conf().modify(|_, w| unsafe {
w.tx_clkm_div_x().bits(clkm_div_x as u16);
w.tx_clkm_div_y().bits(clkm_div_y as u16);
w.tx_clkm_div_yn1().bit(clkm_div_yn1 != 0);
w.tx_clkm_div_z().bits(clkm_div_z as u16)
});
i2s0.tx_clkm_conf().modify(|_, w| unsafe {
w.clk_en().set_bit();
w.tx_clk_active().set_bit();
w.tx_clk_sel()
.bits(I2S_DEFAULT_CLK_SRC) // for now fixed at 160MHz
;
w.tx_clkm_div_num().bits(clock_settings.mclk_divider as u8)
});
i2s0.tx_conf1().modify(|_, w| unsafe {
w.tx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
i2s0.rx_clkm_div_conf().modify(|_, w| unsafe {
w.rx_clkm_div_x().bits(clkm_div_x as u16);
w.rx_clkm_div_y().bits(clkm_div_y as u16);
w.rx_clkm_div_yn1().bit(clkm_div_yn1 != 0);
w.rx_clkm_div_z().bits(clkm_div_z as u16)
});
i2s0.rx_clkm_conf().modify(|_, w| unsafe {
w.rx_clk_active().set_bit();
// for now fixed at 160MHz
w.rx_clk_sel()
.bits(I2S_DEFAULT_CLK_SRC);
w.rx_clkm_div_num().bits(clock_settings.mclk_divider as u8);
w.mclk_sel().bit(true)
});
i2s0.rx_conf1().modify(|_, w| unsafe {
w.rx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
}