From 367cab17061cac6608f45f67afdfe766552c3a55 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Fri, 12 Feb 2021 18:36:03 -0800 Subject: [PATCH 1/6] Add I2S class support Fixes #427 Adds a basic I2S class based off of the Arduino-SAMD core. The raw i2s_xxx functions are still a better way to use I2S due to their flexibility, but this will allow basic Arduino sketches to work. --- cores/esp8266/core_esp8266_i2s.cpp | 16 +- cores/esp8266/{i2s.h => core_esp8266_i2s.h} | 8 +- .../InputSerialPlotter/InputSerialPlotter.ino | 36 +++ .../I2S/examples/SimpleTone/SimpleTone.ino | 46 ++++ libraries/I2S/keywords.txt | 23 ++ libraries/I2S/library.properties | 9 + libraries/I2S/src/I2S.cpp | 209 ++++++++++++++++++ libraries/I2S/src/I2S.h | 86 +++++++ 8 files changed, 421 insertions(+), 12 deletions(-) rename cores/esp8266/{i2s.h => core_esp8266_i2s.h} (92%) create mode 100644 libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino create mode 100644 libraries/I2S/examples/SimpleTone/SimpleTone.ino create mode 100644 libraries/I2S/keywords.txt create mode 100644 libraries/I2S/library.properties create mode 100644 libraries/I2S/src/I2S.cpp create mode 100644 libraries/I2S/src/I2S.h diff --git a/cores/esp8266/core_esp8266_i2s.cpp b/cores/esp8266/core_esp8266_i2s.cpp index c50d5e2d8e..eb1d7d7e2f 100644 --- a/cores/esp8266/core_esp8266_i2s.cpp +++ b/cores/esp8266/core_esp8266_i2s.cpp @@ -24,7 +24,7 @@ #include "osapi.h" #include "ets_sys.h" #include "i2s_reg.h" -#include "i2s.h" +#include "core_esp8266_i2s.h" extern "C" { @@ -194,11 +194,11 @@ static void ICACHE_RAM_ATTR i2s_slc_isr(void) { } void i2s_set_callback(void (*callback) (void)) { - tx->callback = callback; + if (tx) tx->callback = callback; } void i2s_rx_set_callback(void (*callback) (void)) { - rx->callback = callback; + if (rx) rx->callback = callback; } static bool _alloc_channel(i2s_state_t *ch) { @@ -343,7 +343,7 @@ bool i2s_write_lr(int16_t left, int16_t right){ // writes a buffer of frames into the DMA memory, returns the amount of frames written // A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. -static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) { +static uint16_t _i2s_write_buffer(const int16_t *frames, uint16_t frame_count, bool mono, bool nb) { uint16_t frames_written=0; while(frame_count>0) { @@ -401,13 +401,13 @@ static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mo return frames_written; } -uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); } +uint16_t i2s_write_buffer_mono_nb(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); } -uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); } +uint16_t i2s_write_buffer_mono(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); } -uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); } +uint16_t i2s_write_buffer_nb(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); } -uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); } +uint16_t i2s_write_buffer(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); } bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) { if (!rx) { diff --git a/cores/esp8266/i2s.h b/cores/esp8266/core_esp8266_i2s.h similarity index 92% rename from cores/esp8266/i2s.h rename to cores/esp8266/core_esp8266_i2s.h index 581a916576..db572fa19c 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/core_esp8266_i2s.h @@ -69,10 +69,10 @@ void i2s_rx_set_callback(void (*callback) (void)); // writes a buffer of frames into the DMA memory, returns the amount of frames written // A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. -uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_mono(const int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_mono_nb(const int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer(const int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_nb(const int16_t *frames, uint16_t frame_count); #ifdef __cplusplus } diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino new file mode 100644 index 0000000000..edd3998da1 --- /dev/null +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -0,0 +1,36 @@ +/* + This example reads audio data from an Invensense's ICS43432 I2S microphone + breakout board, and prints out the samples to the Serial console. The + Serial Plotter built into the Arduino IDE can be used to plot the audio + data (Tools -> Serial Plotter) + created 17 November 2016 + by Sandeep Mistry + */ + +#include + +void setup() { + // Open serial communications and wait for port to open: + // A baud rate of 115200 is used instead of 9600 for a faster data rate + // on non-native USB ports + Serial.begin(115200); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start I2S at 8 kHz with 24-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 24)) { + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } +} + +void loop() { + // read a sample + int sample = I2S.read(); + + if (sample) { + // if it's non-zero print value to serial + Serial.println(sample); + } +} diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino new file mode 100644 index 0000000000..bfe2e9eb04 --- /dev/null +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -0,0 +1,46 @@ +/* + This example generates a square wave based tone at a specified frequency + and sample rate. Then outputs the data using the I2S interface to a + MAX08357 I2S Amp Breakout board. + + created 17 November 2016 + by Sandeep Mistry + modified for ESP8266 by Earle F. Philhower, III + */ + +#include + +const int frequency = 440; // frequency of square wave in Hz +const int amplitude = 500; // amplitude of square wave +const int sampleRate = 8000; // sample rate in Hz + +const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave + +short sample = amplitude; // current sample value +int count = 0; + +void setup() { + Serial.begin(115200); + Serial.println("I2S simple tone"); + + // start I2S at the sample rate with 16-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) { + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } +} + +void loop() { + if (count % halfWavelength == 0) { + // invert the sample every half wavelength count multiple to generate square wave + sample = -1 * sample; + } + + // write the same sample twice, once for left and once for the right channel + I2S.write(sample); + I2S.write(sample); + + // increment the counter for the next sample + count++; +} + diff --git a/libraries/I2S/keywords.txt b/libraries/I2S/keywords.txt new file mode 100644 index 0000000000..ad80bcb4c0 --- /dev/null +++ b/libraries/I2S/keywords.txt @@ -0,0 +1,23 @@ +####################################### +# Syntax Coloring Map I2S +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +I2S KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +begin KEYWORD2 +end KEYWORD2 + +onReceive KEYWORD2 +onTransmit KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +I2S_PHILIPS_MODE LITERAL1 diff --git a/libraries/I2S/library.properties b/libraries/I2S/library.properties new file mode 100644 index 0000000000..9f1d31f2fa --- /dev/null +++ b/libraries/I2S/library.properties @@ -0,0 +1,9 @@ +name=I2S +version=1.0 +author=Earle F. Philhower, III +maintainer=Earle F. Philhower, III +sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for ESP8266, based off of SAMD. +paragraph= +category=Communication +url=http://www.arduino.cc/en/Reference/I2S +architectures=esp8266 diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp new file mode 100644 index 0000000000..ebfcbeb219 --- /dev/null +++ b/libraries/I2S/src/I2S.cpp @@ -0,0 +1,209 @@ +/* + Based off of ArduinoCore-SAMD I2S interface. Modified for the + ESP8266 by Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "I2S.h" + +I2SClass::I2SClass(bool enableTransmit, bool enableRecv, bool driveClocks) { + _enableTx = enableTransmit; + _enableRx = enableRecv; + _driveClk = driveClocks; + _running = false; + _onTransmit = nullptr; + _onReceive = nullptr; + _havePeeked = 0; + _peekedData = 0; + _bps = 0; + _writtenHalf = false; +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) { + if ( _running || (mode != I2S_PHILIPS_MODE) || ( (bitsPerSample != 16) && (bitsPerSample != 24) ) ) { + return 0; + } + if (!i2s_rxtxdrive_begin(_enableRx, _enableTx, _driveClk, _driveClk)) { + return 0; + } + i2s_set_rate(sampleRate); + i2s_set_callback(_onTransmit); + i2s_rx_set_callback(_onReceive); + _bps = bitsPerSample; + _running = true; + return 1; +} + +void I2SClass::end() { + if (_running) { + i2s_end(); + } + i2s_set_callback(nullptr); + i2s_rx_set_callback(nullptr); + _running = false; +} + +void I2SClass::onTransmit(void(*fcn)(void)) { + i2s_set_callback(fcn); + _onTransmit = fcn; +} + +void I2SClass::onReceive(void(*fcn)(void)) { + i2s_rx_set_callback(fcn); + _onReceive = fcn; +} + +int I2SClass::available() { + if (!_running) return 0; + return i2s_rx_available(); +} + +int I2SClass::availableForWrite() { + if (!_running) return 0; + return i2s_available(); +} + +void I2SClass::flush() { + /* No-op */ +} + +int I2SClass::read() { + if (!_running) return -1; + if (_havePeeked) { + if (_bps == 16) { + _havePeeked--; + int ret = _peekedData; + _peekedData >>= 16; + return ret; + } else { + _havePeeked = 0; + return _peekedData; + } + } + // Avoid code duplication by just peeking and calling read() again + peek(); + return read(); +} + +int I2SClass::peek() { + if (!_running) return -1; + if (_havePeeked) { + if (_bps == 16) { + int16_t sample = (int16_t)_peekedData; // Will extends sign on return + return sample; + } else { + return _peekedData; + } + } + int16_t l, r; + i2s_read_sample(&l, &r, true); + _peekedData = ((int)l << 16) | (0xffff & (int)r); + _havePeeked = 2; // We now have 2 16-bit quantities which can also be used as 1 32-bit(24-bit) + if (_bps == 16) { + return r; + } else { + return _peekedData; + } +} + +int I2SClass::read(void *buffer, size_t size) { + if (!_running) return -1; + int cnt = 0; + + if ( ((_bps == 24) && (size % 4)) || ((_bps == 16) && (size % 2)) || (size < 2) ) { + return 0; // Invalid, can only read in units of samples + } + // Make sure any peeked data is consumed first + if (_havePeeked) { + if (_bps == 16) { + while (_havePeeked && size) { + uint16_t *p = (uint16_t *)buffer; + *(p++) = _peekedData; + _peekedData >>= 16; + _havePeeked--; + buffer = (void *)p; + size -= 2; + cnt += 2; + } + } else { + uint32_t *p = (uint32_t *)buffer; + *(p++) = _peekedData; + buffer = (void *)p; + size -= 4; + cnt += 4; + } + } + // Now just non-blocking read up to the remaining size + int16_t l, r; + int16_t *p = (int16_t *)buffer; + while (size && i2s_read_sample(&l, &r, false)) { + *(p++) = l; + size--; + cnt++; + if (size) { + *(p++) = r; + size--; + cnt++; + } else { + // We read a simple we can't return, stuff it in the peeked data + _havePeeked = 1; + _peekedData = r; + } + } + return cnt; +} + +size_t I2SClass::write(uint8_t s) { + if (!_running) return 0; + return write((int32_t)s); +} + +size_t I2SClass::write(const uint8_t *buffer, size_t size) { + return write((const void *)buffer, size); +} + +size_t I2SClass::write(int32_t s) { + if (!_running) return 0; + // Because our HW really wants 32b writes, store any 16b writes until another + // 16b write comes in and then send the combined write down. + if (_bps == 16) { + if (_writtenHalf) { + _writtenData <<= 16; + _writtenData |= 0xffff & s; + _writtenHalf = false; + return i2s_write_sample(_writtenData) ? 1 : 0; + } else { + _writtenHalf = true; + _writtenData = s & 0xffff; + return 1; + } + } else { + return i2s_write_sample((uint32_t)s) ? 1 : 0; + } +} + +// SAMD core has this as non-blocking +size_t I2SClass::write(const void *buffer, size_t size) { + if (!_running) return 0; + return i2s_write_buffer_nb((int16_t *)buffer, size / 2); +} + + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS) +I2SClass I2S; +#endif + diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h new file mode 100644 index 0000000000..ff1e9bb719 --- /dev/null +++ b/libraries/I2S/src/I2S.h @@ -0,0 +1,86 @@ +/* + Based off of ArduinoCore-SAMD I2S interface. Modified for the + ESP8266 by Earle F. Philhower, III + + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _I2S_H_INCLUDED +#define _I2S_H_INCLUDED + +#include +#include + +typedef enum { + I2S_PHILIPS_MODE // Only mode allowed for now by the core +} i2s_mode_t; + +class I2SClass : public Stream +{ +public: + // By default only transmit and drive the clock pins + I2SClass(bool enableTransmit = true, bool enableRecv = false, bool driveClocks = true); + + // Only 16 and 24 bps are allowed by the hardware + int begin(int mode, long sampleRate, int bitsPerSample); + void end(); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + + // from Print + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buffer, size_t size); + + virtual int availableForWrite(); + + int read(void* buffer, size_t size); + + size_t write(int32_t); + size_t write(const void *buffer, size_t size); + + // Note that these callback are called from **INTERRUPT CONTEXT** and hence + // must be both stored in IRAM and not perform anything that's not legal in + // an interrupt + void onTransmit(void(*)(void)); + void onReceive(void(*)(void)); + +private: + int _bps; + bool _running; + bool _enableTx; + bool _enableRx; + bool _driveClk; + void (*_onTransmit)(void); + void (*_onReceive)(void); + // Support for peek() on read path + uint32_t _peekedData; + int _havePeeked; + // Support for ::write(x) on 16b wuantities + uint32_t _writtenData; + bool _writtenHalf; +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS) +extern I2SClass I2S; +#endif + +#endif + From efe58a5b068bcf92047ebf8ab386306264d5a9b9 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Fri, 12 Feb 2021 18:45:58 -0800 Subject: [PATCH 2/6] Fix samples formatting --- .../InputSerialPlotter/InputSerialPlotter.ino | 14 +++++++------- libraries/I2S/examples/SimpleTone/SimpleTone.ino | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index edd3998da1..713440d73f 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -1,11 +1,11 @@ /* - This example reads audio data from an Invensense's ICS43432 I2S microphone - breakout board, and prints out the samples to the Serial console. The - Serial Plotter built into the Arduino IDE can be used to plot the audio - data (Tools -> Serial Plotter) - created 17 November 2016 - by Sandeep Mistry - */ + This example reads audio data from an Invensense's ICS43432 I2S microphone + breakout board, and prints out the samples to the Serial console. The + Serial Plotter built into the Arduino IDE can be used to plot the audio + data (Tools -> Serial Plotter) + created 17 November 2016 + by Sandeep Mistry +*/ #include diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index bfe2e9eb04..6959ae74ba 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -1,12 +1,12 @@ /* - This example generates a square wave based tone at a specified frequency - and sample rate. Then outputs the data using the I2S interface to a - MAX08357 I2S Amp Breakout board. - - created 17 November 2016 - by Sandeep Mistry - modified for ESP8266 by Earle F. Philhower, III - */ + This example generates a square wave based tone at a specified frequency + and sample rate. Then outputs the data using the I2S interface to a + MAX08357 I2S Amp Breakout board. + + created 17 November 2016 + by Sandeep Mistry + modified for ESP8266 by Earle F. Philhower, III +*/ #include From ad8de936eafc1f31f490739f4fa69ed74ff557cf Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sat, 13 Feb 2021 07:41:18 -0800 Subject: [PATCH 3/6] Add backward header compatibility --- cores/esp8266/i2s.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cores/esp8266/i2s.h diff --git a/cores/esp8266/i2s.h b/cores/esp8266/i2s.h new file mode 100644 index 0000000000..e3d40706cd --- /dev/null +++ b/cores/esp8266/i2s.h @@ -0,0 +1,12 @@ +// This include file is a hack to ensure backward compatibility with +// pre 3.0.0 versions of the core. There was a *lowercase* "i2s.h" +// header which was in this directory, now renamed to "core_esp82i66s.h" +// But, the I2S class has a header, "I2S.h" in uppercase. On Linux +// the two names are different, but on Windows it's case-insensitive +// so the names conflict. +// +// Avoid the issue by preserving the old i2s.h file and have it redirect +// to I2S.h which will give the ESP8266-specific functions as well as +// the generic I2S class. + +#include "../../libraries/I2S/src/I2S.h" From 7bf79d2f3a50d0376a8ee14901244a7e778a9d38 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 4 Mar 2021 07:59:00 -0800 Subject: [PATCH 4/6] Fix review issues, NO_GLOBAL ifdefs Use the enum type in the begin() method instead of generic int to specify I2S formatting mode. Remove tail recursion in the ::read() method. Typo fix the NO_GLOBAL_ define to check for _I2S not _LITTLEFS. --- libraries/I2S/src/I2S.cpp | 14 ++++++++------ libraries/I2S/src/I2S.h | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index ebfcbeb219..6220d97a5f 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -33,7 +33,7 @@ I2SClass::I2SClass(bool enableTransmit, bool enableRecv, bool driveClocks) { _writtenHalf = false; } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) { +int I2SClass::begin(i2s_mode_t mode, long sampleRate, int bitsPerSample) { if ( _running || (mode != I2S_PHILIPS_MODE) || ( (bitsPerSample != 16) && (bitsPerSample != 24) ) ) { return 0; } @@ -83,20 +83,22 @@ void I2SClass::flush() { int I2SClass::read() { if (!_running) return -1; + // Always just read from the peeked value to simplify operation + if (!_havePeeked) { + peek(); + } if (_havePeeked) { if (_bps == 16) { _havePeeked--; int ret = _peekedData; _peekedData >>= 16; return ret; - } else { + } else /* _bps == 24 */ { _havePeeked = 0; return _peekedData; } } - // Avoid code duplication by just peeking and calling read() again - peek(); - return read(); + return 0; } int I2SClass::peek() { @@ -203,7 +205,7 @@ size_t I2SClass::write(const void *buffer, size_t size) { } -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS) +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_I2S) I2SClass I2S; #endif diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index ff1e9bb719..0e5bc601b3 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -36,7 +36,7 @@ class I2SClass : public Stream I2SClass(bool enableTransmit = true, bool enableRecv = false, bool driveClocks = true); // Only 16 and 24 bps are allowed by the hardware - int begin(int mode, long sampleRate, int bitsPerSample); + int begin(i2s_mode_t mode, long sampleRate, int bitsPerSample); void end(); // from Stream @@ -78,7 +78,7 @@ class I2SClass : public Stream bool _writtenHalf; }; -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS) +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_I2S) extern I2SClass I2S; #endif From a93f39424df3abf4814500995eea8be30629170f Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 4 Mar 2021 16:43:28 -0800 Subject: [PATCH 5/6] Update comments per review --- libraries/I2S/src/I2S.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 0e5bc601b3..a90cc9a741 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -33,9 +33,11 @@ class I2SClass : public Stream { public: // By default only transmit and drive the clock pins - I2SClass(bool enableTransmit = true, bool enableRecv = false, bool driveClocks = true); + I2SClass(bool enableTransmit = true, bool enableRecv = false, + bool driveClocks = true); - // Only 16 and 24 bps are allowed by the hardware + // Only 16 and 24 bitsPerSample are allowed by the hardware + // 24-bit is MSB-aligned, with 0x00 in the lowest byte of each element. int begin(i2s_mode_t mode, long sampleRate, int bitsPerSample); void end(); @@ -45,15 +47,22 @@ class I2SClass : public Stream virtual int peek(); virtual void flush(); - // from Print + // from Print (see notes on write() below) virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buffer, size_t size); - virtual int availableForWrite(); + // Read up to size samples from the I2S device. Non-blocking, will read + // from 0...size samples and return the count read. Be sure your app handles + // the partial read case (i.e. yield()ing and trying to read more). int read(void* buffer, size_t size); + // Write a single sample to the I2S device. Blocking until write succeeds size_t write(int32_t); + // Write up to size samples to the I2S device. Non-blocking, will write + // from 0...size samples and return that count. Be sure your app handles + // partial writes (i.e. by yield()ing and then retrying to write the + // remaining data. size_t write(const void *buffer, size_t size); // Note that these callback are called from **INTERRUPT CONTEXT** and hence From 6a55158dfad35d333538c6e135933beb597c8fb4 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 4 Mar 2021 16:46:58 -0800 Subject: [PATCH 6/6] Update I2S.h --- libraries/I2S/src/I2S.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index a90cc9a741..08400d2997 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -43,11 +43,11 @@ class I2SClass : public Stream // from Stream virtual int available(); - virtual int read(); - virtual int peek(); + virtual int read(); // Blocking, will wait for incoming data + virtual int peek(); // Blocking, will wait for incoming data virtual void flush(); - // from Print (see notes on write() below) + // from Print (see notes on write() methods below) virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buffer, size_t size); virtual int availableForWrite();