From e0cfb5a9950a05f245d3ff2573bf2272525c89e9 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sat, 23 Jan 2021 14:40:29 -0800 Subject: [PATCH] Add 24-bit mode to I2S (#7835) Add basic 24 bit mode to the I2S API with a i2s_set_bits() call. By default 16b mode is still used, but if i2s_set_bits(24) is run before i2s_begin() then the HW will drive 24-bits of data. This data must be left-aligned (i.e. bits 31..8) in 4-byte samples. Fixes #5244 (the HW doesn't support 8 or 32 bits, only 16 or 24). --- cores/esp8266/core_esp8266_i2s.cpp | 17 ++++++++++++++++- cores/esp8266/i2s.h | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cores/esp8266/core_esp8266_i2s.cpp b/cores/esp8266/core_esp8266_i2s.cpp index dccd7c1df8..a18d9a3cc4 100644 --- a/cores/esp8266/core_esp8266_i2s.cpp +++ b/cores/esp8266/core_esp8266_i2s.cpp @@ -73,6 +73,7 @@ static i2s_state_t *tx = NULL; // Last I2S sample rate requested static uint32_t _i2s_sample_rate; +static int _i2s_bits = 16; // IOs used for I2S. Not defined in i2s.h, unfortunately. // Note these are internal GPIO numbers and not pins on an @@ -84,6 +85,14 @@ static uint32_t _i2s_sample_rate; #define I2SI_BCK 13 #define I2SI_WS 14 +bool i2s_set_bits(int bits) { + if (tx || rx || (bits != 16 && bits != 24)) { + return false; + } + _i2s_bits = bits; + return true; +} + static bool _i2s_is_full(const i2s_state_t *ch) { if (!ch) { return false; @@ -441,7 +450,7 @@ void i2s_set_rate(uint32_t rate) { //Rate in HZ } _i2s_sample_rate = rate; - uint32_t scaled_base_freq = I2SBASEFREQ/32; + uint32_t scaled_base_freq = I2SBASEFREQ / (_i2s_bits * 2); float delta_best = scaled_base_freq; uint8_t sbd_div_best=1; @@ -483,6 +492,9 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) { // div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this i2sc_temp |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD); + // Adjust the shift count for 16/24b output + i2sc_temp |= (_i2s_bits == 24 ? 8 : 0) << I2SBM; + I2SC = i2sc_temp; i2sc_temp &= ~(I2STXR); // Release reset @@ -549,6 +561,9 @@ bool i2s_rxtxdrive_begin(bool enableRx, bool enableTx, bool driveRxClocks, bool // I2STXFMM, I2SRXFMM=0 => 16-bit, dual channel data shifted in/out I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) + if (_i2s_bits == 24) { + I2SFC |= (2 << I2STXFM) | (2 << I2SRXFM); + } I2SFC |= I2SDE; // Enable DMA // I2STXCMM, I2SRXCMM=0 => Dual channel mode diff --git a/cores/esp8266/i2s.h b/cores/esp8266/i2s.h index f2978286af..581a916576 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/i2s.h @@ -27,6 +27,7 @@ How does this work? Basically, to get sound, you need to: - Connect an I2S codec to the I2S pins on the ESP. - Start up a thread that's going to do the sound output +- Call i2s_set_bits() if you want to enable 24-bit mode - Call i2s_begin() - Call i2s_set_rate() with the sample rate you want. - Generate sound and call i2s_write_sample() with 32-bit samples. @@ -42,6 +43,10 @@ speed. extern "C" { #endif +bool i2s_set_bits(int bits); // Set bits per sample, only 16 or 24 supported. Call before begin. +// Note that in 24 bit mode each sample must be left-aligned (i.e. 0x00000000 .. 0xffffff00) as the +// hardware shifts starting at bit 31, not bit 23. + void i2s_begin(); // Enable TX only, for compatibility bool i2s_rxtx_begin(bool enableRx, bool enableTx); // Allow TX and/or RX, returns false on OOM error bool i2s_rxtxdrive_begin(bool enableRx, bool enableTx, bool driveRxClocks, bool driveTxClocks);