From a36eab3aa1ef0e473423338ed5e2d8a571cf5142 Mon Sep 17 00:00:00 2001 From: ZacJW Date: Wed, 16 Jul 2025 08:10:24 +0100 Subject: [PATCH] WIP: Started microphone support --- Cargo.lock | 1 + Cargo.toml | 1 + src/lib.rs | 1 + src/microphone.rs | 38 ++++++ src/microphone/clock_calculation.rs | 193 ++++++++++++++++++++++++++++ 5 files changed, 234 insertions(+) create mode 100644 src/microphone.rs create mode 100644 src/microphone/clock_calculation.rs diff --git a/Cargo.lock b/Cargo.lock index eba6097..68b38d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,7 @@ dependencies = [ "bitmask-enum", "embedded-hal-bus", "esp-hal", + "esp32s3", "mipidsi", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index e938933..4a3a5c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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} diff --git a/src/lib.rs b/src/lib.rs index 1a7cf0a..f7558a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod display; pub mod keyboard; +pub mod microphone; #[cfg(test)] fn main() {} diff --git a/src/microphone.rs b/src/microphone.rs new file mode 100644 index 0000000..bb97807 --- /dev/null +++ b/src/microphone.rs @@ -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()); +} diff --git a/src/microphone/clock_calculation.rs b/src/microphone/clock_calculation.rs new file mode 100644 index 0000000..06c2dde --- /dev/null +++ b/src/microphone/clock_calculation.rs @@ -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) + }); +}