From 185b7ef796ec0e581f6c3120c1aa780880af3f5a Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 18 Jun 2021 08:49:12 +0200 Subject: [PATCH 01/94] [FEATURE] Basic implementation of Arduino's I2S library --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 88 ++++ .../InputSerialPlotter/InputSerialPlotter.ino | 47 ++ .../I2S/examples/SimpleTone/SimpleTone.ino | 55 ++ libraries/I2S/keywords.txt | 28 + libraries/I2S/library.properties | 9 + libraries/I2S/src/I2S.cpp | 486 ++++++++++++++++++ libraries/I2S/src/I2S.h | 138 +++++ 7 files changed, 851 insertions(+) create mode 100644 libraries/I2S/examples/ADCPlotter/ADCPlotter.ino 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/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino new file mode 100644 index 00000000000..971172ead77 --- /dev/null +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -0,0 +1,88 @@ +/* + This example is only for ESP devices + This example demonstrates usage of integrated Digital to Analog Converter (DAC) + You can display sound wave from audio device, or just measure voltage. + + To display audio prepare circuit found in following link or drafted as ASCII art + https://forum.arduino.cc/index.php?topic=567581.0 + (!) Note that unlike in the link we are connecting the supply line to 3.3V (not 5V) + because ADC can measure only up to around 3V. Anything above 3V will be very inaccurate. + + ^ +3.3V + | + _ + | |10k + |_| + | | |10uF + GPIO34-------------*------------| |----------- line in +(Default ADC pin) | +| | + | + _ + | |10k + |_| + | + | + V GND + +Connect hot wire of your audio to Line in and GNd wire of audio cable to common ground (GND) + +Second option to measure voltage on trimmer / potentiometer has following connection + ^ +3.3V + | + _ + | | + GPIO34----------->| | +(Default ADC pin) |_| + | + | + _ + | | optional resistor + |_| + | + | + V GND +Optional resistor will decrease read value. + +Steps to run: +1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board +2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + On your board press and hold Boot button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. +3. Open plotter + Tools -> Serial Plotter + Enjoy + +Created by Tomas Pilny +on 17th June 2021 +*/ + +#include + +void setup() { + disableCore0WDT(); + disableCore1WDT(); + // 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 32-bits per sample + if (!I2S.begin(I2S_ADC_DAC, 8000, 16)) { + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } +} + +void loop() { + // read a sample + int sample = I2S.read(); + Serial.println(sample); + delay(100); + } diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino new file mode 100644 index 00000000000..3df8712bc64 --- /dev/null +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -0,0 +1,47 @@ +/* + 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) + + Circuit: + * Arduino/Genuino Zero, MKR family and Nano 33 IoT + * ICS43432: + * GND connected GND + * 3.3V connected to 3.3V (Zero, Nano, ESP32), VCC (MKR) + * WS connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32) + * CLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) + * SD connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + created 17 November 2016 + by Sandeep Mistry + */ + +#include + +void setup() { + disableCore0WDT(); + disableCore1WDT(); + // 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 32-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) { + 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 00000000000..2c88cad2981 --- /dev/null +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -0,0 +1,55 @@ +/* + 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. + + Circuit: + * Arduino/Genuino Zero, MKR family and Nano 33 IoT + * MAX08357: + * GND connected GND + * VIN connected 5V + * LRC connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32) + * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) + * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + + created 17 November 2016 + by Sandeep Mistry + */ + +#include + +const int frequency = 1250; // frequency of square wave in Hz +const int amplitude = 32767; // amplitude of square wave +const int sampleRate = 16000; // 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() { + disableCore0WDT(); + disableCore1WDT(); + 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 00000000000..217868dea36 --- /dev/null +++ b/libraries/I2S/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map I2S +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +I2S KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +begin KEYWORD2 +end KEYWORD2 + +onReceive KEYWORD2 +onTransmit KEYWORD2 + +setBufferSize KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +I2S_PHILIPS_MODE LITERAL1 +I2S_RIGHT_JUSTIFIED_MODE LITERAL1 +I2S_LEFT_JUSTIFIED_MODE LITERAL1 +I2S_ADC_DAC LITERAL1 diff --git a/libraries/I2S/library.properties b/libraries/I2S/library.properties new file mode 100644 index 00000000000..99ac3080db5 --- /dev/null +++ b/libraries/I2S/library.properties @@ -0,0 +1,9 @@ +name=I2S +version=1.0 +author=Arduino +maintainer=Arduino +sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for Arduino Zero. +paragraph= +category=Communication +url=http://www.arduino.cc/en/Reference/I2S +architectures=esp32 diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp new file mode 100644 index 00000000000..279a76db903 --- /dev/null +++ b/libraries/I2S/src/I2S.cpp @@ -0,0 +1,486 @@ +/* + 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 +*/ + +#include +#include +#include "I2S.h" + +int I2SClass::_beginCount = 0; + +I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : + _deviceIndex(0), + _clockGenerator(clockGenerator), + _sdPin(sdPin), // shared data pin + _inSdPin(sdPin), // input data pin + _outSdPin(sdPin), // output data pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin + + _state(I2S_STATE_IDLE), + _dmaChannel(-1), + _bitsPerSample(0), + _sampleRate(0), + _mode(I2S_PHILIPS_MODE), + + _dmaTransferInProgress(false), + _initialized(false), + _callbackTaskHandle(NULL), + _i2sEventQueue(NULL), + + _onTransmit(NULL), + _onReceive(NULL) +{ +} + +I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex + _deviceIndex(0), + _clockGenerator(clockGenerator), + _sdPin(inSdPin), // shared data pin + _inSdPin(inSdPin), // input data pin + _outSdPin(outSdPin), // output data pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin + + _state(I2S_STATE_DUPLEX), + _dmaChannel(-1), + _bitsPerSample(0), + _sampleRate(0), + _mode(I2S_PHILIPS_MODE), + + _dmaTransferInProgress(false), + _initialized(false), + _callbackTaskHandle(NULL), + _i2sEventQueue(NULL), + + _onTransmit(NULL), + _onReceive(NULL) +{ +} + +void I2SClass::createCallbackTask() +{ + int stack_size = 3000; + if(_callbackTaskHandle == NULL){ + _xCallbackEventBits = xEventGroupCreate(); + vTaskDelay(1); + xTaskCreate( + onDmaTransferComplete, // Function to implement the task + "onDmaTransferComplete", // Name of the task + stack_size, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_callbackTaskHandle // Task handle. + ); + } +} + +void I2SClass::destroyCallbackTask() +{ + if(_callbackTaskHandle != NULL){ + if(xTaskGetCurrentTaskHandle() == _callbackTaskHandle){ + return; + } + EventBits_t uxReturn; + int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_CMD_END_0); // Command callback task to finish and quit infinite loop + + // wait on confirmation from callback task that it exited infinite loop + uxReturn = xEventGroupWaitBits(_xCallbackEventBits, + _I2S_CALLBACK_TASK_END_CONFIRMED_1, + pdFALSE, // Don't clear received bits + pdFALSE, // logical OR for waiting bits + portMAX_DELAY); // wait indefinitely + if((uxReturn & _I2S_CALLBACK_TASK_END_CONFIRMED_1) == _I2S_CALLBACK_TASK_END_CONFIRMED_1){ + vTaskDelete(_callbackTaskHandle); + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + vEventGroupDelete(_xCallbackEventBits); + _xCallbackEventBits = NULL; + vTaskDelay(1); // memory cleanup + } // group bits - check confirmation + } // callback handle check +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) +{ + // master mode (driving clock and frame select pins - output) + return begin(mode, sampleRate, bitsPerSample, true); +} + +int I2SClass::begin(int mode, int bitsPerSample) +{ + Serial.println("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP"); + Serial.println("Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below"); + Serial.println("\tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); + return 0; // ERR + // slave mode (not driving clock and frame select pin - input) + //return begin(mode, 0, bitsPerSample, false); +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) +{ + if(_initialized){ + end(); + } + + if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + return 0; // ERR + } + + // TODO implement left / right justified modes + switch (mode) { + case I2S_PHILIPS_MODE: + //case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + //case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + case I2S_ADC_DAC: + break; + + case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + default: + // invalid mode + return 0; // ERR + } + + _mode = mode; + _sampleRate = sampleRate; + _bitsPerSample = bitsPerSample; + + esp_i2s::i2s_mode_t i2s_mode; + if(driveClock){ + i2s_mode = esp_i2s::I2S_MODE_MASTER; + }else{ + // TODO there will much more work with slave mode + i2s_mode = esp_i2s::I2S_MODE_SLAVE; + } + + if(_mode == I2S_ADC_DAC){ + if(bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + return 0; // ERR + } + + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_TX | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); + + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = _sampleRate, + .bits_per_sample = esp_i2s::I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 8, + .dma_buf_len = 512 // buffer length in Bytes + }; + + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)){ // Install and start i2s driver + return 0; // ERR + } + + esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; + esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // + esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); + esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ + Serial.println("i2s_set_pin err"); + return 0; // ERR + } + + esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); + esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); + esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); + + }else{ // normal I2S mode without ADC/DAC + if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ + Serial.println("I2S.begin(): invalid bits per second"); + // ESP does support 24 bps, however for the compatibility + // with original Arduino implementation it is not allowed + return 0; // ERR + } + + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); + esp_i2s::i2s_channel_fmt_t i2s_channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT; + esp_i2s::i2s_comm_format_t i2s_comm_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT); // 0x01 | 0x04 + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = _sampleRate, + .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t) _bitsPerSample, + .channel_format = i2s_channel_format, + .communication_format = i2s_comm_format, + .intr_alloc_flags = 1, + .dma_buf_count = 8, + .dma_buf_len = 512 // buffer length in Bytes + }; + + esp_i2s::i2s_pin_config_t pin_config; + if (_state == I2S_STATE_DUPLEX){ + pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + }else{ // half-duplex + pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_in_num = _sdPin + }; + } + + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)) { + Serial.println("i2s_driver_install err"); + return 0; // ERR + } + + if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { + Serial.println("i2s_set_pin err"); + end(); + return 0; // ERR + } + } // ADC/DAC or normal I2S mode + createCallbackTask(); + _initialized = true; + return 1; // OK +} + +void I2SClass::end() +{ + if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ + destroyCallbackTask(); + } + + if(_initialized){ + if(_mode == I2S_ADC_DAC){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + _initialized = false; + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } + } + _onTransmit = NULL; + _onReceive = NULL; +} + +// available to read +int I2SClass::available() +{ + // There is no actual way to tell in ESP + return 8; +} + +union i2s_sample_t { + uint8_t b8; + int16_t b16; + int32_t b32; +}; + +int I2SClass::read() +{ + i2s_sample_t sample; + sample.b32 = 0; + int bytes_read = read(&sample, _bitsPerSample / 8); + + if (_bitsPerSample == 32) { + return sample.b32; + } else if (_bitsPerSample == 16) { + return sample.b16; + } else if (_bitsPerSample == 8) { + return sample.b8; + } else { + return 0; + } + return 0; +} + +int I2SClass::read(void* buffer, size_t size) +{ + static long debug_timer_prev = 0; + if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { + if(!enableReceiver()){ + return 0; // There was an error switching to receiver + } + } + int read; + debug_timer_prev = millis(); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 10); + + if(_mode == I2S_ADC_DAC){ + for(int i = 0; i < read / 2; ++i){ + ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; + } + } + return read; +} + +/* +size_t I2SClass::write(int sample) +{ + return write((int32_t)sample); +} +*/ + +size_t I2SClass::write(int32_t sample) +{ + return write(&sample, 1); +} + +size_t I2SClass::write(const void *buffer, size_t size) +{ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { + if(!enableTransmitter()){ + return 0; // There was an error switching to transmitter + } + } + size_t bytes_written; + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 10); + return bytes_written; +} + +int I2SClass::peek() +{ + // TODO + Serial.println("I2SClass: peek() is not implemented for ESP"); + return 0; +} + +void I2SClass::flush() +{ + // do nothing, writes are DMA triggered in ESP +} + +size_t I2SClass::write(uint8_t data) +{ + return write((int32_t)data); +} + +size_t I2SClass::write(const uint8_t *buffer, size_t size) +{ + return write((const void*)buffer, size); +} + +int I2SClass::availableForWrite() +{ + // There is no actual way to tell in ESP + return 512; +} + +void I2SClass::onTransmit(void(*function)(void)) +{ + _onTransmit = function; +} + +void I2SClass::onReceive(void(*function)(void)) +{ + _onReceive = function; +} + +void I2SClass::setBufferSize(int bufferSize) +{ + Serial.println("I2SClass::setBufferSize() does nothing for ESP"); + // does nothing in ESP +} + +int I2SClass::enableTransmitter() +{ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _sdPin, + //.data_out_num = 26, // TODO + .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + _state = I2S_STATE_IDLE; + return 0; // ERR + } + _state = I2S_STATE_TRANSMITTER; + } + return 1; // Ok +} + +int I2SClass::enableReceiver() +{ + if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_in_num = _sdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + _state = I2S_STATE_IDLE; + return 0; // ERR + } + _state = I2S_STATE_RECEIVER; + } + return 1; // Ok +} + +void I2SClass::onTransferComplete() +{ + EventBits_t uxReturn; + esp_i2s::i2s_event_type_t i2s_event; + UBaseType_t uxHighWaterMark; // debug + while(true){ + if(_xCallbackEventBits != NULL){ + uxReturn = xEventGroupGetBits(_xCallbackEventBits); + if((uxReturn & _I2S_CALLBACK_TASK_CMD_END_0) == _I2S_CALLBACK_TASK_CMD_END_0){ + break; // from the infinite loop + } + } + + if(_i2sEventQueue != NULL){ + if(pdPASS == xQueueReceive(_i2sEventQueue, &i2s_event, 10)){ + if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + if(_onTransmit){ + _onTransmit(); + } + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ + if (_onReceive) { + _onReceive(); + } + } // if event TX or RX + } // event queue receive + } // queue not NULL + } // infinite loop + int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_END_CONFIRMED_1); // Notify end() function this task has properly exited infinite loop and is killing itself + while(true){ + // wait for death + } +} + +void I2SClass::onDmaTransferComplete(void*) +{ + I2S.onTransferComplete(); +} + +#if I2S_INTERFACES_COUNT > 0 + #ifdef ESP_PLATFORM + // change pins? + #define I2S_DEVICE 0 + #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP + #define PIN_I2S_SCK 5 + #define PIN_I2S_FS 25 + #define PIN_I2S_SD 26 + #define PIN_I2S_SD_OUT 35 + #endif +I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex +//I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex +#endif diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h new file mode 100644 index 00000000000..9934dd03797 --- /dev/null +++ b/libraries/I2S/src/I2S.h @@ -0,0 +1,138 @@ +/* + 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 +namespace esp_i2s { + #include "driver/i2s.h" // ESP specific i2s driver +} + + +#define I2S_HAS_SET_BUFFER_SIZE 1 + +#ifdef CONFIG_IDF_TARGET_ESP32 + #define I2S_INTERFACES_COUNT 2 +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32S2 + #define I2S_INTERFACES_COUNT 1 +#endif + +#define INCLUDE_xTaskGetCurrentTaskHandle 1 + +typedef enum { + I2S_PHILIPS_MODE, + I2S_RIGHT_JUSTIFIED_MODE, + I2S_LEFT_JUSTIFIED_MODE, + I2S_ADC_DAC +} i2s_mode_t; + +class I2SClass : public Stream +{ +public: + // the device index and pins must map to the "COM" pads in Table 6-1 of the datasheet + I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin); + I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin); // set duplex + // the SCK and FS pins are driven as outputs using the sample rate + int begin(int mode, long sampleRate, int bitsPerSample); + // the SCK and FS pins are inputs, other side controls sample rate + int begin(int mode, 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(int); + size_t write(int32_t); + size_t write(const void *buffer, size_t size); + + void onTransmit(void(*)(void)); + void onReceive(void(*)(void)); + + void setBufferSize(int bufferSize); +private: + int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock); + + int enableTransmitter(); + int enableReceiver(); + void onTransferComplete(); + + void destroyCallbackTask(); + void createCallbackTask(); + + static void onDmaTransferComplete(void*); + +private: + typedef enum { + I2S_STATE_IDLE, + I2S_STATE_TRANSMITTER, + I2S_STATE_RECEIVER, + I2S_STATE_DUPLEX + } i2s_state_t; + + static int _beginCount; + + uint8_t _deviceIndex; + uint8_t _clockGenerator; + uint8_t _sdPin; + uint8_t _inSdPin; + uint8_t _outSdPin; + uint8_t _sckPin; + uint8_t _fsPin; + + + i2s_state_t _state; + int _dmaChannel; + int _bitsPerSample; + long _sampleRate; + int _mode; + + volatile bool _dmaTransferInProgress; + + bool _initialized; + TaskHandle_t _callbackTaskHandle; + QueueHandle_t _i2sEventQueue; + + EventGroupHandle_t _xCallbackEventBits; + #define _I2S_CALLBACK_TASK_CMD_END_0 ( 1 << 0 ) + #define _I2S_CALLBACK_TASK_END_CONFIRMED_1 ( 1 << 1 ) + + void (*_onTransmit)(void); + void (*_onReceive)(void); +}; + +// "I2S" is already defined by the CMSIS device, undefine it so the I2SClass +// instance can be called I2S +#undef I2S + +extern I2SClass I2S; + +#endif From 2aabfb76bba2802f50b12f8b5db6a881a28b5105 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 18 Jun 2021 10:00:00 +0200 Subject: [PATCH 02/94] [MAINTENANCE] Added I2S to CMakeLists --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17b064c3a8f..0aecb1e9ec5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set(LIBRARY_SRCS libraries/FS/src/vfs_api.cpp libraries/HTTPClient/src/HTTPClient.cpp libraries/HTTPUpdate/src/HTTPUpdate.cpp + libraries/I2S/src/I2S.cpp libraries/LITTLEFS/src/LITTLEFS.cpp libraries/NetBIOS/src/NetBIOS.cpp libraries/Preferences/src/Preferences.cpp @@ -140,6 +141,7 @@ set(includedirs libraries/FS/src libraries/HTTPClient/src libraries/HTTPUpdate/src + libraries/I2S/src libraries/LITTLEFS/src libraries/NetBIOS/src libraries/Preferences/src From ca6f456a2cda7c0465281753c83b5620c6e37808 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 18 Jun 2021 12:05:54 +0200 Subject: [PATCH 03/94] [MAINTENANCE] Joined task blocking (i2s event queue + kill command) --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 2 - .../InputSerialPlotter/InputSerialPlotter.ino | 2 - .../I2S/examples/SimpleTone/SimpleTone.ino | 2 - libraries/I2S/src/I2S.cpp | 89 ++++++++----------- libraries/I2S/src/I2S.h | 9 +- 5 files changed, 39 insertions(+), 65 deletions(-) diff --git a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino index 971172ead77..10a12bdc06c 100644 --- a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -63,8 +63,6 @@ on 17th June 2021 #include void setup() { - disableCore0WDT(); - disableCore1WDT(); // 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 diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index 3df8712bc64..743b1c29175 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -19,8 +19,6 @@ #include void setup() { - disableCore0WDT(); - disableCore1WDT(); // 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 diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 2c88cad2981..36464148c33 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -28,8 +28,6 @@ short sample = amplitude; // current sample value int count = 0; void setup() { - disableCore0WDT(); - disableCore1WDT(); Serial.begin(115200); Serial.println("I2S simple tone"); diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 279a76db903..853e31ada1d 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -37,10 +37,10 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _sampleRate(0), _mode(I2S_PHILIPS_MODE), - _dmaTransferInProgress(false), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), + _task_kill_cmd_semaphore_handle(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -62,10 +62,10 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _sampleRate(0), _mode(I2S_PHILIPS_MODE), - _dmaTransferInProgress(false), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), + _task_kill_cmd_semaphore_handle(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -75,9 +75,10 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, void I2SClass::createCallbackTask() { int stack_size = 3000; - if(_callbackTaskHandle == NULL){ - _xCallbackEventBits = xEventGroupCreate(); - vTaskDelay(1); + if(_callbackTaskHandle == NULL){ + if(_task_kill_cmd_semaphore_handle == NULL){ + _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); + } xTaskCreate( onDmaTransferComplete, // Function to implement the task "onDmaTransferComplete", // Name of the task @@ -91,26 +92,8 @@ void I2SClass::createCallbackTask() void I2SClass::destroyCallbackTask() { - if(_callbackTaskHandle != NULL){ - if(xTaskGetCurrentTaskHandle() == _callbackTaskHandle){ - return; - } - EventBits_t uxReturn; - int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_CMD_END_0); // Command callback task to finish and quit infinite loop - - // wait on confirmation from callback task that it exited infinite loop - uxReturn = xEventGroupWaitBits(_xCallbackEventBits, - _I2S_CALLBACK_TASK_END_CONFIRMED_1, - pdFALSE, // Don't clear received bits - pdFALSE, // logical OR for waiting bits - portMAX_DELAY); // wait indefinitely - if((uxReturn & _I2S_CALLBACK_TASK_END_CONFIRMED_1) == _I2S_CALLBACK_TASK_END_CONFIRMED_1){ - vTaskDelete(_callbackTaskHandle); - _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task - vEventGroupDelete(_xCallbackEventBits); - _xCallbackEventBits = NULL; - vTaskDelay(1); // memory cleanup - } // group bits - check confirmation + if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ + xSemaphoreGive(_task_kill_cmd_semaphore_handle); } // callback handle check } @@ -185,8 +168,8 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc .dma_buf_len = 512 // buffer length in Bytes }; - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)){ // Install and start i2s driver - return 0; // ERR + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + return 0; // ERR } esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; @@ -435,35 +418,35 @@ int I2SClass::enableReceiver() void I2SClass::onTransferComplete() { - EventBits_t uxReturn; + static QueueSetHandle_t xQueueSet; + QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; - UBaseType_t uxHighWaterMark; // debug - while(true){ - if(_xCallbackEventBits != NULL){ - uxReturn = xEventGroupGetBits(_xCallbackEventBits); - if((uxReturn & _I2S_CALLBACK_TASK_CMD_END_0) == _I2S_CALLBACK_TASK_CMD_END_0){ - break; // from the infinite loop - } - } + EventBits_t uxReturn; + + xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); + configASSERT(xQueueSet); + xQueueAddToSet(_i2sEventQueue, xQueueSet); + xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); - if(_i2sEventQueue != NULL){ - if(pdPASS == xQueueReceive(_i2sEventQueue, &i2s_event, 10)){ - if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ - if(_onTransmit){ - _onTransmit(); - } - }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ - if (_onReceive) { - _onReceive(); - } - } // if event TX or RX - } // event queue receive - } // queue not NULL - } // infinite loop - int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_END_CONFIRMED_1); // Notify end() function this task has properly exited infinite loop and is killing itself while(true){ - // wait for death - } + xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); + if(xActivatedMember == _task_kill_cmd_semaphore_handle){ + xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); + break; // from the infinite loop + }else if(xActivatedMember == _i2sEventQueue){ + xQueueReceive(_i2sEventQueue, &i2s_event, 0); + if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + if(_onTransmit){ + _onTransmit(); + } + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ + if (_onReceive) { + _onReceive(); + } + } // if event TX or RX + } + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9934dd03797..994ec45788a 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -23,6 +23,7 @@ namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver } +#include "freertos/semphr.h" #define I2S_HAS_SET_BUFFER_SIZE 1 @@ -36,6 +37,7 @@ namespace esp_i2s { #endif #define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define _I2S_EVENT_QUEUE_LENGTH 10 typedef enum { I2S_PHILIPS_MODE, @@ -115,15 +117,10 @@ class I2SClass : public Stream long _sampleRate; int _mode; - volatile bool _dmaTransferInProgress; - bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; - - EventGroupHandle_t _xCallbackEventBits; - #define _I2S_CALLBACK_TASK_CMD_END_0 ( 1 << 0 ) - #define _I2S_CALLBACK_TASK_END_CONFIRMED_1 ( 1 << 1 ) + QueueHandle_t _task_kill_cmd_semaphore_handle; void (*_onTransmit)(void); void (*_onReceive)(void); From a3309d460a50e8fc6c1295216220b3f6fc1980c1 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 21 Jun 2021 17:11:12 +0200 Subject: [PATCH 04/94] [FEATURE] Added functions to set pins for both Input and Output --- libraries/I2S/src/I2S.cpp | 53 +++++++++++++++++++++++++++++++-------- libraries/I2S/src/I2S.h | 19 ++++++++------ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 853e31ada1d..227b9a838fc 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -26,8 +26,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _deviceIndex(0), _clockGenerator(clockGenerator), _sdPin(sdPin), // shared data pin - _inSdPin(sdPin), // input data pin - _outSdPin(sdPin), // output data pin + _inSdPin(-1), // input data pin + _outSdPin(-1), // output data pin _sckPin(sckPin), // clock pin _fsPin(fsPin), // frame (word) select pin @@ -208,14 +208,14 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc }; esp_i2s::i2s_pin_config_t pin_config; - if (_state == I2S_STATE_DUPLEX){ + if (_state == I2S_STATE_DUPLEX){ // duplex pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, .data_out_num = _outSdPin, .data_in_num = _inSdPin }; - }else{ // half-duplex + }else{ // simplex pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, @@ -240,6 +240,39 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc return 1; // OK } +int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ + _inSdPin = inSdPin; + _outSdPin = outSdPin; + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + return 0; // ERR + } + } + return 1; // OK +} + +int I2SClass::setSimplex(uint8_t sdPin){ + _sdPin = sdPin; + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_in_num = _sdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + return 0; // ERR + } + } + return 1; // OK +} + void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ @@ -385,8 +418,7 @@ int I2SClass::enableTransmitter() esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, - .data_out_num = _sdPin, - //.data_out_num = 26, // TODO + .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ @@ -405,7 +437,7 @@ int I2SClass::enableReceiver() .bck_io_num = _sckPin, .ws_io_num = _fsPin, .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, - .data_in_num = _sdPin + .data_in_num = _inSdPin != -1 ? _inSdPin : _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ _state = I2S_STATE_IDLE; @@ -446,6 +478,7 @@ void I2SClass::onTransferComplete() } // if event TX or RX } _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + } vTaskDelete(NULL); } @@ -456,14 +489,14 @@ void I2SClass::onDmaTransferComplete(void*) #if I2S_INTERFACES_COUNT > 0 #ifdef ESP_PLATFORM - // change pins? #define I2S_DEVICE 0 #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP #define PIN_I2S_SCK 5 #define PIN_I2S_FS 25 - #define PIN_I2S_SD 26 - #define PIN_I2S_SD_OUT 35 + #define PIN_I2S_SD 35 // ESP data in / codec data out (microphone) + #define PIN_I2S_SD_OUT 26 // ESP data out / codec data in (speakers) #endif + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex #endif diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 994ec45788a..9688a4712d4 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -56,6 +56,11 @@ class I2SClass : public Stream int begin(int mode, long sampleRate, int bitsPerSample); // the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); + + // change pin setup (default is Simplex) + int setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin); + int setSimplex(uint8_t sdPin); + void end(); // from Stream @@ -102,13 +107,13 @@ class I2SClass : public Stream static int _beginCount; - uint8_t _deviceIndex; - uint8_t _clockGenerator; - uint8_t _sdPin; - uint8_t _inSdPin; - uint8_t _outSdPin; - uint8_t _sckPin; - uint8_t _fsPin; + int _deviceIndex; + int _clockGenerator; + int _sdPin; + int _inSdPin; + int _outSdPin; + int _sckPin; + int _fsPin; i2s_state_t _state; From 3ad6d36fec8165b4ad8b03f3ec8d86dce1eb3d7c Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 24 Jun 2021 11:24:08 +0200 Subject: [PATCH 05/94] [FEATURE] Added new example demonstrating usage of callbacks --- .../I2S/examples/Callbacks/Callbacks.ino | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 libraries/I2S/examples/Callbacks/Callbacks.ino diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino new file mode 100644 index 00000000000..c896a8ed044 --- /dev/null +++ b/libraries/I2S/examples/Callbacks/Callbacks.ino @@ -0,0 +1,116 @@ +/* + This example is only for ESP devices. + + This example demonstrates usage of callback functions and duplex operation. + Callback functions allow you to perform audio events pseudo parallel to your main loop. + This way you no longer need to poll for reading or writing data from/to I2S module. + + Unlike Arduino, ESP allows you to operate input and output simultaneously on separate pins. + +Hardware: + 1. ESP board with at least 4 GPIOs available + 2. I2S mirophone (for example this one https://www.adafruit.com/product/3421) + 3. I2S decoder (for example this one https://www.adafruit.com/product/3678) + 4. Headphones, or speaker(s) to be connected into the decoder + 5. Some connecting wires, USB cable and optionally a breadboard + + Wiring: + Note: If you need to use other than default pins you can change the default definition below this comment block + 1. Connect pins of both the microphone and decoder to common SCK and FS pins on ESP + 1.a SCK (Source Clock, or Bit Clock) connects by default to pin 5 + 1.b FS (Frame Select, or Left-Right Select) connects by default to pin 25 + 2. Connect data pin of your microphone to pin 35 + 3. Connect data pin of your decoder to pin 26 + 4. Connect power pins and other remaining pins of your modules according to their specific instructions + 5. Connect headphones/speaker(s) to decoder + 6. Connect ESP board to your computer via USB cable + + Steps to run: + 1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board + 2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + If loading doesn't start automatically, you may need to press and hold Boot + button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. + 3. Open plotter + Tools -> Serial Plotter + 4. Enjoy + Listen to generated square wave signal, while observing the wave signal recorded by your microphone + both at the same time thanks to ESP duplex ability. + + Tip: + Install ArduinoSound library and discover extended functionality of I2S + you can try ESP WiFi telephone, Record on SD card and Play back from it... + +Created by Tomas Pilny +on 23rd June 2021 +*/ + + +#include +const int sampleRate = 16000; // sample rate in Hz +const int bitsPerSample = 16; + +// code from SimpleTone example +const int frequency = 1250; // frequency of square wave in Hz +const int amplitude = 32767; // amplitude of square wave + +const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave + +short sample = amplitude; // current sample value +int count = 0; + +void outputCallback(){ + 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++; +} + +// code from InputSerialPlotter example +void inputCallback(){ + // read a sample + int sample = I2S.read(); + + if (sample) { + // if it's non-zero print value to serial + Serial.println(sample); + } +} + +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 32-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) { + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } + I2S.setHalfDuplex(); // Use two data pins (default pins are input=35, output=26) + + // Register our callback functions + I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) + I2S.onReceive(inputCallback); // Function inputCallback will be called each time I2S finishes receive operation (audio input) + Serial.println("Callbacks example setup done."); +} + +void loop() { + // loop task remains free for other work + delay(100); // Let the FreeRTOS reset the watchDogTimer +} \ No newline at end of file From 12d926fcc8ecdcd3a7b15408514a917e7e55332f Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 28 Jun 2021 09:58:17 +0200 Subject: [PATCH 06/94] [MAINTENANCE] Minor changes --- .../I2S/examples/Callbacks/Callbacks.ino | 13 +- libraries/I2S/src/I2S.cpp | 338 ++++++++++++------ libraries/I2S/src/I2S.h | 31 +- 3 files changed, 243 insertions(+), 139 deletions(-) diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino index c896a8ed044..105ef64133b 100644 --- a/libraries/I2S/examples/Callbacks/Callbacks.ino +++ b/libraries/I2S/examples/Callbacks/Callbacks.ino @@ -49,6 +49,13 @@ Created by Tomas Pilny on 23rd June 2021 */ +// If you need to change any of the default pins, simply uncomment chosen line and change the pin number +/* +#define PIN_I2S_SCK 33 +#define PIN_I2S_FS 16 +#define PIN_I2S_SD 17 +#define PIN_I2S_SD_OUT 4 +*/ #include const int sampleRate = 16000; // sample rate in Hz @@ -82,6 +89,8 @@ void inputCallback(){ // read a sample int sample = I2S.read(); + Serial.println(sample); // only for debug + if (sample) { // if it's non-zero print value to serial Serial.println(sample); @@ -100,9 +109,9 @@ void setup() { // start I2S at 8 kHz with 32-bits per sample if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) { Serial.println("Failed to initialize I2S!"); - while (1); // do nothing + while (1) delay(100); // do nothing } - I2S.setHalfDuplex(); // Use two data pins (default pins are input=35, output=26) + I2S.setAllPins(); // Register our callback functions I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 227b9a838fc..c7c8f139eac 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -19,12 +19,51 @@ #include #include #include "I2S.h" +#include "freertos/semphr.h" + +//#define _USE_SYS_VIEW 1 +#ifdef _USE_SYS_VIEW + #include "esp_sysview_trace.h" // debug + //#include "esp_heap_trace.h" // debug + #include "esp_log.h" // debug +#endif // _USE_SYS_VIEW + +#define _I2S_EVENT_QUEUE_LENGTH 10 +#define _I2S_DMA_BUFFER_SIZE 512 +#define _I2S_DMA_BUFFER_COUNT 8 +#define I2S_INTERFACES_COUNT SOC_I2S_NUM + +#ifndef I2S_DEVICE + #define I2S_DEVICE 0 +#endif + +#ifndef I2S_CLOCK_GENERATOR + #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP +#endif + +#ifndef PIN_I2S_SCK + #define PIN_I2S_SCK 5 +#endif + +#ifndef PIN_I2S_FS + #define PIN_I2S_FS 25 +#endif + +#ifndef PIN_I2S_SD + #define PIN_I2S_SD 26 +#endif + +#ifndef PIN_I2S_SD_IN + #define PIN_I2S_SD_IN 35 // 35 can be only input pin +#endif + -int I2SClass::_beginCount = 0; +#ifndef PIN_I2S_SD_OUT + #define PIN_I2S_SD_OUT 26 +#endif I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : - _deviceIndex(0), - _clockGenerator(clockGenerator), + _deviceIndex(deviceIndex), _sdPin(sdPin), // shared data pin _inSdPin(-1), // input data pin _outSdPin(-1), // output data pin @@ -32,7 +71,6 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_IDLE), - _dmaChannel(-1), _bitsPerSample(0), _sampleRate(0), _mode(I2S_PHILIPS_MODE), @@ -48,8 +86,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u } I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex - _deviceIndex(0), - _clockGenerator(clockGenerator), + _deviceIndex(deviceIndex), _sdPin(inSdPin), // shared data pin _inSdPin(inSdPin), // input data pin _outSdPin(outSdPin), // output data pin @@ -57,7 +94,6 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_DUPLEX), - _dmaChannel(-1), _bitsPerSample(0), _sampleRate(0), _mode(I2S_PHILIPS_MODE), @@ -84,7 +120,7 @@ void I2SClass::createCallbackTask() "onDmaTransferComplete", // Name of the task stack_size, // Stack size in words NULL, // Task input parameter - 1, // Priority of the task + 2, // Priority of the task &_callbackTaskHandle // Task handle. ); } @@ -120,14 +156,13 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + Serial.println("ERROR I2SClass::begin invalid state"); // debug return 0; // ERR } // TODO implement left / right justified modes switch (mode) { case I2S_PHILIPS_MODE: - //case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - //case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP case I2S_ADC_DAC: break; @@ -135,111 +170,154 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP default: // invalid mode + Serial.println("ERROR I2SClass::begin invalid mode"); // debug return 0; // ERR } _mode = mode; _sampleRate = sampleRate; _bitsPerSample = bitsPerSample; + esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); - esp_i2s::i2s_mode_t i2s_mode; if(driveClock){ - i2s_mode = esp_i2s::I2S_MODE_MASTER; + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER); }else{ // TODO there will much more work with slave mode - i2s_mode = esp_i2s::I2S_MODE_SLAVE; + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } + esp_i2s::i2s_pin_config_t pin_config; + pin_config.bck_io_num = _sckPin; + pin_config.ws_io_num = _fsPin; + Serial.println("I2SClass::begin foo"); // debug if(_mode == I2S_ADC_DAC){ - if(bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - return 0; // ERR - } - - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_TX | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); - - esp_i2s::i2s_config_t i2s_config = { - .mode = i2s_mode, - .sample_rate = _sampleRate, - .bits_per_sample = esp_i2s::I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 8, - .dma_buf_len = 512 // buffer length in Bytes - }; - - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + Serial.println("I2SClass::begin foo adc"); // debug + if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + Serial.println("ERROR I2SClass::begin invalid bps for ADC/DAC"); // debug return 0; // ERR } - - esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; - esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // - esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); - esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ - Serial.println("i2s_set_pin err"); - return 0; // ERR - } - - esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); - esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); - esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); - - }else{ // normal I2S mode without ADC/DAC + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); + }else{ // End of ADC/DAC mode; start of Normal mode + Serial.println("I2SClass::begin foo normal"); // debug if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ - Serial.println("I2S.begin(): invalid bits per second"); + Serial.println("I2S.begin(): invalid bits per second for normal mode"); // ESP does support 24 bps, however for the compatibility // with original Arduino implementation it is not allowed return 0; // ERR } - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); - esp_i2s::i2s_channel_fmt_t i2s_channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT; - esp_i2s::i2s_comm_format_t i2s_comm_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT); // 0x01 | 0x04 - esp_i2s::i2s_config_t i2s_config = { - .mode = i2s_mode, - .sample_rate = _sampleRate, - .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t) _bitsPerSample, - .channel_format = i2s_channel_format, - .communication_format = i2s_comm_format, - .intr_alloc_flags = 1, - .dma_buf_count = 8, - .dma_buf_len = 512 // buffer length in Bytes - }; - esp_i2s::i2s_pin_config_t pin_config; if (_state == I2S_STATE_DUPLEX){ // duplex pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, .data_out_num = _outSdPin, .data_in_num = _inSdPin }; }else{ // simplex pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_out_num = I2S_PIN_NO_CHANGE, .data_in_num = _sdPin }; } - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)) { - Serial.println("i2s_driver_install err"); + } // Normal mode + Serial.println("I2SClass::begin bar"); // debug + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = _sampleRate, + .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, + .dma_buf_count = _I2S_DMA_BUFFER_COUNT, + .dma_buf_len = _I2S_DMA_BUFFER_SIZE + }; + + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + Serial.println("ERROR I2SClass::begin error installing i2s driver"); // debug + Serial.flush(); + delay(100); + return 0; // ERR + } + + if(_mode == I2S_ADC_DAC){ + esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; + esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // + esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); + esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ + Serial.println("i2s_set_pin err"); return 0; // ERR } + esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); + esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); + esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); + }else{ // End of ADC/DAC mode; start of Normal mode + Serial.println("I2SClass::begin calling set"); // debug if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { Serial.println("i2s_set_pin err"); end(); return 0; // ERR } - } // ADC/DAC or normal I2S mode + } createCallbackTask(); _initialized = true; return 1; // OK } +int I2SClass::setAllPins(){ + return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); +} + +int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ + if(sckPin >= 0){ + _sckPin = sckPin; + }else{ + _sckPin = PIN_I2S_SCK; + } + + if(fsPin >= 0){ + _fsPin = fsPin; + }else{ + _fsPin = PIN_I2S_FS; + } + + if(inSdPin >= 0){ + _inSdPin = inSdPin; + }else{ + _inSdPin = PIN_I2S_SD; + } + + if(outSdPin >= 0){ + _outSdPin = outSdPin; + }else{ + _outSdPin = PIN_I2S_SD_OUT; + } + + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + Serial.printf("setFullDuplex() set pins error\n"); // debug + return 0; // ERR + } + } + return 1; // OK +} + +int I2SClass::setStateDuplex(){ + if(_inSdPin < 0 || _outSdPin < 0){ + return 0; // ERR + } + _state = I2S_STATE_DUPLEX; + return 1; +} + +//int I2SClass::setHalfDuplex(uint8_t inSdPin=PIN_I2S_SD, uint8_t outSdPin=PIN_I2S_SD_OUT){ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ _inSdPin = inSdPin; _outSdPin = outSdPin; @@ -251,24 +329,30 @@ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + Serial.printf("setFullDuplex() set pins error\n"); // debug return 0; // ERR } + Serial.printf("setFullDuplex(inSdPin=%d, outSdPin=%d) OK\n", inSdPin, outSdPin); // debug } return 1; // OK } +//int I2SClass::setSimplex(uint8_t sdPin=PIN_I2S_SD){ int I2SClass::setSimplex(uint8_t sdPin){ _sdPin = sdPin; if(_initialized){ esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, - .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + //.data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_out_num = I2S_PIN_NO_CHANGE, .data_in_num = _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + Serial.printf("setHalfDuplex: err setting pin %d\n", sdPin); // debug return 0; // ERR } + Serial.printf("setHalfDuplex(sdPin=%d) OK\n", sdPin); // debug } return 1; // OK } @@ -277,27 +361,30 @@ void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ destroyCallbackTask(); - } - if(_initialized){ - if(_mode == I2S_ADC_DAC){ - esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); - } - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); - _initialized = false; - if(_state != I2S_STATE_DUPLEX){ - _state = I2S_STATE_IDLE; + if(_initialized){ + if(_mode == I2S_ADC_DAC){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + _initialized = false; + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } } + _onTransmit = NULL; + _onReceive = NULL; + }else{ + // TODO log_e with error - destroy task from inside not permitted } - _onTransmit = NULL; - _onReceive = NULL; } // available to read int I2SClass::available() { // There is no actual way to tell in ESP - return 8; + return _I2S_DMA_BUFFER_SIZE; + //uxQueueSpacesAvailable(); } union i2s_sample_t { @@ -310,7 +397,8 @@ int I2SClass::read() { i2s_sample_t sample; sample.b32 = 0; - int bytes_read = read(&sample, _bitsPerSample / 8); + //int bytes_read = read(&sample, _bitsPerSample / 8); + read(&sample, _bitsPerSample / 8); if (_bitsPerSample == 32) { return sample.b32; @@ -326,15 +414,19 @@ int I2SClass::read() int I2SClass::read(void* buffer, size_t size) { - static long debug_timer_prev = 0; if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { if(!enableReceiver()){ return 0; // There was an error switching to receiver } } int read; - debug_timer_prev = millis(); - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 10); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_read start"); +#endif // _USE_SYS_VIEW + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_read stop"); +#endif // _USE_SYS_VIEW if(_mode == I2S_ADC_DAC){ for(int i = 0; i < read / 2; ++i){ @@ -353,31 +445,38 @@ size_t I2SClass::write(int sample) size_t I2SClass::write(int32_t sample) { - return write(&sample, 1); + return write(&sample, _bitsPerSample/8); } size_t I2SClass::write(const void *buffer, size_t size) { + //Serial.printf("I2SClass::write(): _state = %d\n",_state); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!enableTransmitter()){ return 0; // There was an error switching to transmitter } } size_t bytes_written; - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 10); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_write start"); +#endif // _USE_SYS_VIEW + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_write stop"); +#endif // _USE_SYS_VIEW return bytes_written; } int I2SClass::peek() { // TODO - Serial.println("I2SClass: peek() is not implemented for ESP"); + // peek() is not implemented for ESP return 0; } void I2SClass::flush() { - // do nothing, writes are DMA triggered in ESP + // do nothing, writes are DMA triggered } size_t I2SClass::write(uint8_t data) @@ -393,7 +492,8 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) int I2SClass::availableForWrite() { // There is no actual way to tell in ESP - return 512; + return _I2S_DMA_BUFFER_SIZE; + //uxQueueSpacesAvailable(); } void I2SClass::onTransmit(void(*function)(void)) @@ -408,18 +508,19 @@ void I2SClass::onReceive(void(*function)(void)) void I2SClass::setBufferSize(int bufferSize) { - Serial.println("I2SClass::setBufferSize() does nothing for ESP"); // does nothing in ESP } int I2SClass::enableTransmitter() { - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, - .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, + Serial.printf("I2SClass::enableTransmitter(): _state = %d\n",_state); + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ + Serial.printf("I2SClass::enableTransmitter(): change data out pin to %d\n",_sdPin); + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, + .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ _state = I2S_STATE_IDLE; @@ -453,7 +554,6 @@ void I2SClass::onTransferComplete() static QueueSetHandle_t xQueueSet; QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; - EventBits_t uxReturn; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -461,42 +561,56 @@ void I2SClass::onTransferComplete() xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ + //Serial.printf("I2S:onTransferComplete: loop start\n"); xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); if(xActivatedMember == _task_kill_cmd_semaphore_handle){ + Serial.printf("I2S:onTransferComplete: received kill command, breaking from loop\n"); xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop }else if(xActivatedMember == _i2sEventQueue){ + //Serial.printf("I2S:onTransferComplete: received Q\n"); xQueueReceive(_i2sEventQueue, &i2s_event, 0); if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ if(_onTransmit){ + //Serial.printf("I2S:onTransferComplete: calling _onTransmit\n"); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onTransmit start"); +#endif // _USE_SYS_VIEW _onTransmit(); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onTransmit stop"); +#endif // _USE_SYS_VIEW } }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ if (_onReceive) { + //Serial.printf("I2S:onTransferComplete: calling _onReceive\n"); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onReceive start"); +#endif // _USE_SYS_VIEW _onReceive(); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onReceive stop"); +#endif // _USE_SYS_VIEW } } // if event TX or RX } - _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } + Serial.printf("xxxxxxx I2S:onTransferComplete: out of loop - kill my self xxxxxxx\n"); + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); + I2S.onTransferComplete(); } #if I2S_INTERFACES_COUNT > 0 - #ifdef ESP_PLATFORM - #define I2S_DEVICE 0 - #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP - #define PIN_I2S_SCK 5 - #define PIN_I2S_FS 25 - #define PIN_I2S_SD 35 // ESP data in / codec data out (microphone) - #define PIN_I2S_SD_OUT 26 // ESP data out / codec data in (speakers) - #endif - -I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex -//I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex + //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex #endif + +#if I2S_INTERFACES_COUNT > 1 + // TODO set default pins for second module + //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex +#endif \ No newline at end of file diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9688a4712d4..2624e1afc17 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -20,24 +20,10 @@ #define _I2S_H_INCLUDED #include + namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver } -#include "freertos/semphr.h" - - -#define I2S_HAS_SET_BUFFER_SIZE 1 - -#ifdef CONFIG_IDF_TARGET_ESP32 - #define I2S_INTERFACES_COUNT 2 -#endif - -#ifdef CONFIG_IDF_TARGET_ESP32S2 - #define I2S_INTERFACES_COUNT 1 -#endif - -#define INCLUDE_xTaskGetCurrentTaskHandle 1 -#define _I2S_EVENT_QUEUE_LENGTH 10 typedef enum { I2S_PHILIPS_MODE, @@ -57,7 +43,11 @@ class I2SClass : public Stream // the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); - // change pin setup (default is Simplex) + // change pin setup and mode (default is Half Duplex) + // Can be called only on initialized object (after begin) + int setStateDuplex(); + int setAllPins(); + int setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin); int setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin); int setSimplex(uint8_t sdPin); @@ -105,19 +95,14 @@ class I2SClass : public Stream I2S_STATE_DUPLEX } i2s_state_t; - static int _beginCount; - int _deviceIndex; - int _clockGenerator; int _sdPin; int _inSdPin; int _outSdPin; int _sckPin; int _fsPin; - i2s_state_t _state; - int _dmaChannel; int _bitsPerSample; long _sampleRate; int _mode; @@ -131,10 +116,6 @@ class I2SClass : public Stream void (*_onReceive)(void); }; -// "I2S" is already defined by the CMSIS device, undefine it so the I2SClass -// instance can be called I2S -#undef I2S - extern I2SClass I2S; #endif From d7eb3f397546105098adbe41f2cdf88403976ec4 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 7 Jul 2021 14:51:26 +0200 Subject: [PATCH 07/94] [BACKUP] Work in progress on overlaying buffer (crashing) --- libraries/I2S/src/I2S.cpp | 188 +++++++++++++++++++++++--------------- libraries/I2S/src/I2S.h | 9 ++ 2 files changed, 122 insertions(+), 75 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c7c8f139eac..82d8a390f67 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,16 +21,11 @@ #include "I2S.h" #include "freertos/semphr.h" -//#define _USE_SYS_VIEW 1 -#ifdef _USE_SYS_VIEW - #include "esp_sysview_trace.h" // debug - //#include "esp_heap_trace.h" // debug - #include "esp_log.h" // debug -#endif // _USE_SYS_VIEW +#define _I2S_EVENT_QUEUE_LENGTH 100 + +#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 +#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER count must be between 2 and 128 -#define _I2S_EVENT_QUEUE_LENGTH 10 -#define _I2S_DMA_BUFFER_SIZE 512 -#define _I2S_DMA_BUFFER_COUNT 8 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -53,11 +48,11 @@ #define PIN_I2S_SD 26 #endif + #ifndef PIN_I2S_SD_IN #define PIN_I2S_SD_IN 35 // 35 can be only input pin #endif - #ifndef PIN_I2S_SD_OUT #define PIN_I2S_SD_OUT 26 #endif @@ -75,6 +70,15 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _sampleRate(0), _mode(I2S_PHILIPS_MODE), + _buffer_byte_size(0), + _output_buffer_pointer(0), + _input_buffer_pointer(0), + _read_available(0), + _in_buf_semaphore(NULL), + _out_buf_semaphore(NULL), + _inputBuffer(NULL), + _outputBuffer(NULL), + _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), @@ -98,6 +102,15 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _sampleRate(0), _mode(I2S_PHILIPS_MODE), + _buffer_byte_size(0), + _output_buffer_pointer(0), + _input_buffer_pointer(0), + _read_available(0), + _in_buf_semaphore(NULL), + _out_buf_semaphore(NULL), + _inputBuffer(NULL), + _outputBuffer(NULL), + _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), @@ -110,7 +123,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, void I2SClass::createCallbackTask() { - int stack_size = 3000; + int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); @@ -189,16 +202,13 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc pin_config.bck_io_num = _sckPin; pin_config.ws_io_num = _fsPin; - Serial.println("I2SClass::begin foo"); // debug if(_mode == I2S_ADC_DAC){ - Serial.println("I2SClass::begin foo adc"); // debug if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode Serial.println("ERROR I2SClass::begin invalid bps for ADC/DAC"); // debug return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); }else{ // End of ADC/DAC mode; start of Normal mode - Serial.println("I2SClass::begin foo normal"); // debug if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ Serial.println("I2S.begin(): invalid bits per second for normal mode"); // ESP does support 24 bps, however for the compatibility @@ -220,22 +230,39 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } } // Normal mode - Serial.println("I2SClass::begin bar"); // debug esp_i2s::i2s_config_t i2s_config = { .mode = i2s_mode, .sample_rate = _sampleRate, .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _I2S_DMA_BUFFER_SIZE }; + + _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8); + _inputBuffer = malloc(_buffer_byte_size); + _outputBuffer = malloc(_buffer_byte_size); + _output_buffer_pointer = 0; + _input_buffer_pointer = 0; + if(_inputBuffer == NULL || _outputBuffer == NULL){ + return 0; // ERR + } + _in_buf_semaphore = xSemaphoreCreateMutex(); + _out_buf_semaphore = xSemaphoreCreateMutex(); + //_in_buf_semaphore = xSemaphoreCreateBinary(); + //_out_buf_semaphore = xSemaphoreCreateBinary(); + + if(_in_buf_semaphore == NULL || _out_buf_semaphore == NULL){ + return 0; // ERR + } + //xSemaphoreGive(_in_buf_semaphore); + //xSemaphoreGive(_out_buf_semaphore); + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver Serial.println("ERROR I2SClass::begin error installing i2s driver"); // debug - Serial.flush(); - delay(100); return 0; // ERR } @@ -253,7 +280,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); }else{ // End of ADC/DAC mode; start of Normal mode - Serial.println("I2SClass::begin calling set"); // debug if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { Serial.println("i2s_set_pin err"); end(); @@ -262,6 +288,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } createCallbackTask(); _initialized = true; + return 1; // OK } @@ -302,7 +329,6 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - Serial.printf("setFullDuplex() set pins error\n"); // debug return 0; // ERR } } @@ -329,10 +355,8 @@ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - Serial.printf("setFullDuplex() set pins error\n"); // debug return 0; // ERR } - Serial.printf("setFullDuplex(inSdPin=%d, outSdPin=%d) OK\n", inSdPin, outSdPin); // debug } return 1; // OK } @@ -349,10 +373,8 @@ int I2SClass::setSimplex(uint8_t sdPin){ .data_in_num = _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - Serial.printf("setHalfDuplex: err setting pin %d\n", sdPin); // debug return 0; // ERR } - Serial.printf("setHalfDuplex(sdPin=%d) OK\n", sdPin); // debug } return 1; // OK } @@ -374,6 +396,18 @@ void I2SClass::end() } _onTransmit = NULL; _onReceive = NULL; + free(_inputBuffer); + free(_outputBuffer); + _inputBuffer = NULL; + _outputBuffer = NULL; + _buffer_byte_size = 0; + _output_buffer_pointer = 0; + _input_buffer_pointer = 0; + vSemaphoreDelete(_in_buf_semaphore); + vSemaphoreDelete(_out_buf_semaphore); + _in_buf_semaphore = NULL; + _out_buf_semaphore = NULL; + }else{ // TODO log_e with error - destroy task from inside not permitted } @@ -382,9 +416,7 @@ void I2SClass::end() // available to read int I2SClass::available() { - // There is no actual way to tell in ESP - return _I2S_DMA_BUFFER_SIZE; - //uxQueueSpacesAvailable(); + return _read_available; } union i2s_sample_t { @@ -419,21 +451,22 @@ int I2SClass::read(void* buffer, size_t size) return 0; // There was an error switching to receiver } } - int read; -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_read start"); -#endif // _USE_SYS_VIEW - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_read stop"); -#endif // _USE_SYS_VIEW - + //int read; + //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); + size_t cpy_size = size <= _read_available ? size : _read_available; + //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ + if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ + memcpy(buffer, _inputBuffer, cpy_size); + xSemaphoreGive(_in_buf_semaphore); + _input_buffer_pointer = (_input_buffer_pointer + cpy_size) > _buffer_byte_size ? _buffer_byte_size : _input_buffer_pointer + cpy_size; + _read_available -= cpy_size; + } if(_mode == I2S_ADC_DAC){ - for(int i = 0; i < read / 2; ++i){ + for(int i = 0; i < cpy_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; } } - return read; + return cpy_size; } /* @@ -450,20 +483,20 @@ size_t I2SClass::write(int32_t sample) size_t I2SClass::write(const void *buffer, size_t size) { - //Serial.printf("I2SClass::write(): _state = %d\n",_state); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!enableTransmitter()){ return 0; // There was an error switching to transmitter } } size_t bytes_written; -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_write start"); -#endif // _USE_SYS_VIEW - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_write stop"); -#endif // _USE_SYS_VIEW + //esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); + size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; + if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ + memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); + xSemaphoreGive(_out_buf_semaphore); + }else{ + } + _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; return bytes_written; } @@ -491,9 +524,7 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) int I2SClass::availableForWrite() { - // There is no actual way to tell in ESP - return _I2S_DMA_BUFFER_SIZE; - //uxQueueSpacesAvailable(); + return _buffer_byte_size - _output_buffer_pointer; } void I2SClass::onTransmit(void(*function)(void)) @@ -513,9 +544,7 @@ void I2SClass::setBufferSize(int bufferSize) int I2SClass::enableTransmitter() { - Serial.printf("I2SClass::enableTransmitter(): _state = %d\n",_state); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - Serial.printf("I2SClass::enableTransmitter(): change data out pin to %d\n",_sdPin); esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, @@ -561,48 +590,57 @@ void I2SClass::onTransferComplete() xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - //Serial.printf("I2S:onTransferComplete: loop start\n"); xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); if(xActivatedMember == _task_kill_cmd_semaphore_handle){ - Serial.printf("I2S:onTransferComplete: received kill command, breaking from loop\n"); xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop }else if(xActivatedMember == _i2sEventQueue){ - //Serial.printf("I2S:onTransferComplete: received Q\n"); xQueueReceive(_i2sEventQueue, &i2s_event, 0); - if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + //if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ + size_t bytes_written; + if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); + _output_buffer_pointer = 0; + if(xSemaphoreGive(_out_buf_semaphore) != pdTRUE){ + // We would not expect this call to fail because we must have + // obtained the semaphore to get here. + } + } if(_onTransmit){ - //Serial.printf("I2S:onTransferComplete: calling _onTransmit\n"); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onTransmit start"); -#endif // _USE_SYS_VIEW _onTransmit(); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onTransmit stop"); -#endif // _USE_SYS_VIEW - } - }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ - if (_onReceive) { - //Serial.printf("I2S:onTransferComplete: calling _onReceive\n"); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onReceive start"); -#endif // _USE_SYS_VIEW - _onReceive(); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onReceive stop"); -#endif // _USE_SYS_VIEW - } + } // user callback + //}else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ + //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY == pdTRUE)){ + if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); + _input_buffer_pointer = 0; + if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ // CRASHING HERE on first call + // We would not expect this call to fail because we must have + // obtained the semaphore to get here. + } + if (_onReceive) { + _onReceive(); + } // user callback + } // semaphore } // if event TX or RX } } - Serial.printf("xxxxxxx I2S:onTransferComplete: out of loop - kill my self xxxxxxx\n"); _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) { + +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("onTransferComplete start"); +#endif // _USE_SYS_VIEW I2S.onTransferComplete(); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("onTransferComplete stop"); +#endif // _USE_SYS_VIEW } #if I2S_INTERFACES_COUNT > 0 diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 2624e1afc17..42a589ca294 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -107,6 +107,15 @@ class I2SClass : public Stream long _sampleRate; int _mode; + uint16_t _buffer_byte_size; + uint16_t _output_buffer_pointer; + uint16_t _input_buffer_pointer; + uint16_t _read_available; + SemaphoreHandle_t _in_buf_semaphore; + SemaphoreHandle_t _out_buf_semaphore; + void *_inputBuffer; + void *_outputBuffer; + bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; From ad2c5403d82aacbacafb2e9101c24413509ed182 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 7 Jul 2021 15:35:33 +0200 Subject: [PATCH 08/94] [BUGFIX] Fixed data type which was causing crashes --- libraries/I2S/src/I2S.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 42a589ca294..edb29a46b68 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -110,7 +110,7 @@ class I2SClass : public Stream uint16_t _buffer_byte_size; uint16_t _output_buffer_pointer; uint16_t _input_buffer_pointer; - uint16_t _read_available; + size_t _read_available; SemaphoreHandle_t _in_buf_semaphore; SemaphoreHandle_t _out_buf_semaphore; void *_inputBuffer; From baff65d4d372c2f536910af24c1bd685af12702d Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 8 Jul 2021 09:20:52 +0200 Subject: [PATCH 09/94] [BUGFIX] Fixed usage of new buffer in read function --- .../InputSerialPlotter/InputSerialPlotter.ino | 4 +-- libraries/I2S/src/I2S.cpp | 31 +++++++------------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index 743b1c29175..a50fe0a609f 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -32,14 +32,14 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } + I2S.setAllPins(27,25,33,26); } void loop() { // read a sample int sample = I2S.read(); - if (sample) { - // if it's non-zero print value to serial + if (sample && sample != -1 && sample != 1) { Serial.println(sample); } } diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 82d8a390f67..eaf381259a6 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,9 +22,8 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 100 - #define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 -#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER count must be between 2 and 128 +#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -444,28 +443,27 @@ int I2SClass::read() return 0; } -int I2SClass::read(void* buffer, size_t size) -{ +int I2SClass::read(void* buffer, size_t size){ if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { if(!enableReceiver()){ return 0; // There was an error switching to receiver } } - //int read; + size_t cpy_size = 0; //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); - size_t cpy_size = size <= _read_available ? size : _read_available; //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ - memcpy(buffer, _inputBuffer, cpy_size); - xSemaphoreGive(_in_buf_semaphore); + cpy_size = size <= _read_available ? size : _read_available; + memcpy(buffer, (void*)((uint)_inputBuffer+_input_buffer_pointer), cpy_size); _input_buffer_pointer = (_input_buffer_pointer + cpy_size) > _buffer_byte_size ? _buffer_byte_size : _input_buffer_pointer + cpy_size; _read_available -= cpy_size; + xSemaphoreGive(_in_buf_semaphore); } - if(_mode == I2S_ADC_DAC){ - for(int i = 0; i < cpy_size / 2; ++i){ + if(_mode == I2S_ADC_DAC){ + for(size_t i = 0; i < cpy_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; } - } + } // ADC/DAC mode return cpy_size; } @@ -494,7 +492,6 @@ size_t I2SClass::write(const void *buffer, size_t size) if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); xSemaphoreGive(_out_buf_semaphore); - }else{ } _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; return bytes_written; @@ -616,7 +613,7 @@ void I2SClass::onTransferComplete() if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); _input_buffer_pointer = 0; - if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ // CRASHING HERE on first call + if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ // We would not expect this call to fail because we must have // obtained the semaphore to get here. } @@ -634,13 +631,7 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("onTransferComplete start"); -#endif // _USE_SYS_VIEW I2S.onTransferComplete(); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("onTransferComplete stop"); -#endif // _USE_SYS_VIEW } #if I2S_INTERFACES_COUNT > 0 @@ -651,4 +642,4 @@ void I2SClass::onDmaTransferComplete(void*) #if I2S_INTERFACES_COUNT > 1 // TODO set default pins for second module //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex -#endif \ No newline at end of file +#endif From f3661604ceb9e8ecebb9c783b8f9fe9eba7b63a3 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 23 Jul 2021 15:08:50 +0200 Subject: [PATCH 10/94] [BACKUP] Latest experiments with internal buffer --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 32 ++++++------ .../I2S/examples/Callbacks/Callbacks.ino | 33 ++++++------ .../InputSerialPlotter/InputSerialPlotter.ino | 1 - .../I2S/examples/SimpleTone/SimpleTone.ino | 7 ++- libraries/I2S/src/I2S.cpp | 50 +++++-------------- libraries/I2S/src/I2S.h | 17 +++++++ 6 files changed, 66 insertions(+), 74 deletions(-) diff --git a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino index 10a12bdc06c..1c68be33b40 100644 --- a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -1,5 +1,6 @@ /* - This example is only for ESP devices + This example is only for ESP devices. + This example demonstrates usage of integrated Digital to Analog Converter (DAC) You can display sound wave from audio device, or just measure voltage. @@ -41,20 +42,20 @@ Second option to measure voltage on trimmer / potentiometer has following connec | | V GND -Optional resistor will decrease read value. + Optional resistor will decrease read value. -Steps to run: -1. Select target board: - Tools -> Board -> ESP32 Arduino -> your board -2. Upload sketch - Press upload button (arrow in top left corner) - When you see in console line like this: "Connecting........_____.....__" - On your board press and hold Boot button and press EN button shortly. Now you can release both buttons. - You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. - If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. -3. Open plotter - Tools -> Serial Plotter - Enjoy + Steps to run: + 1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board + 2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + On your board press and hold Boot button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. + 3. Open plotter + Tools -> Serial Plotter + Enjoy Created by Tomas Pilny on 17th June 2021 @@ -82,5 +83,4 @@ void loop() { // read a sample int sample = I2S.read(); Serial.println(sample); - delay(100); - } +} diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino index 105ef64133b..5149842fe90 100644 --- a/libraries/I2S/examples/Callbacks/Callbacks.ino +++ b/libraries/I2S/examples/Callbacks/Callbacks.ino @@ -51,10 +51,10 @@ on 23rd June 2021 // If you need to change any of the default pins, simply uncomment chosen line and change the pin number /* -#define PIN_I2S_SCK 33 -#define PIN_I2S_FS 16 -#define PIN_I2S_SD 17 -#define PIN_I2S_SD_OUT 4 +#define PIN_I2S_SCK 5 +#define PIN_I2S_FS 25 +#define PIN_I2S_SD 35 +#define PIN_I2S_SD_OUT 26 */ #include @@ -67,18 +67,21 @@ const int amplitude = 32767; // amplitude of square wave const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave -short sample = amplitude; // current sample value +short out_sample = amplitude; // current sample value int count = 0; void outputCallback(){ + if (count % halfWavelength == 0) { // invert the sample every half wavelength count multiple to generate square wave - sample = -1 * sample; + out_sample = -1 * out_sample; } + Serial.printf("write %d\n", out_sample); // only for debug + // write the same sample twice, once for left and once for the right channel - I2S.write(sample); - I2S.write(sample); + I2S.write(out_sample); + I2S.write(out_sample); // increment the counter for the next sample count++; @@ -87,13 +90,11 @@ void outputCallback(){ // code from InputSerialPlotter example void inputCallback(){ // read a sample - int sample = I2S.read(); + int in_sample = I2S.read(); - Serial.println(sample); // only for debug - - if (sample) { + if (in_sample) { // if it's non-zero print value to serial - Serial.println(sample); + Serial.println(in_sample); } } @@ -111,7 +112,7 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1) delay(100); // do nothing } - I2S.setAllPins(); + I2S.setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); // Register our callback functions I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) @@ -121,5 +122,5 @@ void setup() { void loop() { // loop task remains free for other work - delay(100); // Let the FreeRTOS reset the watchDogTimer -} \ No newline at end of file + delay(10); // Let the FreeRTOS reset the watchDogTimer +} diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index a50fe0a609f..db9f7d0d4f8 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -32,7 +32,6 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } - I2S.setAllPins(27,25,33,26); } void loop() { diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 36464148c33..c60e0aeaca9 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -17,10 +17,9 @@ */ #include - -const int frequency = 1250; // frequency of square wave in Hz -const int amplitude = 32767; // amplitude of square wave -const int sampleRate = 16000; // sample rate in Hz +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 diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index eaf381259a6..5b6138ffd0a 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -24,7 +24,6 @@ #define _I2S_EVENT_QUEUE_LENGTH 100 #define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 #define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 - #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -35,27 +34,6 @@ #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP #endif -#ifndef PIN_I2S_SCK - #define PIN_I2S_SCK 5 -#endif - -#ifndef PIN_I2S_FS - #define PIN_I2S_FS 25 -#endif - -#ifndef PIN_I2S_SD - #define PIN_I2S_SD 26 -#endif - - -#ifndef PIN_I2S_SD_IN - #define PIN_I2S_SD_IN 35 // 35 can be only input pin -#endif - -#ifndef PIN_I2S_SD_OUT - #define PIN_I2S_SD_OUT 26 -#endif - I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), _sdPin(sdPin), // shared data pin @@ -168,7 +146,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { - Serial.println("ERROR I2SClass::begin invalid state"); // debug return 0; // ERR } @@ -182,7 +159,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP default: // invalid mode - Serial.println("ERROR I2SClass::begin invalid mode"); // debug return 0; // ERR } @@ -203,7 +179,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc if(_mode == I2S_ADC_DAC){ if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - Serial.println("ERROR I2SClass::begin invalid bps for ADC/DAC"); // debug return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); @@ -261,7 +236,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc //xSemaphoreGive(_out_buf_semaphore); if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver - Serial.println("ERROR I2SClass::begin error installing i2s driver"); // debug return 0; // ERR } @@ -450,6 +424,7 @@ int I2SClass::read(void* buffer, size_t size){ } } size_t cpy_size = 0; + //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ @@ -488,12 +463,16 @@ size_t I2SClass::write(const void *buffer, size_t size) } size_t bytes_written; //esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); - size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ + size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); + _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; + if(_output_buffer_pointer == _buffer_byte_size){ // when full flush it + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); + _output_buffer_pointer = 0; + } xSemaphoreGive(_out_buf_semaphore); } - _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; return bytes_written; } @@ -593,7 +572,6 @@ void I2SClass::onTransferComplete() break; // from the infinite loop }else if(xActivatedMember == _i2sEventQueue){ xQueueReceive(_i2sEventQueue, &i2s_event, 0); - //if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ size_t bytes_written; if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ @@ -602,14 +580,12 @@ void I2SClass::onTransferComplete() if(xSemaphoreGive(_out_buf_semaphore) != pdTRUE){ // We would not expect this call to fail because we must have // obtained the semaphore to get here. - } - } + } // semaphore give + } // output buffer semaphore if(_onTransmit){ _onTransmit(); } // user callback - //}else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ - //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY == pdTRUE)){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); _input_buffer_pointer = 0; @@ -620,10 +596,10 @@ void I2SClass::onTransferComplete() if (_onReceive) { _onReceive(); } // user callback - } // semaphore - } // if event TX or RX - } - } + } // input buffer semaphore + } // RX Done + } // I2S event + } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task vTaskDelete(NULL); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index edb29a46b68..7709abc019f 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -25,6 +25,23 @@ namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver } +// Default pins +#ifndef PIN_I2S_SCK + #define PIN_I2S_SCK 5 +#endif + +#ifndef PIN_I2S_FS + #define PIN_I2S_FS 25 +#endif + +#ifndef PIN_I2S_SD + #define PIN_I2S_SD 35 // Input if used in duplex +#endif + +#ifndef PIN_I2S_SD_OUT + #define PIN_I2S_SD_OUT 26 +#endif + typedef enum { I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, From 7a64d9743b59c7340651679adb8320580f711522 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 28 Jul 2021 10:04:42 +0200 Subject: [PATCH 11/94] [BUGFIX] Fixed merging error in CMakeLists --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bcd9e7d462f..a3f81ee7975 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ set(LIBRARY_SRCS libraries/HTTPClient/src/HTTPClient.cpp libraries/HTTPUpdate/src/HTTPUpdate.cpp libraries/I2S/src/I2S.cpp - libraries/LITTLEFS/src/LITTLEFS.cpp + libraries/LittleFS/src/LittleFS.cpp libraries/NetBIOS/src/NetBIOS.cpp libraries/Preferences/src/Preferences.cpp libraries/RainMaker/src/RMaker.cpp @@ -142,7 +142,7 @@ set(includedirs libraries/HTTPClient/src libraries/HTTPUpdate/src libraries/I2S/src - libraries/LITTLEFS/src + libraries/LittleFS/src/ libraries/NetBIOS/src libraries/Preferences/src libraries/RainMaker/src From ee969b84b2349292a1d7b226f81546a4a6ba9a23 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 30 Jul 2021 12:27:40 +0200 Subject: [PATCH 12/94] Improved quality of playback --- .../I2S/examples/SimpleTone/SimpleTone.ino | 22 +-- libraries/I2S/src/I2S.cpp | 136 +++++++++--------- libraries/I2S/src/I2S.h | 5 +- 3 files changed, 83 insertions(+), 80 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index c60e0aeaca9..0967f50dca5 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -38,15 +38,17 @@ void setup() { } void loop() { - if (count % halfWavelength == 0) { - // invert the sample every half wavelength count multiple to generate square wave - sample = -1 * sample; + if(I2S.availableForWrite() >= 2){ + 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++; } - - // 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/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 5b6138ffd0a..5743a60b0cc 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,9 +21,11 @@ #include "I2S.h" #include "freertos/semphr.h" -#define _I2S_EVENT_QUEUE_LENGTH 100 -#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 -#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 +#define _I2S_EVENT_QUEUE_LENGTH 64 +#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 +// (Theoretically it should be above 8, but for some reason sizes below 200 results in frozen callback task) + +#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -36,11 +38,11 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), - _sdPin(sdPin), // shared data pin - _inSdPin(-1), // input data pin - _outSdPin(-1), // output data pin - _sckPin(sckPin), // clock pin - _fsPin(fsPin), // frame (word) select pin + _sdPin(sdPin), // shared data pin + _inSdPin(-1), // input data pin + _outSdPin(-1), // output data pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_IDLE), _bitsPerSample(0), @@ -48,18 +50,16 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _output_buffer_pointer(0), _input_buffer_pointer(0), _read_available(0), _in_buf_semaphore(NULL), - _out_buf_semaphore(NULL), _inputBuffer(NULL), - _outputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _output_ring_buffer(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -68,11 +68,11 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex _deviceIndex(deviceIndex), - _sdPin(inSdPin), // shared data pin - _inSdPin(inSdPin), // input data pin + _sdPin(inSdPin), // shared data pin + _inSdPin(inSdPin), // input data pin _outSdPin(outSdPin), // output data pin - _sckPin(sckPin), // clock pin - _fsPin(fsPin), // frame (word) select pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_DUPLEX), _bitsPerSample(0), @@ -80,18 +80,16 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _output_buffer_pointer(0), _input_buffer_pointer(0), _read_available(0), _in_buf_semaphore(NULL), - _out_buf_semaphore(NULL), _inputBuffer(NULL), - _outputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _output_ring_buffer(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -131,9 +129,9 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) int I2SClass::begin(int mode, int bitsPerSample) { - Serial.println("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP"); - Serial.println("Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below"); - Serial.println("\tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); + log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ + Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ + \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); return 0; // ERR // slave mode (not driving clock and frame select pin - input) //return begin(mode, 0, bitsPerSample, false); @@ -184,7 +182,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); }else{ // End of ADC/DAC mode; start of Normal mode if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ - Serial.println("I2S.begin(): invalid bits per second for normal mode"); + log_e("I2S.begin(): invalid bits per second for normal mode"); // ESP does support 24 bps, however for the compatibility // with original Arduino implementation it is not allowed return 0; // ERR @@ -216,24 +214,19 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc }; - _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8); + _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; _inputBuffer = malloc(_buffer_byte_size); - _outputBuffer = malloc(_buffer_byte_size); - _output_buffer_pointer = 0; + //_output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_ALLOWSPLIT); + _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _input_buffer_pointer = 0; - if(_inputBuffer == NULL || _outputBuffer == NULL){ + if(_inputBuffer == NULL){ return 0; // ERR } _in_buf_semaphore = xSemaphoreCreateMutex(); - _out_buf_semaphore = xSemaphoreCreateMutex(); - //_in_buf_semaphore = xSemaphoreCreateBinary(); - //_out_buf_semaphore = xSemaphoreCreateBinary(); - if(_in_buf_semaphore == NULL || _out_buf_semaphore == NULL){ + if(_in_buf_semaphore == NULL){ return 0; // ERR } - //xSemaphoreGive(_in_buf_semaphore); - //xSemaphoreGive(_out_buf_semaphore); if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver return 0; // ERR @@ -245,7 +238,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ - Serial.println("i2s_set_pin err"); + log_e("i2s_set_pin err"); return 0; // ERR } @@ -254,11 +247,12 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); }else{ // End of ADC/DAC mode; start of Normal mode if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { - Serial.println("i2s_set_pin err"); + log_e("i2s_set_pin err"); end(); return 0; // ERR } } + createCallbackTask(); _initialized = true; @@ -370,19 +364,14 @@ void I2SClass::end() _onTransmit = NULL; _onReceive = NULL; free(_inputBuffer); - free(_outputBuffer); _inputBuffer = NULL; - _outputBuffer = NULL; _buffer_byte_size = 0; - _output_buffer_pointer = 0; _input_buffer_pointer = 0; vSemaphoreDelete(_in_buf_semaphore); - vSemaphoreDelete(_out_buf_semaphore); _in_buf_semaphore = NULL; - _out_buf_semaphore = NULL; - + vRingbufferDelete(_output_ring_buffer); }else{ - // TODO log_e with error - destroy task from inside not permitted + log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!") } } @@ -402,7 +391,6 @@ int I2SClass::read() { i2s_sample_t sample; sample.b32 = 0; - //int bytes_read = read(&sample, _bitsPerSample / 8); read(&sample, _bitsPerSample / 8); if (_bitsPerSample == 32) { @@ -425,8 +413,6 @@ int I2SClass::read(void* buffer, size_t size){ } size_t cpy_size = 0; - //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); - //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ cpy_size = size <= _read_available ? size : _read_available; memcpy(buffer, (void*)((uint)_inputBuffer+_input_buffer_pointer), cpy_size); @@ -461,19 +447,11 @@ size_t I2SClass::write(const void *buffer, size_t size) return 0; // There was an error switching to transmitter } } - size_t bytes_written; - //esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); - if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ - size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; - memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); - _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; - if(_output_buffer_pointer == _buffer_byte_size){ // when full flush it - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); - _output_buffer_pointer = 0; - } - xSemaphoreGive(_out_buf_semaphore); + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + return size; + }else{ + return 0; } - return bytes_written; } int I2SClass::peek() @@ -500,7 +478,7 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) int I2SClass::availableForWrite() { - return _buffer_byte_size - _output_buffer_pointer; + return (int)xRingbufferGetCurFreeSize(_output_ring_buffer); } void I2SClass::onTransmit(void(*function)(void)) @@ -559,6 +537,13 @@ void I2SClass::onTransferComplete() static QueueSetHandle_t xQueueSet; QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; + size_t item_size = 0; + size_t prev_item_size = 0; + void *item = NULL; + bool prev_item_valid = false; + size_t bytes_written; + int prev_item_offset = 0; + uint8_t prev_item[_I2S_DMA_BUFFER_SIZE]; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -573,15 +558,33 @@ void I2SClass::onTransferComplete() }else if(xActivatedMember == _i2sEventQueue){ xQueueReceive(_i2sEventQueue, &i2s_event, 0); if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ - size_t bytes_written; - if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); - _output_buffer_pointer = 0; - if(xSemaphoreGive(_out_buf_semaphore) != pdTRUE){ - // We would not expect this call to fail because we must have - // obtained the semaphore to get here. - } // semaphore give - } // output buffer semaphore + do{ // send to esp i2s driver loop + prev_item_valid = false; + if(prev_item && prev_item_valid){ // use item from previous round + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); + if(prev_item_size != bytes_written){ + prev_item_offset = bytes_written; + prev_item_valid = true; + } + prev_item_size -= bytes_written; + }else{ // load new item from ring buffer + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), _I2S_DMA_BUFFER_SIZE); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ + memcpy(prev_item, item+bytes_written, item_size-bytes_written); + prev_item_size = item_size - bytes_written; + prev_item_offset = 0; + prev_item_valid = true; + } + vRingbufferReturnItem(_output_ring_buffer, item); + //Failed to receive item + } // Check received item + } // old or new item + if(item_size != bytes_written){ + log_w("i2s_write could not write requested amount: requested=%d; written=%d\n", item_size, bytes_written); + } + }while((item_size == bytes_written && item_size == _I2S_DMA_BUFFER_SIZE) || prev_item_size == bytes_written); if(_onTransmit){ _onTransmit(); } // user callback @@ -606,7 +609,6 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 7709abc019f..be093e2c43d 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -20,6 +20,7 @@ #define _I2S_H_INCLUDED #include +#include "freertos/ringbuf.h" namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver @@ -125,18 +126,16 @@ class I2SClass : public Stream int _mode; uint16_t _buffer_byte_size; - uint16_t _output_buffer_pointer; uint16_t _input_buffer_pointer; size_t _read_available; SemaphoreHandle_t _in_buf_semaphore; - SemaphoreHandle_t _out_buf_semaphore; void *_inputBuffer; - void *_outputBuffer; bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; QueueHandle_t _task_kill_cmd_semaphore_handle; + RingbufHandle_t _output_ring_buffer; void (*_onTransmit)(void); void (*_onReceive)(void); From 5b65b5d3a373fabcaf09432b9a187dad6ad8a492 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 3 Aug 2021 11:17:00 +0200 Subject: [PATCH 13/94] Reimplemented input buffer as ring buffer --- libraries/I2S/src/I2S.cpp | 167 ++++++++++++++++++-------------------- libraries/I2S/src/I2S.h | 5 +- 2 files changed, 82 insertions(+), 90 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 5743a60b0cc..c8103534c89 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,9 +21,9 @@ #include "I2S.h" #include "freertos/semphr.h" -#define _I2S_EVENT_QUEUE_LENGTH 64 -#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 -// (Theoretically it should be above 8, but for some reason sizes below 200 results in frozen callback task) +#define _I2S_EVENT_QUEUE_LENGTH 16 +#define _I2S_DMA_BUFFER_SIZE 256 // BUFFER SIZE must be between 200 and 1024 +// (Theoretically it could be above 8, but for some reason sizes below 200 results in frozen callback task) #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -50,15 +50,12 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _input_buffer_pointer(0), - _read_available(0), - _in_buf_semaphore(NULL), - _inputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _input_ring_buffer(NULL), _output_ring_buffer(NULL), _onTransmit(NULL), @@ -80,15 +77,12 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _input_buffer_pointer(0), - _read_available(0), - _in_buf_semaphore(NULL), - _inputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _input_ring_buffer(NULL), _output_ring_buffer(NULL), _onTransmit(NULL), @@ -144,6 +138,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + log_e("I2S.begin: unexpected _state (%d)",_state); return 0; // ERR } @@ -157,6 +152,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP default: // invalid mode + log_e("ERROR I2SClass::begin() unknown mode"); return 0; // ERR } @@ -177,6 +173,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc if(_mode == I2S_ADC_DAC){ if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); @@ -215,20 +212,15 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; - _inputBuffer = malloc(_buffer_byte_size); - //_output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_ALLOWSPLIT); + _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); - _input_buffer_pointer = 0; - if(_inputBuffer == NULL){ - return 0; // ERR - } - _in_buf_semaphore = xSemaphoreCreateMutex(); - - if(_in_buf_semaphore == NULL){ + if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ + log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); return 0; // ERR } if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + log_e("ERROR could not install i2s driver"); return 0; // ERR } @@ -238,7 +230,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ - log_e("i2s_set_pin err"); + log_e("i2s_set_pin failed"); return 0; // ERR } @@ -247,7 +239,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); }else{ // End of ADC/DAC mode; start of Normal mode if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { - log_e("i2s_set_pin err"); + log_e("i2s_set_pin failed"); end(); return 0; // ERR } @@ -296,6 +288,7 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); return 0; // ERR } } @@ -304,6 +297,7 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ int I2SClass::setStateDuplex(){ if(_inSdPin < 0 || _outSdPin < 0){ + log_e("I2S cannot set Duplex-one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); return 0; // ERR } _state = I2S_STATE_DUPLEX; @@ -322,6 +316,7 @@ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); return 0; // ERR } } @@ -340,6 +335,7 @@ int I2SClass::setSimplex(uint8_t sdPin){ .data_in_num = _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); return 0; // ERR } } @@ -363,22 +359,17 @@ void I2SClass::end() } _onTransmit = NULL; _onReceive = NULL; - free(_inputBuffer); - _inputBuffer = NULL; - _buffer_byte_size = 0; - _input_buffer_pointer = 0; - vSemaphoreDelete(_in_buf_semaphore); - _in_buf_semaphore = NULL; + vRingbufferDelete(_input_ring_buffer); vRingbufferDelete(_output_ring_buffer); }else{ - log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!") + log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!"); } } -// available to read +// Bytes available to read int I2SClass::available() { - return _read_available; + return _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer); } union i2s_sample_t { @@ -411,21 +402,22 @@ int I2SClass::read(void* buffer, size_t size){ return 0; // There was an error switching to receiver } } - size_t cpy_size = 0; - - if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ - cpy_size = size <= _read_available ? size : _read_available; - memcpy(buffer, (void*)((uint)_inputBuffer+_input_buffer_pointer), cpy_size); - _input_buffer_pointer = (_input_buffer_pointer + cpy_size) > _buffer_byte_size ? _buffer_byte_size : _input_buffer_pointer + cpy_size; - _read_available -= cpy_size; - xSemaphoreGive(_in_buf_semaphore); + + size_t item_size = 0; + void *tmp_buffer; + tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); + if(tmp_buffer != NULL){ + memcpy(buffer, tmp_buffer, item_size); + if(_mode == I2S_ADC_DAC){ + for(size_t i = 0; i < item_size / 2; ++i){ + ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; + } + } // ADC/DAC mode + vRingbufferReturnItem(_input_ring_buffer, tmp_buffer); + return item_size; + }else{ + return 0; } - if(_mode == I2S_ADC_DAC){ - for(size_t i = 0; i < cpy_size / 2; ++i){ - ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; - } - } // ADC/DAC mode - return cpy_size; } /* @@ -435,11 +427,21 @@ size_t I2SClass::write(int sample) } */ +size_t I2SClass::write(uint8_t data) +{ + return write((int32_t)data); +} + size_t I2SClass::write(int32_t sample) { return write(&sample, _bitsPerSample/8); } +size_t I2SClass::write(const uint8_t *buffer, size_t size) +{ + return write((const void*)buffer, size); +} + size_t I2SClass::write(const void *buffer, size_t size) { if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { @@ -457,7 +459,7 @@ size_t I2SClass::write(const void *buffer, size_t size) int I2SClass::peek() { // TODO - // peek() is not implemented for ESP + // peek() is not implemented for ESP yet return 0; } @@ -466,16 +468,7 @@ void I2SClass::flush() // do nothing, writes are DMA triggered } -size_t I2SClass::write(uint8_t data) -{ - return write((int32_t)data); -} - -size_t I2SClass::write(const uint8_t *buffer, size_t size) -{ - return write((const void*)buffer, size); -} - +// Bytes available to write int I2SClass::availableForWrite() { return (int)xRingbufferGetCurFreeSize(_output_ring_buffer); @@ -525,6 +518,7 @@ int I2SClass::enableReceiver() }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ _state = I2S_STATE_IDLE; + log_e("i2s_set_pin failed"); return 0; // ERR } _state = I2S_STATE_RECEIVER; @@ -535,15 +529,17 @@ int I2SClass::enableReceiver() void I2SClass::onTransferComplete() { static QueueSetHandle_t xQueueSet; + const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; size_t item_size = 0; size_t prev_item_size = 0; void *item = NULL; bool prev_item_valid = false; - size_t bytes_written; + size_t bytes_written, bytes_read; int prev_item_offset = 0; - uint8_t prev_item[_I2S_DMA_BUFFER_SIZE]; + uint8_t prev_item[_I2S_DMA_BUFFER_SIZE*4]; + uint8_t _inputBuffer[_I2S_DMA_BUFFER_SIZE*4]; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -558,48 +554,46 @@ void I2SClass::onTransferComplete() }else if(xActivatedMember == _i2sEventQueue){ xQueueReceive(_i2sEventQueue, &i2s_event, 0); if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ - do{ // send to esp i2s driver loop - prev_item_valid = false; - if(prev_item && prev_item_valid){ // use item from previous round - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); - if(prev_item_size != bytes_written){ - prev_item_offset = bytes_written; - prev_item_valid = true; - } - prev_item_size -= bytes_written; - }else{ // load new item from ring buffer - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), _I2S_DMA_BUFFER_SIZE); + if(prev_item_valid){ // use item from previous round + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); + if(prev_item_size == bytes_written){ + prev_item_valid = false; + } // write size check + prev_item_offset = bytes_written; + prev_item_size -= bytes_written; + } // prev_item_valid + + if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer + do{ // "send to esp i2s driver" loop + bytes_written = 0; + item_size = 0; + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); if (item != NULL){ esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); - if(item_size != bytes_written){ - memcpy(prev_item, item+bytes_written, item_size-bytes_written); + if(item_size != bytes_written){ // save item that was not written correctly for later + memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); prev_item_size = item_size - bytes_written; prev_item_offset = 0; prev_item_valid = true; - } + } // save item that was not written correctly for later vRingbufferReturnItem(_output_ring_buffer, item); - //Failed to receive item } // Check received item - } // old or new item - if(item_size != bytes_written){ - log_w("i2s_write could not write requested amount: requested=%d; written=%d\n", item_size, bytes_written); - } - }while((item_size == bytes_written && item_size == _I2S_DMA_BUFFER_SIZE) || prev_item_size == bytes_written); + }while(item_size == bytes_written && item_size == single_dma_buf); + } // don't read from almost empty buffer if(_onTransmit){ _onTransmit(); } // user callback + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ - if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); - _input_buffer_pointer = 0; - if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ - // We would not expect this call to fail because we must have - // obtained the semaphore to get here. - } + size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ + log_w("I2S failed to send item from DMA to internal buffer\n"); + }else{ if (_onReceive) { _onReceive(); } // user callback - } // input buffer semaphore + } // xRingbufferSendComplete } // RX Done } // I2S event } // infinite loop @@ -609,6 +603,7 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { + I2S.onTransferComplete(); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index be093e2c43d..5412bbc3c45 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -126,15 +126,12 @@ class I2SClass : public Stream int _mode; uint16_t _buffer_byte_size; - uint16_t _input_buffer_pointer; - size_t _read_available; - SemaphoreHandle_t _in_buf_semaphore; - void *_inputBuffer; bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; QueueHandle_t _task_kill_cmd_semaphore_handle; + RingbufHandle_t _input_ring_buffer; RingbufHandle_t _output_ring_buffer; void (*_onTransmit)(void); From f0ebf2bab88419b73d15a96bbc217d5e97d4f03d Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 5 Aug 2021 14:15:12 +0200 Subject: [PATCH 14/94] Fixed kill semaphore error; added pin getters/setters --- libraries/I2S/src/I2S.cpp | 163 +++++++++++++++++++++++++------------- libraries/I2S/src/I2S.h | 21 ++++- 2 files changed, 124 insertions(+), 60 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c8103534c89..c076114bc3c 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -90,13 +90,18 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, { } -void I2SClass::createCallbackTask() +int I2SClass::createCallbackTask() { int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); + if(_task_kill_cmd_semaphore_handle == NULL){ + log_e("Could not create semaphore"); + return 0; // ERR + } } + xTaskCreate( onDmaTransferComplete, // Function to implement the task "onDmaTransferComplete", // Name of the task @@ -105,13 +110,22 @@ void I2SClass::createCallbackTask() 2, // Priority of the task &_callbackTaskHandle // Task handle. ); + if(_callbackTaskHandle == NULL){ + log_e("Could not create callback task"); + return 0; // ERR + } } + return 1; // OK } void I2SClass::destroyCallbackTask() { if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ - xSemaphoreGive(_task_kill_cmd_semaphore_handle); + while(_callbackTaskHandle != NULL){ + ; // wait + } + vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage + _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore } // callback handle check } @@ -210,7 +224,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc .dma_buf_len = _I2S_DMA_BUFFER_SIZE }; - _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); @@ -245,54 +258,96 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } } - createCallbackTask(); + if(!createCallbackTask()){ + return 0; // ERR + } _initialized = true; return 1; // OK } -int I2SClass::setAllPins(){ - return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); +int I2SClass::_applyPinSetting(){ + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); + return 0; // ERR + } + } + return 1; // OK } -int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ +void I2SClass::_setSckPin(int sckPin){ if(sckPin >= 0){ _sckPin = sckPin; }else{ _sckPin = PIN_I2S_SCK; } +} + +int I2SClass::setSckPin(int sckPin){ + _setSckPin(sckPin); + return _applyPinSetting(); +} +void I2SClass::_setFsPin(int fsPin){ if(fsPin >= 0){ _fsPin = fsPin; }else{ _fsPin = PIN_I2S_FS; } +} + +int I2SClass::setFsPin(int fsPin){ + _setFsPin(fsPin); + return _applyPinSetting(); +} +void I2SClass::_setDataInPin(int inSdPin){ if(inSdPin >= 0){ _inSdPin = inSdPin; }else{ _inSdPin = PIN_I2S_SD; } +} + +int I2SClass::setDataInPin(int inSdPin){ + _setDataInPin(inSdPin); + pinMode(_inSdPin, INPUT_PULLDOWN); + // TODO if there is any default pinMode - set it to old input + return _applyPinSetting(); +} +void I2SClass::_setDataOutPin(int outSdPin){ if(outSdPin >= 0){ _outSdPin = outSdPin; }else{ _outSdPin = PIN_I2S_SD_OUT; } +} - if(_initialized){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin, - .data_in_num = _inSdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - } - } - return 1; // OK +int I2SClass::setDataOutPin(int outSdPin){ + _setDataOutPin(outSdPin); + return _applyPinSetting(); +} + + +int I2SClass::setAllPins(){ + return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); +} + +int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ + setSckPin(sckPin); + setFsPin(fsPin); + setDataInPin(inSdPin); + setDataOutPin(outSdPin); + + return _applyPinSetting(); } int I2SClass::setStateDuplex(){ @@ -304,42 +359,24 @@ int I2SClass::setStateDuplex(){ return 1; } -//int I2SClass::setHalfDuplex(uint8_t inSdPin=PIN_I2S_SD, uint8_t outSdPin=PIN_I2S_SD_OUT){ -int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ - _inSdPin = inSdPin; - _outSdPin = outSdPin; - if(_initialized){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin, - .data_in_num = _inSdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - } - } - return 1; // OK +int I2SClass::getSckPin(){ + return _sckPin; } -//int I2SClass::setSimplex(uint8_t sdPin=PIN_I2S_SD){ -int I2SClass::setSimplex(uint8_t sdPin){ - _sdPin = sdPin; - if(_initialized){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - //.data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, - .data_out_num = I2S_PIN_NO_CHANGE, - .data_in_num = _sdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - } - } - return 1; // OK +int I2SClass::getFsPin(){ + return _fsPin; +} + +int I2SClass::getDataPin(){ + return _sdPin; +} + +int I2SClass::getDataInPin(){ + return _inSdPin; +} + +int I2SClass::getDataOutPin(){ + return _outSdPin; } void I2SClass::end() @@ -465,7 +502,18 @@ int I2SClass::peek() void I2SClass::flush() { - // do nothing, writes are DMA triggered + const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); + size_t item_size = 0; + size_t bytes_written; + void *item = NULL; + + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ + } + vRingbufferReturnItem(_output_ring_buffer, item); + } } // Bytes available to write @@ -580,6 +628,7 @@ void I2SClass::onTransferComplete() } // Check received item }while(item_size == bytes_written && item_size == single_dma_buf); } // don't read from almost empty buffer + if(_onTransmit){ _onTransmit(); } // user callback @@ -595,16 +644,16 @@ void I2SClass::onTransferComplete() } // user callback } // xRingbufferSendComplete } // RX Done - } // I2S event + } // Queue set (I2S event or kill command) } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task - vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) { I2S.onTransferComplete(); + vTaskDelete(NULL); } #if I2S_INTERFACES_COUNT > 0 diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 5412bbc3c45..8af28cb59ee 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -64,10 +64,20 @@ class I2SClass : public Stream // change pin setup and mode (default is Half Duplex) // Can be called only on initialized object (after begin) int setStateDuplex(); + + int setSckPin(int sckPin); + int setFsPin(int fsPin); + int setDataInPin(int inSdPin); + int setDataOutPin(int outSdPin); + int setAllPins(); int setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin); - int setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin); - int setSimplex(uint8_t sdPin); + + int getSckPin(); + int getFsPin(); + int getDataPin(); + int getDataInPin(); + int getDataOutPin(); void end(); @@ -101,9 +111,14 @@ class I2SClass : public Stream void onTransferComplete(); void destroyCallbackTask(); - void createCallbackTask(); + int createCallbackTask(); static void onDmaTransferComplete(void*); + void _setSckPin(int sckPin); + void _setFsPin(int fsPin); + void _setDataInPin(int inSdPin); + void _setDataOutPin(int outSdPin); + int _applyPinSetting(); private: typedef enum { From a86a32bc7b05c9a66e125d8a89d05aef908c702b Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 5 Aug 2021 15:03:02 +0200 Subject: [PATCH 15/94] Returning accidentally removed mutex give --- libraries/I2S/src/I2S.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c076114bc3c..bc8ffd15cb1 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -121,6 +121,7 @@ int I2SClass::createCallbackTask() void I2SClass::destroyCallbackTask() { if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ + xSemaphoreGive(_task_kill_cmd_semaphore_handle); while(_callbackTaskHandle != NULL){ ; // wait } From 97102fee362ace9b1dcd85e6a14ba5ed67b3c02c Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 5 Aug 2021 16:16:33 +0200 Subject: [PATCH 16/94] Increased DMA buffer size for better performance --- libraries/I2S/src/I2S.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index bc8ffd15cb1..496d569ce30 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,8 +22,9 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_SIZE 256 // BUFFER SIZE must be between 200 and 1024 +#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 // (Theoretically it could be above 8, but for some reason sizes below 200 results in frozen callback task) +// And values bellow 500 may result in low quality audio #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -123,11 +124,13 @@ void I2SClass::destroyCallbackTask() if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ xSemaphoreGive(_task_kill_cmd_semaphore_handle); while(_callbackTaskHandle != NULL){ - ; // wait + ; // wait until task ends itself properly } vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore - } // callback handle check + }else{ // callback handle check + log_e("Could not destroy callback"); + } } int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) @@ -193,13 +196,17 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); }else{ // End of ADC/DAC mode; start of Normal mode - if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ - log_e("I2S.begin(): invalid bits per second for normal mode"); - // ESP does support 24 bps, however for the compatibility - // with original Arduino implementation it is not allowed + if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ + if(_bitsPerSample == 8){ + log_e("ESP unfortunately does not support 8 bits per sample"); + }else{ + log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 16 | 24 | 32", _bitsPerSample); + } return 0; // ERR } - + if(_bitsPerSample == 24){ + log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); + } if (_state == I2S_STATE_DUPLEX){ // duplex pin_config = { From 83bb4489cc392113dab72deeb8d7281afe589904 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 13 Aug 2021 12:38:40 +0200 Subject: [PATCH 17/94] Split begin; changed pin setups and duplex/simplex setup --- .../I2S/examples/SimpleTone/SimpleTone.ino | 6 +- libraries/I2S/src/I2S.cpp | 270 ++++++++++-------- libraries/I2S/src/I2S.h | 13 +- 3 files changed, 160 insertions(+), 129 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 0967f50dca5..e3ad31b8055 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -20,6 +20,7 @@ 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 bps = 16; const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave @@ -31,14 +32,14 @@ void setup() { Serial.println("I2S simple tone"); // start I2S at the sample rate with 16-bits per sample - if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) { + if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bps)) { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } + I2S.setDataOutPin(26); } void loop() { - if(I2S.availableForWrite() >= 2){ if (count % halfWavelength == 0 ) { // invert the sample every half wavelength count multiple to generate square wave sample = -1 * sample; @@ -50,5 +51,4 @@ void loop() { // increment the counter for the next sample count++; - } } diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 496d569ce30..4a5bd958ac8 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,11 +21,8 @@ #include "I2S.h" #include "freertos/semphr.h" -#define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 -// (Theoretically it could be above 8, but for some reason sizes below 200 results in frozen callback task) -// And values bellow 500 may result in low quality audio +#define _I2S_EVENT_QUEUE_LENGTH 16 #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -58,6 +55,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _task_kill_cmd_semaphore_handle(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), + _i2s_dma_buffer_size(1024), + _driveClock(true), _onTransmit(NULL), _onReceive(NULL) @@ -85,6 +84,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _task_kill_cmd_semaphore_handle(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), + _i2s_dma_buffer_size(1024), + _driveClock(true), _onTransmit(NULL), _onReceive(NULL) @@ -93,7 +94,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, int I2SClass::createCallbackTask() { - int stack_size = 10000; + int stack_size = 15000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); @@ -133,62 +134,15 @@ void I2SClass::destroyCallbackTask() } } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) -{ - // master mode (driving clock and frame select pins - output) - return begin(mode, sampleRate, bitsPerSample, true); -} - -int I2SClass::begin(int mode, int bitsPerSample) -{ - log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ - Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ - \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); - return 0; // ERR - // slave mode (not driving clock and frame select pin - input) - //return begin(mode, 0, bitsPerSample, false); -} - -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) -{ - if(_initialized){ - end(); - } - - if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { - log_e("I2S.begin: unexpected _state (%d)",_state); - return 0; // ERR - } - - // TODO implement left / right justified modes - switch (mode) { - case I2S_PHILIPS_MODE: - case I2S_ADC_DAC: - break; - - case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - default: - // invalid mode - log_e("ERROR I2SClass::begin() unknown mode"); - return 0; // ERR - } - - _mode = mode; - _sampleRate = sampleRate; - _bitsPerSample = bitsPerSample; +int I2SClass::_install_driver(){ esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); - if(driveClock){ + if(_driveClock){ i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER); }else{ // TODO there will much more work with slave mode i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } - esp_i2s::i2s_pin_config_t pin_config; - pin_config.bck_io_num = _sckPin; - pin_config.ws_io_num = _fsPin; - if(_mode == I2S_ADC_DAC){ if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); @@ -208,18 +162,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } - if (_state == I2S_STATE_DUPLEX){ // duplex - pin_config = { - .data_out_num = _outSdPin, - .data_in_num = _inSdPin - }; - }else{ // simplex - pin_config = { - .data_out_num = I2S_PIN_NO_CHANGE, - .data_in_num = _sdPin - }; - } - } // Normal mode esp_i2s::i2s_config_t i2s_config = { .mode = i2s_mode, @@ -229,17 +171,9 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, - .dma_buf_len = _I2S_DMA_BUFFER_SIZE + .dma_buf_len = _i2s_dma_buffer_size }; - _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; - _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); - _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); - if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ - log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); - return 0; // ERR - } - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver log_e("ERROR could not install i2s driver"); return 0; // ERR @@ -258,18 +192,78 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); + _initialized = true; }else{ // End of ADC/DAC mode; start of Normal mode - if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { - log_e("i2s_set_pin failed"); + _initialized = true; + if(!_applyPinSetting()){ end(); return 0; // ERR } } + return 1; // OK +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) +{ + // master mode (driving clock and frame select pins - output) + return begin(mode, sampleRate, bitsPerSample, true); +} + +int I2SClass::begin(int mode, int bitsPerSample) +{ + log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ + Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ + \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); + return 0; // ERR + // slave mode (not driving clock and frame select pin - input) + //return begin(mode, 0, bitsPerSample, false); +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) +{ + if(_initialized){ + end(); + } + _driveClock = driveClock; + _mode = mode; + _sampleRate = sampleRate; + _bitsPerSample = bitsPerSample; + + if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + log_e("I2S.begin: unexpected _state (%d)",_state); + return 0; // ERR + } + + // TODO implement left / right justified modes + switch (mode) { + case I2S_PHILIPS_MODE: + case I2S_ADC_DAC: + break; + + case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + default: + // invalid mode + log_e("ERROR I2SClass::begin() unknown mode"); + return 0; // ERR + } + + _buffer_byte_size = _i2s_dma_buffer_size * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; + _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); + _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); + if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ + log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); + return 0; // ERR + } + + if(!_install_driver()){ + return 0; // ERR + } + if(!createCallbackTask()){ return 0; // ERR } - _initialized = true; return 1; // OK } @@ -279,14 +273,31 @@ int I2SClass::_applyPinSetting(){ esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, - .data_out_num = _outSdPin, - .data_in_num = _inSdPin + .data_out_num = I2S_PIN_NO_CHANGE, + .data_in_num = I2S_PIN_NO_CHANGE }; + if (_state == I2S_STATE_DUPLEX){ // duplex + pin_config.data_out_num = _outSdPin; + pin_config.data_in_num = _inSdPin; + }else{ // simplex + if(_state == I2S_STATE_RECEIVER){ + pin_config.data_out_num = I2S_PIN_NO_CHANGE; + pin_config.data_in_num = _inSdPin>0 ? _inSdPin : _sdPin; + }else if(_state == I2S_STATE_TRANSMITTER){ + pin_config.data_out_num = _outSdPin>0 ? _outSdPin : _sdPin; + pin_config.data_in_num = I2S_PIN_NO_CHANGE; + }else{ + } + } + // pinMode(_inSdPin, INPUT_PULLDOWN); + // TODO if there is any default pinMode - set it to old input if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); + log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d\n", _sckPin, _fsPin, pin_config.data_in_num, pin_config.data_out_num); return 0; // ERR + }else{ + return 1; // OK } - } + } // Is driver _initialized ? return 1; // OK } @@ -326,8 +337,6 @@ void I2SClass::_setDataInPin(int inSdPin){ int I2SClass::setDataInPin(int inSdPin){ _setDataInPin(inSdPin); - pinMode(_inSdPin, INPUT_PULLDOWN); - // TODO if there is any default pinMode - set it to old input return _applyPinSetting(); } @@ -350,23 +359,38 @@ int I2SClass::setAllPins(){ } int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ - setSckPin(sckPin); - setFsPin(fsPin); - setDataInPin(inSdPin); - setDataOutPin(outSdPin); - - return _applyPinSetting(); + _setSckPin(sckPin); + _setFsPin(fsPin); + _setDataInPin(inSdPin); + _setDataOutPin(outSdPin); + //return _applyPinSetting(); + return 1; // OK } -int I2SClass::setStateDuplex(){ +int I2SClass::setDuplex(){ + /* if(_inSdPin < 0 || _outSdPin < 0){ - log_e("I2S cannot set Duplex-one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); + log_e("I2S cannot set Duplex - one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); return 0; // ERR } + */ _state = I2S_STATE_DUPLEX; return 1; } +int I2SClass::setSimplex(){ + if(_sdPin < 0){ + log_e("I2S cannot set Simplex - shared data pin is not set\n data pin = %d", _sdPin); + return 0; // ERR + } + _state = I2S_STATE_IDLE; + return 1; +} + +int I2SClass::isDuplex(){ + return (int)(_state == I2S_STATE_DUPLEX); +} + int I2SClass::getSckPin(){ return _sckPin; } @@ -387,12 +411,18 @@ int I2SClass::getDataOutPin(){ return _outSdPin; } +int I2SClass::_uninstall_driver(){ + // TODO + return 1; // Ok +} + void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ destroyCallbackTask(); if(_initialized){ + if(_mode == I2S_ADC_DAC){ esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); } @@ -510,7 +540,7 @@ int I2SClass::peek() void I2SClass::flush() { - const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); size_t item_size = 0; size_t bytes_written; void *item = NULL; @@ -540,44 +570,39 @@ void I2SClass::onReceive(void(*function)(void)) _onReceive = function; } -void I2SClass::setBufferSize(int bufferSize) +int I2SClass::setBufferSize(int bufferSize) { - // does nothing in ESP + if(bufferSize >= 8 && bufferSize <= 1024){ + _i2s_dma_buffer_size = bufferSize; + if(_initialized){ + end(); + return begin(_mode, _sampleRate, _bitsPerSample, _driveClock); + } + + return 1; // OK + }else{ + return 0; // ERR + } +} + +int I2SClass::getBufferSize(){ + return _i2s_dma_buffer_size; } int I2SClass::enableTransmitter() { - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, - .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - _state = I2S_STATE_IDLE; - return 0; // ERR - } + if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_TRANSMITTER){ _state = I2S_STATE_TRANSMITTER; + return _applyPinSetting(); } return 1; // Ok } int I2SClass::enableReceiver() { - if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, - .data_in_num = _inSdPin != -1 ? _inSdPin : _sdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - _state = I2S_STATE_IDLE; - log_e("i2s_set_pin failed"); - return 0; // ERR - } + if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_RECEIVER){ _state = I2S_STATE_RECEIVER; + return _applyPinSetting(); } return 1; // Ok } @@ -585,7 +610,7 @@ int I2SClass::enableReceiver() void I2SClass::onTransferComplete() { static QueueSetHandle_t xQueueSet; - const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; size_t item_size = 0; @@ -594,8 +619,8 @@ void I2SClass::onTransferComplete() bool prev_item_valid = false; size_t bytes_written, bytes_read; int prev_item_offset = 0; - uint8_t prev_item[_I2S_DMA_BUFFER_SIZE*4]; - uint8_t _inputBuffer[_I2S_DMA_BUFFER_SIZE*4]; + uint8_t prev_item[_i2s_dma_buffer_size*4]; + uint8_t _inputBuffer[_i2s_dma_buffer_size*4]; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -620,7 +645,7 @@ void I2SClass::onTransferComplete() } // prev_item_valid if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer - do{ // "send to esp i2s driver" loop + bytes_written = 0; item_size = 0; item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); @@ -634,7 +659,6 @@ void I2SClass::onTransferComplete() } // save item that was not written correctly for later vRingbufferReturnItem(_output_ring_buffer, item); } // Check received item - }while(item_size == bytes_written && item_size == single_dma_buf); } // don't read from almost empty buffer if(_onTransmit){ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 8af28cb59ee..0be04db2387 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -63,8 +63,6 @@ class I2SClass : public Stream // change pin setup and mode (default is Half Duplex) // Can be called only on initialized object (after begin) - int setStateDuplex(); - int setSckPin(int sckPin); int setFsPin(int fsPin); int setDataInPin(int inSdPin); @@ -79,6 +77,10 @@ class I2SClass : public Stream int getDataInPin(); int getDataOutPin(); + int setDuplex(); + int setSimplex(); + int isDuplex(); + void end(); // from Stream @@ -102,7 +104,8 @@ class I2SClass : public Stream void onTransmit(void(*)(void)); void onReceive(void(*)(void)); - void setBufferSize(int bufferSize); + int setBufferSize(int bufferSize); + int getBufferSize(); private: int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock); @@ -114,6 +117,8 @@ class I2SClass : public Stream int createCallbackTask(); static void onDmaTransferComplete(void*); + int _install_driver(); + int _uninstall_driver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); void _setDataInPin(int inSdPin); @@ -148,6 +153,8 @@ class I2SClass : public Stream QueueHandle_t _task_kill_cmd_semaphore_handle; RingbufHandle_t _input_ring_buffer; RingbufHandle_t _output_ring_buffer; + int _i2s_dma_buffer_size; + bool _driveClock; void (*_onTransmit)(void); void (*_onReceive)(void); From 5bb509036c91321f94939d137828d6284265e5d4 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 18 Aug 2021 09:50:39 +0200 Subject: [PATCH 18/94] Initial implementation of PDM support (untested) --- libraries/I2S/src/I2S.cpp | 42 +++++++++++++++++++++++++++++---------- libraries/I2S/src/I2S.h | 7 ++++--- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 4a5bd958ac8..4def0ede2c4 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -134,7 +134,7 @@ void I2SClass::destroyCallbackTask() } } -int I2SClass::_install_driver(){ +int I2SClass::_installDriver(){ esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); if(_driveClock){ @@ -144,12 +144,19 @@ int I2SClass::_install_driver(){ i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } if(_mode == I2S_ADC_DAC){ - if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); + #if SOC_I2S_SUPPORTS_ADC_DAC + if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); + return 0; // ERR + } + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); + #else + log_e("This chip does not support DAC / ADC"); return 0; // ERR - } - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); - }else{ // End of ADC/DAC mode; start of Normal mode + #endif + }else if(_mode == I2S_PHILIPS_MODE || + _mode == I2S_RIGHT_JUSTIFIED_MODE || + _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal mode if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ if(_bitsPerSample == 8){ log_e("ESP unfortunately does not support 8 bits per sample"); @@ -162,7 +169,14 @@ int I2SClass::_install_driver(){ log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } - } // Normal mode + }else if(_mode == I2S_PDM){ + #if SOC_I2S_SUPPORTS_PDM + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); + #else + log_e("This chip does not support PDM"); + return 0; // ERR + #endif + } // Mode esp_i2s::i2s_config_t i2s_config = { .mode = i2s_mode, .sample_rate = _sampleRate, @@ -174,10 +188,16 @@ int I2SClass::_install_driver(){ .dma_buf_len = _i2s_dma_buffer_size }; - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + if(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver log_e("ERROR could not install i2s driver"); return 0; // ERR } + if(_i2sEventQueue == NULL){ + log_e("ERROR i2s driver did not create event queue"); + //return 0; // ERR + }else{ + log_d("DEBUG MSG i2s event queue exists"); + } if(_mode == I2S_ADC_DAC){ esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; @@ -239,6 +259,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc switch (mode) { case I2S_PHILIPS_MODE: case I2S_ADC_DAC: + case I2S_PDM: break; case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP @@ -257,7 +278,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc return 0; // ERR } - if(!_install_driver()){ + if(!_installDriver()){ return 0; // ERR } @@ -411,7 +432,7 @@ int I2SClass::getDataOutPin(){ return _outSdPin; } -int I2SClass::_uninstall_driver(){ +int I2SClass::_uninstallDriver(){ // TODO return 1; // Ok } @@ -624,6 +645,7 @@ void I2SClass::onTransferComplete() xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); + configASSERT(_i2sEventQueue); xQueueAddToSet(_i2sEventQueue, xQueueSet); xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 0be04db2387..42250d10128 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -47,7 +47,8 @@ typedef enum { I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, I2S_LEFT_JUSTIFIED_MODE, - I2S_ADC_DAC + I2S_ADC_DAC, + I2S_PDM } i2s_mode_t; class I2SClass : public Stream @@ -117,8 +118,8 @@ class I2SClass : public Stream int createCallbackTask(); static void onDmaTransferComplete(void*); - int _install_driver(); - int _uninstall_driver(); + int _installDriver(); + int _uninstallDriver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); void _setDataInPin(int inSdPin); From fbfe7c7a87d4f0f0771bed6bcad7c4de5fb9fccb Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 20 Aug 2021 13:16:26 +0200 Subject: [PATCH 19/94] Added esp-dsp in CMakeLists.txt to compile in IDF --- CMakeLists.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae0f78cf9c0..256b9ca81f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,15 @@ set(CORE_SRCS set(LIBRARY_SRCS libraries/ArduinoOTA/src/ArduinoOTA.cpp + libraries/ArduinoSound/src/AmplitudeAnalyzer.cpp + libraries/ArduinoSound/src/AudioAnalyzer.cpp + libraries/ArduinoSound/src/AudioIn.cpp + libraries/ArduinoSound/src/AudioInI2S.cpp + libraries/ArduinoSound/src/AudioOut.cpp + libraries/ArduinoSound/src/AudioOutI2S.cpp + libraries/ArduinoSound/src/es8388.cpp + libraries/ArduinoSound/src/FFTAnalyzer.cpp + libraries/ArduinoSound/src/SDWaveFile.cpp libraries/AsyncUDP/src/AsyncUDP.cpp libraries/BluetoothSerial/src/BluetoothSerial.cpp libraries/BluetoothSerial/src/BTAddress.cpp @@ -128,11 +137,11 @@ set(BLE_SRCS libraries/BLE/src/GeneralUtils.cpp ) - set(includedirs variants/${IDF_TARGET}/ cores/esp32/ libraries/ArduinoOTA/src + libraries/ArduinoSound/src/ libraries/AsyncUDP/src libraries/BLE/src libraries/BluetoothSerial/src @@ -201,6 +210,8 @@ function(maybe_add_component component_name) endif() endfunction() +maybe_add_component(esp-dsp) + if(IDF_TARGET MATCHES "esp32" AND CONFIG_ESP_RMAKER_TASK_STACK) maybe_add_component(esp_rainmaker) maybe_add_component(qrcode) From 9cb2d3d14affdbfd6ca87be7d5e267aca66a4f61 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 1 Sep 2021 17:07:54 +0200 Subject: [PATCH 20/94] Pre-release changes --- libraries/I2S/src/I2S.cpp | 239 ++++++++++++++++++++++---------------- libraries/I2S/src/I2S.h | 16 ++- 2 files changed, 148 insertions(+), 107 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 4def0ede2c4..fcf546f7869 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,7 +21,6 @@ #include "I2S.h" #include "freertos/semphr.h" - #define _I2S_EVENT_QUEUE_LENGTH 16 #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -94,7 +93,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, int I2SClass::createCallbackTask() { - int stack_size = 15000; + int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); @@ -129,9 +128,7 @@ void I2SClass::destroyCallbackTask() } vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore - }else{ // callback handle check - log_e("Could not destroy callback"); - } + } // callback handle check } int I2SClass::_installDriver(){ @@ -187,17 +184,19 @@ int I2SClass::_installDriver(){ .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _i2s_dma_buffer_size }; - - if(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver - log_e("ERROR could not install i2s driver"); - return 0; // ERR - } - if(_i2sEventQueue == NULL){ - log_e("ERROR i2s driver did not create event queue"); - //return 0; // ERR - }else{ - log_d("DEBUG MSG i2s event queue exists"); - } + // Install and start i2s driver + while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ + // double buffer size + log_w("WARNING i2s driver install failed; Trying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); + if(2*_i2s_dma_buffer_size <= 1024){ + setBufferSize(2*_i2s_dma_buffer_size); + }else if(_i2s_dma_buffer_size < 1024){ + setBufferSize(1024); + }else{ + log_e("ERROR i2s driver install failed"); + return 0; // ERR + } + } //try installing with increasing size if(_mode == I2S_ADC_DAC){ esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; @@ -224,7 +223,7 @@ int I2SClass::_installDriver(){ return 1; // OK } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) +int I2SClass::begin(int mode, int sampleRate, int bitsPerSample) { // master mode (driving clock and frame select pins - output) return begin(mode, sampleRate, bitsPerSample, true); @@ -233,14 +232,14 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) int I2SClass::begin(int mode, int bitsPerSample) { log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ - Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ + Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\n\ \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); return 0; // ERR // slave mode (not driving clock and frame select pin - input) //return begin(mode, 0, bitsPerSample, false); } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) +int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock) { if(_initialized){ end(); @@ -279,10 +278,12 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if(!_installDriver()){ + _initialized = false; return 0; // ERR } if(!createCallbackTask()){ + _initialized = false; return 0; // ERR } @@ -389,21 +390,11 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ } int I2SClass::setDuplex(){ - /* - if(_inSdPin < 0 || _outSdPin < 0){ - log_e("I2S cannot set Duplex - one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); - return 0; // ERR - } - */ _state = I2S_STATE_DUPLEX; return 1; } int I2SClass::setSimplex(){ - if(_sdPin < 0){ - log_e("I2S cannot set Simplex - shared data pin is not set\n data pin = %d", _sdPin); - return 0; // ERR - } _state = I2S_STATE_IDLE; return 1; } @@ -432,9 +423,15 @@ int I2SClass::getDataOutPin(){ return _outSdPin; } -int I2SClass::_uninstallDriver(){ - // TODO - return 1; // Ok +void I2SClass::_uninstallDriver(){ + if(_mode == I2S_ADC_DAC){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } } void I2SClass::end() @@ -443,15 +440,8 @@ void I2SClass::end() destroyCallbackTask(); if(_initialized){ - - if(_mode == I2S_ADC_DAC){ - esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); - } - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + _uninstallDriver(); _initialized = false; - if(_state != I2S_STATE_DUPLEX){ - _state = I2S_STATE_IDLE; - } } _onTransmit = NULL; _onReceive = NULL; @@ -538,13 +528,37 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) return write((const void*)buffer, size); } -size_t I2SClass::write(const void *buffer, size_t size) +// blocking version of write +// TODO add timeout +size_t I2SClass::write_blocking(const void *buffer, size_t size) +{ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { + if(!enableTransmitter()){ + return 0; // There was an error switching to transmitter + } + } + // TODO add timeout + while(availableForWrite() < size){ + yield(); + } + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + return size; + }else{ + return 0; + } +} + +// non-blocking version of write +size_t I2SClass::write_nonblocking(const void *buffer, size_t size) { if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!enableTransmitter()){ return 0; // There was an error switching to transmitter } } + if(availableForWrite() < size){ + flush(); + } if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ return size; }else{ @@ -552,6 +566,11 @@ size_t I2SClass::write(const void *buffer, size_t size) } } +size_t I2SClass::write(const void *buffer, size_t size){ + //return write_blocking(buffer, size); + return write_nonblocking(buffer, size); +} + int I2SClass::peek() { // TODO @@ -596,10 +615,9 @@ int I2SClass::setBufferSize(int bufferSize) if(bufferSize >= 8 && bufferSize <= 1024){ _i2s_dma_buffer_size = bufferSize; if(_initialized){ - end(); - return begin(_mode, _sampleRate, _bitsPerSample, _driveClock); + _uninstallDriver(); + return _installDriver(); } - return 1; // OK }else{ return 0; // ERR @@ -628,21 +646,70 @@ int I2SClass::enableReceiver() return 1; // Ok } +void I2SClass::_tx_done_routine(uint8_t* prev_item){ + static bool prev_item_valid = false; + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + static size_t item_size = 0; + static size_t prev_item_size = 0; + static void *item = NULL; + static int prev_item_offset = 0; + static size_t bytes_written; + + if(prev_item_valid){ // use item from previous round + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); + if(prev_item_size == bytes_written){ + prev_item_valid = false; + } // write size check + prev_item_offset = bytes_written; + prev_item_size -= bytes_written; + } // prev_item_valid + + if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer + bytes_written = 0; + item_size = 0; + //if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ // save item that was not written correctly for later + memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); + prev_item_size = item_size - bytes_written; + prev_item_offset = 0; + prev_item_valid = true; + } // save item that was not written correctly for later + vRingbufferReturnItem(_output_ring_buffer, item); + } // Check received item + } // don't read from almost empty buffer + + if(_onTransmit){ + _onTransmit(); + } // user callback +} + +void I2SClass::_rx_done_routine(){ + static size_t bytes_read; + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4); + + size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ + log_w("I2S failed to send item from DMA to internal buffer\n"); + }else{ + if (_onReceive) { + _onReceive(); + } // user callback + } // xRingbufferSendComplete + free(_inputBuffer); +} + + void I2SClass::onTransferComplete() { + uint8_t prev_item[_i2s_dma_buffer_size*4]; static QueueSetHandle_t xQueueSet; - const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); QueueSetMemberHandle_t xActivatedMember; - esp_i2s::i2s_event_type_t i2s_event; - size_t item_size = 0; - size_t prev_item_size = 0; - void *item = NULL; - bool prev_item_valid = false; - size_t bytes_written, bytes_read; - int prev_item_offset = 0; - uint8_t prev_item[_i2s_dma_buffer_size*4]; - uint8_t _inputBuffer[_i2s_dma_buffer_size*4]; - + esp_i2s::i2s_event_t i2s_event; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); configASSERT(_i2sEventQueue); @@ -650,62 +717,30 @@ void I2SClass::onTransferComplete() xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); + //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // defaul + xActivatedMember = xQueueSelectFromSet(xQueueSet, 5); // hack + // TODO try just queue receive at the timeout if(xActivatedMember == _task_kill_cmd_semaphore_handle){ xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop - }else if(xActivatedMember == _i2sEventQueue){ - xQueueReceive(_i2sEventQueue, &i2s_event, 0); - if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ - if(prev_item_valid){ // use item from previous round - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); - if(prev_item_size == bytes_written){ - prev_item_valid = false; - } // write size check - prev_item_offset = bytes_written; - prev_item_size -= bytes_written; - } // prev_item_valid - - if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer - - bytes_written = 0; - item_size = 0; - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); - if (item != NULL){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); - if(item_size != bytes_written){ // save item that was not written correctly for later - memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); - prev_item_size = item_size - bytes_written; - prev_item_offset = 0; - prev_item_valid = true; - } // save item that was not written correctly for later - vRingbufferReturnItem(_output_ring_buffer, item); - } // Check received item - } // don't read from almost empty buffer - - if(_onTransmit){ - _onTransmit(); - } // user callback - - }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ - size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); - if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ - log_w("I2S failed to send item from DMA to internal buffer\n"); - }else{ - if (_onReceive) { - _onReceive(); - } // user callback - } // xRingbufferSendComplete - } // RX Done - } // Queue set (I2S event or kill command) + //}else if(xActivatedMember == _i2sEventQueue){ // default + }else if(xActivatedMember == _i2sEventQueue || xActivatedMember == NULL){ // hack + if(uxQueueMessagesWaiting(_i2sEventQueue)){ + xQueueReceive(_i2sEventQueue, &i2s_event, 0); + if(i2s_event.type == esp_i2s::I2S_EVENT_TX_DONE){ + _tx_done_routine(prev_item); + }else if(i2s_event.type == esp_i2s::I2S_EVENT_RX_DONE){ + _rx_done_routine(); + } // RX Done + } // queue not empty + }else{ + } } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); vTaskDelete(NULL); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 42250d10128..6f8bce077dd 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -58,7 +58,7 @@ class I2SClass : public Stream I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin); I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin); // set duplex // the SCK and FS pins are driven as outputs using the sample rate - int begin(int mode, long sampleRate, int bitsPerSample); + int begin(int mode, int sampleRate, int bitsPerSample); // the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); @@ -101,14 +101,16 @@ class I2SClass : public Stream //size_t write(int); size_t write(int32_t); size_t write(const void *buffer, size_t size); + size_t write_blocking(const void *buffer, size_t size); + size_t write_nonblocking(const void *buffer, size_t size); void onTransmit(void(*)(void)); void onReceive(void(*)(void)); int setBufferSize(int bufferSize); - int getBufferSize(); + int getBufferSize(); private: - int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock); + int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock); int enableTransmitter(); int enableReceiver(); @@ -119,7 +121,7 @@ class I2SClass : public Stream static void onDmaTransferComplete(void*); int _installDriver(); - int _uninstallDriver(); + void _uninstallDriver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); void _setDataInPin(int inSdPin); @@ -143,7 +145,8 @@ class I2SClass : public Stream i2s_state_t _state; int _bitsPerSample; - long _sampleRate; + uint32_t _sampleRate; // + //int _sampleRate; int _mode; uint16_t _buffer_byte_size; @@ -157,6 +160,9 @@ class I2SClass : public Stream int _i2s_dma_buffer_size; bool _driveClock; + void _tx_done_routine(uint8_t* prev_item); + void _rx_done_routine(); + void (*_onTransmit)(void); void (*_onReceive)(void); }; From 3bdb4e5c9bfb60bdfdc10d1076b691509837e570 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 2 Sep 2021 09:26:53 +0200 Subject: [PATCH 21/94] Extended SimpleTone example with DAC settings --- .../I2S/examples/SimpleTone/SimpleTone.ino | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index e3ad31b8055..f2b2ab194f9 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -3,7 +3,7 @@ and sample rate. Then outputs the data using the I2S interface to a MAX08357 I2S Amp Breakout board. - Circuit: + I2S Circuit: * Arduino/Genuino Zero, MKR family and Nano 33 IoT * MAX08357: * GND connected GND @@ -12,8 +12,19 @@ * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + DAC Circuit: + * ESP32 or ESP32-S2 + * Audio amplifier + - Note: + - ESP32 has DAC on GPIO pins 25 and 26. + - ESP32-S2 has DAC on GPIO pins 17 and 18. + - Connect speaker(s) or headphones. + created 17 November 2016 by Sandeep Mistry + For ESP extended by + Tomas Pilny + 2nd September 2021 */ #include @@ -27,12 +38,16 @@ const int halfWavelength = (sampleRate / frequency); // half wavelength of squar short sample = amplitude; // current sample value int count = 0; +//i2s_mode_t mode = I2S_PHILIPS_MODE; +i2s_mode_t mode = I2S_ADC_DAC; + + 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, bps)) { + if (!I2S.begin(mode, sampleRate, bps)) { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } @@ -45,9 +60,11 @@ void loop() { sample = -1 * sample; } - // write the same sample twice, once for left and once for the right channel - I2S.write(sample); I2S.write(sample); + // write the same sample twice, once for left and once for the right channel + if(mode == I2S_PHILIPS_MODE){ + I2S.write(sample); + } // increment the counter for the next sample count++; From 8c8d3578d3af8b9e614250f7a8a048528e505184 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 2 Sep 2021 09:59:03 +0200 Subject: [PATCH 22/94] Removed ArduinoSound from CMakeLists --- CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 256b9ca81f9..a4fc53d01d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,15 +45,6 @@ set(CORE_SRCS set(LIBRARY_SRCS libraries/ArduinoOTA/src/ArduinoOTA.cpp - libraries/ArduinoSound/src/AmplitudeAnalyzer.cpp - libraries/ArduinoSound/src/AudioAnalyzer.cpp - libraries/ArduinoSound/src/AudioIn.cpp - libraries/ArduinoSound/src/AudioInI2S.cpp - libraries/ArduinoSound/src/AudioOut.cpp - libraries/ArduinoSound/src/AudioOutI2S.cpp - libraries/ArduinoSound/src/es8388.cpp - libraries/ArduinoSound/src/FFTAnalyzer.cpp - libraries/ArduinoSound/src/SDWaveFile.cpp libraries/AsyncUDP/src/AsyncUDP.cpp libraries/BluetoothSerial/src/BluetoothSerial.cpp libraries/BluetoothSerial/src/BTAddress.cpp @@ -141,7 +132,6 @@ set(includedirs variants/${IDF_TARGET}/ cores/esp32/ libraries/ArduinoOTA/src - libraries/ArduinoSound/src/ libraries/AsyncUDP/src libraries/BLE/src libraries/BluetoothSerial/src From adb66dc22c40d2bdbdb50f562e0e48864f84a098 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 18 Jun 2021 08:49:12 +0200 Subject: [PATCH 23/94] [FEATURE] Basic implementation of Arduino's I2S library --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 88 ++++ .../InputSerialPlotter/InputSerialPlotter.ino | 47 ++ .../I2S/examples/SimpleTone/SimpleTone.ino | 55 ++ libraries/I2S/keywords.txt | 28 + libraries/I2S/library.properties | 9 + libraries/I2S/src/I2S.cpp | 486 ++++++++++++++++++ libraries/I2S/src/I2S.h | 138 +++++ 7 files changed, 851 insertions(+) create mode 100644 libraries/I2S/examples/ADCPlotter/ADCPlotter.ino 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/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino new file mode 100644 index 00000000000..971172ead77 --- /dev/null +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -0,0 +1,88 @@ +/* + This example is only for ESP devices + This example demonstrates usage of integrated Digital to Analog Converter (DAC) + You can display sound wave from audio device, or just measure voltage. + + To display audio prepare circuit found in following link or drafted as ASCII art + https://forum.arduino.cc/index.php?topic=567581.0 + (!) Note that unlike in the link we are connecting the supply line to 3.3V (not 5V) + because ADC can measure only up to around 3V. Anything above 3V will be very inaccurate. + + ^ +3.3V + | + _ + | |10k + |_| + | | |10uF + GPIO34-------------*------------| |----------- line in +(Default ADC pin) | +| | + | + _ + | |10k + |_| + | + | + V GND + +Connect hot wire of your audio to Line in and GNd wire of audio cable to common ground (GND) + +Second option to measure voltage on trimmer / potentiometer has following connection + ^ +3.3V + | + _ + | | + GPIO34----------->| | +(Default ADC pin) |_| + | + | + _ + | | optional resistor + |_| + | + | + V GND +Optional resistor will decrease read value. + +Steps to run: +1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board +2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + On your board press and hold Boot button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. +3. Open plotter + Tools -> Serial Plotter + Enjoy + +Created by Tomas Pilny +on 17th June 2021 +*/ + +#include + +void setup() { + disableCore0WDT(); + disableCore1WDT(); + // 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 32-bits per sample + if (!I2S.begin(I2S_ADC_DAC, 8000, 16)) { + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } +} + +void loop() { + // read a sample + int sample = I2S.read(); + Serial.println(sample); + delay(100); + } diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino new file mode 100644 index 00000000000..3df8712bc64 --- /dev/null +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -0,0 +1,47 @@ +/* + 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) + + Circuit: + * Arduino/Genuino Zero, MKR family and Nano 33 IoT + * ICS43432: + * GND connected GND + * 3.3V connected to 3.3V (Zero, Nano, ESP32), VCC (MKR) + * WS connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32) + * CLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) + * SD connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + created 17 November 2016 + by Sandeep Mistry + */ + +#include + +void setup() { + disableCore0WDT(); + disableCore1WDT(); + // 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 32-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) { + 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 00000000000..2c88cad2981 --- /dev/null +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -0,0 +1,55 @@ +/* + 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. + + Circuit: + * Arduino/Genuino Zero, MKR family and Nano 33 IoT + * MAX08357: + * GND connected GND + * VIN connected 5V + * LRC connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32) + * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) + * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + + created 17 November 2016 + by Sandeep Mistry + */ + +#include + +const int frequency = 1250; // frequency of square wave in Hz +const int amplitude = 32767; // amplitude of square wave +const int sampleRate = 16000; // 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() { + disableCore0WDT(); + disableCore1WDT(); + 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 00000000000..217868dea36 --- /dev/null +++ b/libraries/I2S/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map I2S +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +I2S KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +begin KEYWORD2 +end KEYWORD2 + +onReceive KEYWORD2 +onTransmit KEYWORD2 + +setBufferSize KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +I2S_PHILIPS_MODE LITERAL1 +I2S_RIGHT_JUSTIFIED_MODE LITERAL1 +I2S_LEFT_JUSTIFIED_MODE LITERAL1 +I2S_ADC_DAC LITERAL1 diff --git a/libraries/I2S/library.properties b/libraries/I2S/library.properties new file mode 100644 index 00000000000..99ac3080db5 --- /dev/null +++ b/libraries/I2S/library.properties @@ -0,0 +1,9 @@ +name=I2S +version=1.0 +author=Arduino +maintainer=Arduino +sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for Arduino Zero. +paragraph= +category=Communication +url=http://www.arduino.cc/en/Reference/I2S +architectures=esp32 diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp new file mode 100644 index 00000000000..279a76db903 --- /dev/null +++ b/libraries/I2S/src/I2S.cpp @@ -0,0 +1,486 @@ +/* + 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 +*/ + +#include +#include +#include "I2S.h" + +int I2SClass::_beginCount = 0; + +I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : + _deviceIndex(0), + _clockGenerator(clockGenerator), + _sdPin(sdPin), // shared data pin + _inSdPin(sdPin), // input data pin + _outSdPin(sdPin), // output data pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin + + _state(I2S_STATE_IDLE), + _dmaChannel(-1), + _bitsPerSample(0), + _sampleRate(0), + _mode(I2S_PHILIPS_MODE), + + _dmaTransferInProgress(false), + _initialized(false), + _callbackTaskHandle(NULL), + _i2sEventQueue(NULL), + + _onTransmit(NULL), + _onReceive(NULL) +{ +} + +I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex + _deviceIndex(0), + _clockGenerator(clockGenerator), + _sdPin(inSdPin), // shared data pin + _inSdPin(inSdPin), // input data pin + _outSdPin(outSdPin), // output data pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin + + _state(I2S_STATE_DUPLEX), + _dmaChannel(-1), + _bitsPerSample(0), + _sampleRate(0), + _mode(I2S_PHILIPS_MODE), + + _dmaTransferInProgress(false), + _initialized(false), + _callbackTaskHandle(NULL), + _i2sEventQueue(NULL), + + _onTransmit(NULL), + _onReceive(NULL) +{ +} + +void I2SClass::createCallbackTask() +{ + int stack_size = 3000; + if(_callbackTaskHandle == NULL){ + _xCallbackEventBits = xEventGroupCreate(); + vTaskDelay(1); + xTaskCreate( + onDmaTransferComplete, // Function to implement the task + "onDmaTransferComplete", // Name of the task + stack_size, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_callbackTaskHandle // Task handle. + ); + } +} + +void I2SClass::destroyCallbackTask() +{ + if(_callbackTaskHandle != NULL){ + if(xTaskGetCurrentTaskHandle() == _callbackTaskHandle){ + return; + } + EventBits_t uxReturn; + int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_CMD_END_0); // Command callback task to finish and quit infinite loop + + // wait on confirmation from callback task that it exited infinite loop + uxReturn = xEventGroupWaitBits(_xCallbackEventBits, + _I2S_CALLBACK_TASK_END_CONFIRMED_1, + pdFALSE, // Don't clear received bits + pdFALSE, // logical OR for waiting bits + portMAX_DELAY); // wait indefinitely + if((uxReturn & _I2S_CALLBACK_TASK_END_CONFIRMED_1) == _I2S_CALLBACK_TASK_END_CONFIRMED_1){ + vTaskDelete(_callbackTaskHandle); + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + vEventGroupDelete(_xCallbackEventBits); + _xCallbackEventBits = NULL; + vTaskDelay(1); // memory cleanup + } // group bits - check confirmation + } // callback handle check +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) +{ + // master mode (driving clock and frame select pins - output) + return begin(mode, sampleRate, bitsPerSample, true); +} + +int I2SClass::begin(int mode, int bitsPerSample) +{ + Serial.println("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP"); + Serial.println("Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below"); + Serial.println("\tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); + return 0; // ERR + // slave mode (not driving clock and frame select pin - input) + //return begin(mode, 0, bitsPerSample, false); +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) +{ + if(_initialized){ + end(); + } + + if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + return 0; // ERR + } + + // TODO implement left / right justified modes + switch (mode) { + case I2S_PHILIPS_MODE: + //case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + //case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + case I2S_ADC_DAC: + break; + + case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + default: + // invalid mode + return 0; // ERR + } + + _mode = mode; + _sampleRate = sampleRate; + _bitsPerSample = bitsPerSample; + + esp_i2s::i2s_mode_t i2s_mode; + if(driveClock){ + i2s_mode = esp_i2s::I2S_MODE_MASTER; + }else{ + // TODO there will much more work with slave mode + i2s_mode = esp_i2s::I2S_MODE_SLAVE; + } + + if(_mode == I2S_ADC_DAC){ + if(bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + return 0; // ERR + } + + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_TX | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); + + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = _sampleRate, + .bits_per_sample = esp_i2s::I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 8, + .dma_buf_len = 512 // buffer length in Bytes + }; + + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)){ // Install and start i2s driver + return 0; // ERR + } + + esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; + esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // + esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); + esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ + Serial.println("i2s_set_pin err"); + return 0; // ERR + } + + esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); + esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); + esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); + + }else{ // normal I2S mode without ADC/DAC + if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ + Serial.println("I2S.begin(): invalid bits per second"); + // ESP does support 24 bps, however for the compatibility + // with original Arduino implementation it is not allowed + return 0; // ERR + } + + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); + esp_i2s::i2s_channel_fmt_t i2s_channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT; + esp_i2s::i2s_comm_format_t i2s_comm_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT); // 0x01 | 0x04 + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = _sampleRate, + .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t) _bitsPerSample, + .channel_format = i2s_channel_format, + .communication_format = i2s_comm_format, + .intr_alloc_flags = 1, + .dma_buf_count = 8, + .dma_buf_len = 512 // buffer length in Bytes + }; + + esp_i2s::i2s_pin_config_t pin_config; + if (_state == I2S_STATE_DUPLEX){ + pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + }else{ // half-duplex + pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_in_num = _sdPin + }; + } + + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)) { + Serial.println("i2s_driver_install err"); + return 0; // ERR + } + + if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { + Serial.println("i2s_set_pin err"); + end(); + return 0; // ERR + } + } // ADC/DAC or normal I2S mode + createCallbackTask(); + _initialized = true; + return 1; // OK +} + +void I2SClass::end() +{ + if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ + destroyCallbackTask(); + } + + if(_initialized){ + if(_mode == I2S_ADC_DAC){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + _initialized = false; + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } + } + _onTransmit = NULL; + _onReceive = NULL; +} + +// available to read +int I2SClass::available() +{ + // There is no actual way to tell in ESP + return 8; +} + +union i2s_sample_t { + uint8_t b8; + int16_t b16; + int32_t b32; +}; + +int I2SClass::read() +{ + i2s_sample_t sample; + sample.b32 = 0; + int bytes_read = read(&sample, _bitsPerSample / 8); + + if (_bitsPerSample == 32) { + return sample.b32; + } else if (_bitsPerSample == 16) { + return sample.b16; + } else if (_bitsPerSample == 8) { + return sample.b8; + } else { + return 0; + } + return 0; +} + +int I2SClass::read(void* buffer, size_t size) +{ + static long debug_timer_prev = 0; + if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { + if(!enableReceiver()){ + return 0; // There was an error switching to receiver + } + } + int read; + debug_timer_prev = millis(); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 10); + + if(_mode == I2S_ADC_DAC){ + for(int i = 0; i < read / 2; ++i){ + ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; + } + } + return read; +} + +/* +size_t I2SClass::write(int sample) +{ + return write((int32_t)sample); +} +*/ + +size_t I2SClass::write(int32_t sample) +{ + return write(&sample, 1); +} + +size_t I2SClass::write(const void *buffer, size_t size) +{ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { + if(!enableTransmitter()){ + return 0; // There was an error switching to transmitter + } + } + size_t bytes_written; + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 10); + return bytes_written; +} + +int I2SClass::peek() +{ + // TODO + Serial.println("I2SClass: peek() is not implemented for ESP"); + return 0; +} + +void I2SClass::flush() +{ + // do nothing, writes are DMA triggered in ESP +} + +size_t I2SClass::write(uint8_t data) +{ + return write((int32_t)data); +} + +size_t I2SClass::write(const uint8_t *buffer, size_t size) +{ + return write((const void*)buffer, size); +} + +int I2SClass::availableForWrite() +{ + // There is no actual way to tell in ESP + return 512; +} + +void I2SClass::onTransmit(void(*function)(void)) +{ + _onTransmit = function; +} + +void I2SClass::onReceive(void(*function)(void)) +{ + _onReceive = function; +} + +void I2SClass::setBufferSize(int bufferSize) +{ + Serial.println("I2SClass::setBufferSize() does nothing for ESP"); + // does nothing in ESP +} + +int I2SClass::enableTransmitter() +{ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _sdPin, + //.data_out_num = 26, // TODO + .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + _state = I2S_STATE_IDLE; + return 0; // ERR + } + _state = I2S_STATE_TRANSMITTER; + } + return 1; // Ok +} + +int I2SClass::enableReceiver() +{ + if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_in_num = _sdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + _state = I2S_STATE_IDLE; + return 0; // ERR + } + _state = I2S_STATE_RECEIVER; + } + return 1; // Ok +} + +void I2SClass::onTransferComplete() +{ + EventBits_t uxReturn; + esp_i2s::i2s_event_type_t i2s_event; + UBaseType_t uxHighWaterMark; // debug + while(true){ + if(_xCallbackEventBits != NULL){ + uxReturn = xEventGroupGetBits(_xCallbackEventBits); + if((uxReturn & _I2S_CALLBACK_TASK_CMD_END_0) == _I2S_CALLBACK_TASK_CMD_END_0){ + break; // from the infinite loop + } + } + + if(_i2sEventQueue != NULL){ + if(pdPASS == xQueueReceive(_i2sEventQueue, &i2s_event, 10)){ + if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + if(_onTransmit){ + _onTransmit(); + } + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ + if (_onReceive) { + _onReceive(); + } + } // if event TX or RX + } // event queue receive + } // queue not NULL + } // infinite loop + int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_END_CONFIRMED_1); // Notify end() function this task has properly exited infinite loop and is killing itself + while(true){ + // wait for death + } +} + +void I2SClass::onDmaTransferComplete(void*) +{ + I2S.onTransferComplete(); +} + +#if I2S_INTERFACES_COUNT > 0 + #ifdef ESP_PLATFORM + // change pins? + #define I2S_DEVICE 0 + #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP + #define PIN_I2S_SCK 5 + #define PIN_I2S_FS 25 + #define PIN_I2S_SD 26 + #define PIN_I2S_SD_OUT 35 + #endif +I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex +//I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex +#endif diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h new file mode 100644 index 00000000000..9934dd03797 --- /dev/null +++ b/libraries/I2S/src/I2S.h @@ -0,0 +1,138 @@ +/* + 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 +namespace esp_i2s { + #include "driver/i2s.h" // ESP specific i2s driver +} + + +#define I2S_HAS_SET_BUFFER_SIZE 1 + +#ifdef CONFIG_IDF_TARGET_ESP32 + #define I2S_INTERFACES_COUNT 2 +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32S2 + #define I2S_INTERFACES_COUNT 1 +#endif + +#define INCLUDE_xTaskGetCurrentTaskHandle 1 + +typedef enum { + I2S_PHILIPS_MODE, + I2S_RIGHT_JUSTIFIED_MODE, + I2S_LEFT_JUSTIFIED_MODE, + I2S_ADC_DAC +} i2s_mode_t; + +class I2SClass : public Stream +{ +public: + // the device index and pins must map to the "COM" pads in Table 6-1 of the datasheet + I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin); + I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin); // set duplex + // the SCK and FS pins are driven as outputs using the sample rate + int begin(int mode, long sampleRate, int bitsPerSample); + // the SCK and FS pins are inputs, other side controls sample rate + int begin(int mode, 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(int); + size_t write(int32_t); + size_t write(const void *buffer, size_t size); + + void onTransmit(void(*)(void)); + void onReceive(void(*)(void)); + + void setBufferSize(int bufferSize); +private: + int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock); + + int enableTransmitter(); + int enableReceiver(); + void onTransferComplete(); + + void destroyCallbackTask(); + void createCallbackTask(); + + static void onDmaTransferComplete(void*); + +private: + typedef enum { + I2S_STATE_IDLE, + I2S_STATE_TRANSMITTER, + I2S_STATE_RECEIVER, + I2S_STATE_DUPLEX + } i2s_state_t; + + static int _beginCount; + + uint8_t _deviceIndex; + uint8_t _clockGenerator; + uint8_t _sdPin; + uint8_t _inSdPin; + uint8_t _outSdPin; + uint8_t _sckPin; + uint8_t _fsPin; + + + i2s_state_t _state; + int _dmaChannel; + int _bitsPerSample; + long _sampleRate; + int _mode; + + volatile bool _dmaTransferInProgress; + + bool _initialized; + TaskHandle_t _callbackTaskHandle; + QueueHandle_t _i2sEventQueue; + + EventGroupHandle_t _xCallbackEventBits; + #define _I2S_CALLBACK_TASK_CMD_END_0 ( 1 << 0 ) + #define _I2S_CALLBACK_TASK_END_CONFIRMED_1 ( 1 << 1 ) + + void (*_onTransmit)(void); + void (*_onReceive)(void); +}; + +// "I2S" is already defined by the CMSIS device, undefine it so the I2SClass +// instance can be called I2S +#undef I2S + +extern I2SClass I2S; + +#endif From 45773315bc21c6e7f030f84a8cec2e41dc64ce1b Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 18 Jun 2021 10:00:00 +0200 Subject: [PATCH 24/94] [MAINTENANCE] Added I2S to CMakeLists --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd228a07def..08b8f866c5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(LIBRARY_SRCS libraries/HTTPClient/src/HTTPClient.cpp libraries/HTTPUpdate/src/HTTPUpdate.cpp libraries/LittleFS/src/LittleFS.cpp + libraries/I2S/src/I2S.cpp libraries/NetBIOS/src/NetBIOS.cpp libraries/Preferences/src/Preferences.cpp libraries/RainMaker/src/RMaker.cpp @@ -153,6 +154,7 @@ set(includedirs libraries/HTTPClient/src libraries/HTTPUpdate/src libraries/LittleFS/src + libraries/I2S/src libraries/NetBIOS/src libraries/Preferences/src libraries/RainMaker/src From 13a988ca91c2b540947e2eb3f6f87a1bc596c98c Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 18 Jun 2021 12:05:54 +0200 Subject: [PATCH 25/94] [MAINTENANCE] Joined task blocking (i2s event queue + kill command) --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 2 - .../InputSerialPlotter/InputSerialPlotter.ino | 2 - .../I2S/examples/SimpleTone/SimpleTone.ino | 2 - libraries/I2S/src/I2S.cpp | 89 ++++++++----------- libraries/I2S/src/I2S.h | 9 +- 5 files changed, 39 insertions(+), 65 deletions(-) diff --git a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino index 971172ead77..10a12bdc06c 100644 --- a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -63,8 +63,6 @@ on 17th June 2021 #include void setup() { - disableCore0WDT(); - disableCore1WDT(); // 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 diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index 3df8712bc64..743b1c29175 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -19,8 +19,6 @@ #include void setup() { - disableCore0WDT(); - disableCore1WDT(); // 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 diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 2c88cad2981..36464148c33 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -28,8 +28,6 @@ short sample = amplitude; // current sample value int count = 0; void setup() { - disableCore0WDT(); - disableCore1WDT(); Serial.begin(115200); Serial.println("I2S simple tone"); diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 279a76db903..853e31ada1d 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -37,10 +37,10 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _sampleRate(0), _mode(I2S_PHILIPS_MODE), - _dmaTransferInProgress(false), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), + _task_kill_cmd_semaphore_handle(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -62,10 +62,10 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _sampleRate(0), _mode(I2S_PHILIPS_MODE), - _dmaTransferInProgress(false), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), + _task_kill_cmd_semaphore_handle(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -75,9 +75,10 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, void I2SClass::createCallbackTask() { int stack_size = 3000; - if(_callbackTaskHandle == NULL){ - _xCallbackEventBits = xEventGroupCreate(); - vTaskDelay(1); + if(_callbackTaskHandle == NULL){ + if(_task_kill_cmd_semaphore_handle == NULL){ + _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); + } xTaskCreate( onDmaTransferComplete, // Function to implement the task "onDmaTransferComplete", // Name of the task @@ -91,26 +92,8 @@ void I2SClass::createCallbackTask() void I2SClass::destroyCallbackTask() { - if(_callbackTaskHandle != NULL){ - if(xTaskGetCurrentTaskHandle() == _callbackTaskHandle){ - return; - } - EventBits_t uxReturn; - int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_CMD_END_0); // Command callback task to finish and quit infinite loop - - // wait on confirmation from callback task that it exited infinite loop - uxReturn = xEventGroupWaitBits(_xCallbackEventBits, - _I2S_CALLBACK_TASK_END_CONFIRMED_1, - pdFALSE, // Don't clear received bits - pdFALSE, // logical OR for waiting bits - portMAX_DELAY); // wait indefinitely - if((uxReturn & _I2S_CALLBACK_TASK_END_CONFIRMED_1) == _I2S_CALLBACK_TASK_END_CONFIRMED_1){ - vTaskDelete(_callbackTaskHandle); - _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task - vEventGroupDelete(_xCallbackEventBits); - _xCallbackEventBits = NULL; - vTaskDelay(1); // memory cleanup - } // group bits - check confirmation + if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ + xSemaphoreGive(_task_kill_cmd_semaphore_handle); } // callback handle check } @@ -185,8 +168,8 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc .dma_buf_len = 512 // buffer length in Bytes }; - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)){ // Install and start i2s driver - return 0; // ERR + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + return 0; // ERR } esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; @@ -435,35 +418,35 @@ int I2SClass::enableReceiver() void I2SClass::onTransferComplete() { - EventBits_t uxReturn; + static QueueSetHandle_t xQueueSet; + QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; - UBaseType_t uxHighWaterMark; // debug - while(true){ - if(_xCallbackEventBits != NULL){ - uxReturn = xEventGroupGetBits(_xCallbackEventBits); - if((uxReturn & _I2S_CALLBACK_TASK_CMD_END_0) == _I2S_CALLBACK_TASK_CMD_END_0){ - break; // from the infinite loop - } - } + EventBits_t uxReturn; + + xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); + configASSERT(xQueueSet); + xQueueAddToSet(_i2sEventQueue, xQueueSet); + xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); - if(_i2sEventQueue != NULL){ - if(pdPASS == xQueueReceive(_i2sEventQueue, &i2s_event, 10)){ - if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ - if(_onTransmit){ - _onTransmit(); - } - }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ - if (_onReceive) { - _onReceive(); - } - } // if event TX or RX - } // event queue receive - } // queue not NULL - } // infinite loop - int ret = xEventGroupSetBits(_xCallbackEventBits, _I2S_CALLBACK_TASK_END_CONFIRMED_1); // Notify end() function this task has properly exited infinite loop and is killing itself while(true){ - // wait for death - } + xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); + if(xActivatedMember == _task_kill_cmd_semaphore_handle){ + xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); + break; // from the infinite loop + }else if(xActivatedMember == _i2sEventQueue){ + xQueueReceive(_i2sEventQueue, &i2s_event, 0); + if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + if(_onTransmit){ + _onTransmit(); + } + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ + if (_onReceive) { + _onReceive(); + } + } // if event TX or RX + } + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9934dd03797..994ec45788a 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -23,6 +23,7 @@ namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver } +#include "freertos/semphr.h" #define I2S_HAS_SET_BUFFER_SIZE 1 @@ -36,6 +37,7 @@ namespace esp_i2s { #endif #define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define _I2S_EVENT_QUEUE_LENGTH 10 typedef enum { I2S_PHILIPS_MODE, @@ -115,15 +117,10 @@ class I2SClass : public Stream long _sampleRate; int _mode; - volatile bool _dmaTransferInProgress; - bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; - - EventGroupHandle_t _xCallbackEventBits; - #define _I2S_CALLBACK_TASK_CMD_END_0 ( 1 << 0 ) - #define _I2S_CALLBACK_TASK_END_CONFIRMED_1 ( 1 << 1 ) + QueueHandle_t _task_kill_cmd_semaphore_handle; void (*_onTransmit)(void); void (*_onReceive)(void); From 019490c39c33ad7af2826574b8621e61f2699ed5 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 21 Jun 2021 17:11:12 +0200 Subject: [PATCH 26/94] [FEATURE] Added functions to set pins for both Input and Output --- libraries/I2S/src/I2S.cpp | 53 +++++++++++++++++++++++++++++++-------- libraries/I2S/src/I2S.h | 19 ++++++++------ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 853e31ada1d..227b9a838fc 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -26,8 +26,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _deviceIndex(0), _clockGenerator(clockGenerator), _sdPin(sdPin), // shared data pin - _inSdPin(sdPin), // input data pin - _outSdPin(sdPin), // output data pin + _inSdPin(-1), // input data pin + _outSdPin(-1), // output data pin _sckPin(sckPin), // clock pin _fsPin(fsPin), // frame (word) select pin @@ -208,14 +208,14 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc }; esp_i2s::i2s_pin_config_t pin_config; - if (_state == I2S_STATE_DUPLEX){ + if (_state == I2S_STATE_DUPLEX){ // duplex pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, .data_out_num = _outSdPin, .data_in_num = _inSdPin }; - }else{ // half-duplex + }else{ // simplex pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, @@ -240,6 +240,39 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc return 1; // OK } +int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ + _inSdPin = inSdPin; + _outSdPin = outSdPin; + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + return 0; // ERR + } + } + return 1; // OK +} + +int I2SClass::setSimplex(uint8_t sdPin){ + _sdPin = sdPin; + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_in_num = _sdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + return 0; // ERR + } + } + return 1; // OK +} + void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ @@ -385,8 +418,7 @@ int I2SClass::enableTransmitter() esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, - .data_out_num = _sdPin, - //.data_out_num = 26, // TODO + .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ @@ -405,7 +437,7 @@ int I2SClass::enableReceiver() .bck_io_num = _sckPin, .ws_io_num = _fsPin, .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, - .data_in_num = _sdPin + .data_in_num = _inSdPin != -1 ? _inSdPin : _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ _state = I2S_STATE_IDLE; @@ -446,6 +478,7 @@ void I2SClass::onTransferComplete() } // if event TX or RX } _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + } vTaskDelete(NULL); } @@ -456,14 +489,14 @@ void I2SClass::onDmaTransferComplete(void*) #if I2S_INTERFACES_COUNT > 0 #ifdef ESP_PLATFORM - // change pins? #define I2S_DEVICE 0 #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP #define PIN_I2S_SCK 5 #define PIN_I2S_FS 25 - #define PIN_I2S_SD 26 - #define PIN_I2S_SD_OUT 35 + #define PIN_I2S_SD 35 // ESP data in / codec data out (microphone) + #define PIN_I2S_SD_OUT 26 // ESP data out / codec data in (speakers) #endif + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex #endif diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 994ec45788a..9688a4712d4 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -56,6 +56,11 @@ class I2SClass : public Stream int begin(int mode, long sampleRate, int bitsPerSample); // the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); + + // change pin setup (default is Simplex) + int setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin); + int setSimplex(uint8_t sdPin); + void end(); // from Stream @@ -102,13 +107,13 @@ class I2SClass : public Stream static int _beginCount; - uint8_t _deviceIndex; - uint8_t _clockGenerator; - uint8_t _sdPin; - uint8_t _inSdPin; - uint8_t _outSdPin; - uint8_t _sckPin; - uint8_t _fsPin; + int _deviceIndex; + int _clockGenerator; + int _sdPin; + int _inSdPin; + int _outSdPin; + int _sckPin; + int _fsPin; i2s_state_t _state; From b18cb313422a2306f922495011644f80f1afb414 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 24 Jun 2021 11:24:08 +0200 Subject: [PATCH 27/94] [FEATURE] Added new example demonstrating usage of callbacks --- .../I2S/examples/Callbacks/Callbacks.ino | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 libraries/I2S/examples/Callbacks/Callbacks.ino diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino new file mode 100644 index 00000000000..c896a8ed044 --- /dev/null +++ b/libraries/I2S/examples/Callbacks/Callbacks.ino @@ -0,0 +1,116 @@ +/* + This example is only for ESP devices. + + This example demonstrates usage of callback functions and duplex operation. + Callback functions allow you to perform audio events pseudo parallel to your main loop. + This way you no longer need to poll for reading or writing data from/to I2S module. + + Unlike Arduino, ESP allows you to operate input and output simultaneously on separate pins. + +Hardware: + 1. ESP board with at least 4 GPIOs available + 2. I2S mirophone (for example this one https://www.adafruit.com/product/3421) + 3. I2S decoder (for example this one https://www.adafruit.com/product/3678) + 4. Headphones, or speaker(s) to be connected into the decoder + 5. Some connecting wires, USB cable and optionally a breadboard + + Wiring: + Note: If you need to use other than default pins you can change the default definition below this comment block + 1. Connect pins of both the microphone and decoder to common SCK and FS pins on ESP + 1.a SCK (Source Clock, or Bit Clock) connects by default to pin 5 + 1.b FS (Frame Select, or Left-Right Select) connects by default to pin 25 + 2. Connect data pin of your microphone to pin 35 + 3. Connect data pin of your decoder to pin 26 + 4. Connect power pins and other remaining pins of your modules according to their specific instructions + 5. Connect headphones/speaker(s) to decoder + 6. Connect ESP board to your computer via USB cable + + Steps to run: + 1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board + 2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + If loading doesn't start automatically, you may need to press and hold Boot + button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. + 3. Open plotter + Tools -> Serial Plotter + 4. Enjoy + Listen to generated square wave signal, while observing the wave signal recorded by your microphone + both at the same time thanks to ESP duplex ability. + + Tip: + Install ArduinoSound library and discover extended functionality of I2S + you can try ESP WiFi telephone, Record on SD card and Play back from it... + +Created by Tomas Pilny +on 23rd June 2021 +*/ + + +#include +const int sampleRate = 16000; // sample rate in Hz +const int bitsPerSample = 16; + +// code from SimpleTone example +const int frequency = 1250; // frequency of square wave in Hz +const int amplitude = 32767; // amplitude of square wave + +const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave + +short sample = amplitude; // current sample value +int count = 0; + +void outputCallback(){ + 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++; +} + +// code from InputSerialPlotter example +void inputCallback(){ + // read a sample + int sample = I2S.read(); + + if (sample) { + // if it's non-zero print value to serial + Serial.println(sample); + } +} + +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 32-bits per sample + if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) { + Serial.println("Failed to initialize I2S!"); + while (1); // do nothing + } + I2S.setHalfDuplex(); // Use two data pins (default pins are input=35, output=26) + + // Register our callback functions + I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) + I2S.onReceive(inputCallback); // Function inputCallback will be called each time I2S finishes receive operation (audio input) + Serial.println("Callbacks example setup done."); +} + +void loop() { + // loop task remains free for other work + delay(100); // Let the FreeRTOS reset the watchDogTimer +} \ No newline at end of file From d5a66dd7abfc33d6b831ebaddc9c049ab5fb4928 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 28 Jun 2021 09:58:17 +0200 Subject: [PATCH 28/94] [MAINTENANCE] Minor changes --- .../I2S/examples/Callbacks/Callbacks.ino | 13 +- libraries/I2S/src/I2S.cpp | 338 ++++++++++++------ libraries/I2S/src/I2S.h | 31 +- 3 files changed, 243 insertions(+), 139 deletions(-) diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino index c896a8ed044..105ef64133b 100644 --- a/libraries/I2S/examples/Callbacks/Callbacks.ino +++ b/libraries/I2S/examples/Callbacks/Callbacks.ino @@ -49,6 +49,13 @@ Created by Tomas Pilny on 23rd June 2021 */ +// If you need to change any of the default pins, simply uncomment chosen line and change the pin number +/* +#define PIN_I2S_SCK 33 +#define PIN_I2S_FS 16 +#define PIN_I2S_SD 17 +#define PIN_I2S_SD_OUT 4 +*/ #include const int sampleRate = 16000; // sample rate in Hz @@ -82,6 +89,8 @@ void inputCallback(){ // read a sample int sample = I2S.read(); + Serial.println(sample); // only for debug + if (sample) { // if it's non-zero print value to serial Serial.println(sample); @@ -100,9 +109,9 @@ void setup() { // start I2S at 8 kHz with 32-bits per sample if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) { Serial.println("Failed to initialize I2S!"); - while (1); // do nothing + while (1) delay(100); // do nothing } - I2S.setHalfDuplex(); // Use two data pins (default pins are input=35, output=26) + I2S.setAllPins(); // Register our callback functions I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 227b9a838fc..c7c8f139eac 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -19,12 +19,51 @@ #include #include #include "I2S.h" +#include "freertos/semphr.h" + +//#define _USE_SYS_VIEW 1 +#ifdef _USE_SYS_VIEW + #include "esp_sysview_trace.h" // debug + //#include "esp_heap_trace.h" // debug + #include "esp_log.h" // debug +#endif // _USE_SYS_VIEW + +#define _I2S_EVENT_QUEUE_LENGTH 10 +#define _I2S_DMA_BUFFER_SIZE 512 +#define _I2S_DMA_BUFFER_COUNT 8 +#define I2S_INTERFACES_COUNT SOC_I2S_NUM + +#ifndef I2S_DEVICE + #define I2S_DEVICE 0 +#endif + +#ifndef I2S_CLOCK_GENERATOR + #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP +#endif + +#ifndef PIN_I2S_SCK + #define PIN_I2S_SCK 5 +#endif + +#ifndef PIN_I2S_FS + #define PIN_I2S_FS 25 +#endif + +#ifndef PIN_I2S_SD + #define PIN_I2S_SD 26 +#endif + +#ifndef PIN_I2S_SD_IN + #define PIN_I2S_SD_IN 35 // 35 can be only input pin +#endif + -int I2SClass::_beginCount = 0; +#ifndef PIN_I2S_SD_OUT + #define PIN_I2S_SD_OUT 26 +#endif I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : - _deviceIndex(0), - _clockGenerator(clockGenerator), + _deviceIndex(deviceIndex), _sdPin(sdPin), // shared data pin _inSdPin(-1), // input data pin _outSdPin(-1), // output data pin @@ -32,7 +71,6 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_IDLE), - _dmaChannel(-1), _bitsPerSample(0), _sampleRate(0), _mode(I2S_PHILIPS_MODE), @@ -48,8 +86,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u } I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex - _deviceIndex(0), - _clockGenerator(clockGenerator), + _deviceIndex(deviceIndex), _sdPin(inSdPin), // shared data pin _inSdPin(inSdPin), // input data pin _outSdPin(outSdPin), // output data pin @@ -57,7 +94,6 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_DUPLEX), - _dmaChannel(-1), _bitsPerSample(0), _sampleRate(0), _mode(I2S_PHILIPS_MODE), @@ -84,7 +120,7 @@ void I2SClass::createCallbackTask() "onDmaTransferComplete", // Name of the task stack_size, // Stack size in words NULL, // Task input parameter - 1, // Priority of the task + 2, // Priority of the task &_callbackTaskHandle // Task handle. ); } @@ -120,14 +156,13 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + Serial.println("ERROR I2SClass::begin invalid state"); // debug return 0; // ERR } // TODO implement left / right justified modes switch (mode) { case I2S_PHILIPS_MODE: - //case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - //case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP case I2S_ADC_DAC: break; @@ -135,111 +170,154 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP default: // invalid mode + Serial.println("ERROR I2SClass::begin invalid mode"); // debug return 0; // ERR } _mode = mode; _sampleRate = sampleRate; _bitsPerSample = bitsPerSample; + esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); - esp_i2s::i2s_mode_t i2s_mode; if(driveClock){ - i2s_mode = esp_i2s::I2S_MODE_MASTER; + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER); }else{ // TODO there will much more work with slave mode - i2s_mode = esp_i2s::I2S_MODE_SLAVE; + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } + esp_i2s::i2s_pin_config_t pin_config; + pin_config.bck_io_num = _sckPin; + pin_config.ws_io_num = _fsPin; + Serial.println("I2SClass::begin foo"); // debug if(_mode == I2S_ADC_DAC){ - if(bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - return 0; // ERR - } - - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_TX | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); - - esp_i2s::i2s_config_t i2s_config = { - .mode = i2s_mode, - .sample_rate = _sampleRate, - .bits_per_sample = esp_i2s::I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 8, - .dma_buf_len = 512 // buffer length in Bytes - }; - - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + Serial.println("I2SClass::begin foo adc"); // debug + if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + Serial.println("ERROR I2SClass::begin invalid bps for ADC/DAC"); // debug return 0; // ERR } - - esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; - esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // - esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); - esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ - Serial.println("i2s_set_pin err"); - return 0; // ERR - } - - esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); - esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); - esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); - - }else{ // normal I2S mode without ADC/DAC + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); + }else{ // End of ADC/DAC mode; start of Normal mode + Serial.println("I2SClass::begin foo normal"); // debug if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ - Serial.println("I2S.begin(): invalid bits per second"); + Serial.println("I2S.begin(): invalid bits per second for normal mode"); // ESP does support 24 bps, however for the compatibility // with original Arduino implementation it is not allowed return 0; // ERR } - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); - esp_i2s::i2s_channel_fmt_t i2s_channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT; - esp_i2s::i2s_comm_format_t i2s_comm_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT); // 0x01 | 0x04 - esp_i2s::i2s_config_t i2s_config = { - .mode = i2s_mode, - .sample_rate = _sampleRate, - .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t) _bitsPerSample, - .channel_format = i2s_channel_format, - .communication_format = i2s_comm_format, - .intr_alloc_flags = 1, - .dma_buf_count = 8, - .dma_buf_len = 512 // buffer length in Bytes - }; - esp_i2s::i2s_pin_config_t pin_config; if (_state == I2S_STATE_DUPLEX){ // duplex pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, .data_out_num = _outSdPin, .data_in_num = _inSdPin }; }else{ // simplex pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_out_num = I2S_PIN_NO_CHANGE, .data_in_num = _sdPin }; } - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, 10, &_i2sEventQueue)) { - Serial.println("i2s_driver_install err"); + } // Normal mode + Serial.println("I2SClass::begin bar"); // debug + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = _sampleRate, + .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, + .dma_buf_count = _I2S_DMA_BUFFER_COUNT, + .dma_buf_len = _I2S_DMA_BUFFER_SIZE + }; + + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + Serial.println("ERROR I2SClass::begin error installing i2s driver"); // debug + Serial.flush(); + delay(100); + return 0; // ERR + } + + if(_mode == I2S_ADC_DAC){ + esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; + esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // + esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); + esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ + Serial.println("i2s_set_pin err"); return 0; // ERR } + esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); + esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); + esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); + }else{ // End of ADC/DAC mode; start of Normal mode + Serial.println("I2SClass::begin calling set"); // debug if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { Serial.println("i2s_set_pin err"); end(); return 0; // ERR } - } // ADC/DAC or normal I2S mode + } createCallbackTask(); _initialized = true; return 1; // OK } +int I2SClass::setAllPins(){ + return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); +} + +int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ + if(sckPin >= 0){ + _sckPin = sckPin; + }else{ + _sckPin = PIN_I2S_SCK; + } + + if(fsPin >= 0){ + _fsPin = fsPin; + }else{ + _fsPin = PIN_I2S_FS; + } + + if(inSdPin >= 0){ + _inSdPin = inSdPin; + }else{ + _inSdPin = PIN_I2S_SD; + } + + if(outSdPin >= 0){ + _outSdPin = outSdPin; + }else{ + _outSdPin = PIN_I2S_SD_OUT; + } + + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + Serial.printf("setFullDuplex() set pins error\n"); // debug + return 0; // ERR + } + } + return 1; // OK +} + +int I2SClass::setStateDuplex(){ + if(_inSdPin < 0 || _outSdPin < 0){ + return 0; // ERR + } + _state = I2S_STATE_DUPLEX; + return 1; +} + +//int I2SClass::setHalfDuplex(uint8_t inSdPin=PIN_I2S_SD, uint8_t outSdPin=PIN_I2S_SD_OUT){ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ _inSdPin = inSdPin; _outSdPin = outSdPin; @@ -251,24 +329,30 @@ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + Serial.printf("setFullDuplex() set pins error\n"); // debug return 0; // ERR } + Serial.printf("setFullDuplex(inSdPin=%d, outSdPin=%d) OK\n", inSdPin, outSdPin); // debug } return 1; // OK } +//int I2SClass::setSimplex(uint8_t sdPin=PIN_I2S_SD){ int I2SClass::setSimplex(uint8_t sdPin){ _sdPin = sdPin; if(_initialized){ esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, - .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + //.data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, + .data_out_num = I2S_PIN_NO_CHANGE, .data_in_num = _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + Serial.printf("setHalfDuplex: err setting pin %d\n", sdPin); // debug return 0; // ERR } + Serial.printf("setHalfDuplex(sdPin=%d) OK\n", sdPin); // debug } return 1; // OK } @@ -277,27 +361,30 @@ void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ destroyCallbackTask(); - } - if(_initialized){ - if(_mode == I2S_ADC_DAC){ - esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); - } - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); - _initialized = false; - if(_state != I2S_STATE_DUPLEX){ - _state = I2S_STATE_IDLE; + if(_initialized){ + if(_mode == I2S_ADC_DAC){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + _initialized = false; + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } } + _onTransmit = NULL; + _onReceive = NULL; + }else{ + // TODO log_e with error - destroy task from inside not permitted } - _onTransmit = NULL; - _onReceive = NULL; } // available to read int I2SClass::available() { // There is no actual way to tell in ESP - return 8; + return _I2S_DMA_BUFFER_SIZE; + //uxQueueSpacesAvailable(); } union i2s_sample_t { @@ -310,7 +397,8 @@ int I2SClass::read() { i2s_sample_t sample; sample.b32 = 0; - int bytes_read = read(&sample, _bitsPerSample / 8); + //int bytes_read = read(&sample, _bitsPerSample / 8); + read(&sample, _bitsPerSample / 8); if (_bitsPerSample == 32) { return sample.b32; @@ -326,15 +414,19 @@ int I2SClass::read() int I2SClass::read(void* buffer, size_t size) { - static long debug_timer_prev = 0; if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { if(!enableReceiver()){ return 0; // There was an error switching to receiver } } int read; - debug_timer_prev = millis(); - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 10); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_read start"); +#endif // _USE_SYS_VIEW + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_read stop"); +#endif // _USE_SYS_VIEW if(_mode == I2S_ADC_DAC){ for(int i = 0; i < read / 2; ++i){ @@ -353,31 +445,38 @@ size_t I2SClass::write(int sample) size_t I2SClass::write(int32_t sample) { - return write(&sample, 1); + return write(&sample, _bitsPerSample/8); } size_t I2SClass::write(const void *buffer, size_t size) { + //Serial.printf("I2SClass::write(): _state = %d\n",_state); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!enableTransmitter()){ return 0; // There was an error switching to transmitter } } size_t bytes_written; - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 10); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_write start"); +#endif // _USE_SYS_VIEW + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("i2s_write stop"); +#endif // _USE_SYS_VIEW return bytes_written; } int I2SClass::peek() { // TODO - Serial.println("I2SClass: peek() is not implemented for ESP"); + // peek() is not implemented for ESP return 0; } void I2SClass::flush() { - // do nothing, writes are DMA triggered in ESP + // do nothing, writes are DMA triggered } size_t I2SClass::write(uint8_t data) @@ -393,7 +492,8 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) int I2SClass::availableForWrite() { // There is no actual way to tell in ESP - return 512; + return _I2S_DMA_BUFFER_SIZE; + //uxQueueSpacesAvailable(); } void I2SClass::onTransmit(void(*function)(void)) @@ -408,18 +508,19 @@ void I2SClass::onReceive(void(*function)(void)) void I2SClass::setBufferSize(int bufferSize) { - Serial.println("I2SClass::setBufferSize() does nothing for ESP"); // does nothing in ESP } int I2SClass::enableTransmitter() { - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, - .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, + Serial.printf("I2SClass::enableTransmitter(): _state = %d\n",_state); + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ + Serial.printf("I2SClass::enableTransmitter(): change data out pin to %d\n",_sdPin); + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, + .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ _state = I2S_STATE_IDLE; @@ -453,7 +554,6 @@ void I2SClass::onTransferComplete() static QueueSetHandle_t xQueueSet; QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; - EventBits_t uxReturn; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -461,42 +561,56 @@ void I2SClass::onTransferComplete() xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ + //Serial.printf("I2S:onTransferComplete: loop start\n"); xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); if(xActivatedMember == _task_kill_cmd_semaphore_handle){ + Serial.printf("I2S:onTransferComplete: received kill command, breaking from loop\n"); xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop }else if(xActivatedMember == _i2sEventQueue){ + //Serial.printf("I2S:onTransferComplete: received Q\n"); xQueueReceive(_i2sEventQueue, &i2s_event, 0); if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ if(_onTransmit){ + //Serial.printf("I2S:onTransferComplete: calling _onTransmit\n"); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onTransmit start"); +#endif // _USE_SYS_VIEW _onTransmit(); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onTransmit stop"); +#endif // _USE_SYS_VIEW } }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ if (_onReceive) { + //Serial.printf("I2S:onTransferComplete: calling _onReceive\n"); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onReceive start"); +#endif // _USE_SYS_VIEW _onReceive(); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("_onReceive stop"); +#endif // _USE_SYS_VIEW } } // if event TX or RX } - _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } + Serial.printf("xxxxxxx I2S:onTransferComplete: out of loop - kill my self xxxxxxx\n"); + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); + I2S.onTransferComplete(); } #if I2S_INTERFACES_COUNT > 0 - #ifdef ESP_PLATFORM - #define I2S_DEVICE 0 - #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP - #define PIN_I2S_SCK 5 - #define PIN_I2S_FS 25 - #define PIN_I2S_SD 35 // ESP data in / codec data out (microphone) - #define PIN_I2S_SD_OUT 26 // ESP data out / codec data in (speakers) - #endif - -I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex -//I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex + //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex #endif + +#if I2S_INTERFACES_COUNT > 1 + // TODO set default pins for second module + //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex +#endif \ No newline at end of file diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9688a4712d4..2624e1afc17 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -20,24 +20,10 @@ #define _I2S_H_INCLUDED #include + namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver } -#include "freertos/semphr.h" - - -#define I2S_HAS_SET_BUFFER_SIZE 1 - -#ifdef CONFIG_IDF_TARGET_ESP32 - #define I2S_INTERFACES_COUNT 2 -#endif - -#ifdef CONFIG_IDF_TARGET_ESP32S2 - #define I2S_INTERFACES_COUNT 1 -#endif - -#define INCLUDE_xTaskGetCurrentTaskHandle 1 -#define _I2S_EVENT_QUEUE_LENGTH 10 typedef enum { I2S_PHILIPS_MODE, @@ -57,7 +43,11 @@ class I2SClass : public Stream // the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); - // change pin setup (default is Simplex) + // change pin setup and mode (default is Half Duplex) + // Can be called only on initialized object (after begin) + int setStateDuplex(); + int setAllPins(); + int setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin); int setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin); int setSimplex(uint8_t sdPin); @@ -105,19 +95,14 @@ class I2SClass : public Stream I2S_STATE_DUPLEX } i2s_state_t; - static int _beginCount; - int _deviceIndex; - int _clockGenerator; int _sdPin; int _inSdPin; int _outSdPin; int _sckPin; int _fsPin; - i2s_state_t _state; - int _dmaChannel; int _bitsPerSample; long _sampleRate; int _mode; @@ -131,10 +116,6 @@ class I2SClass : public Stream void (*_onReceive)(void); }; -// "I2S" is already defined by the CMSIS device, undefine it so the I2SClass -// instance can be called I2S -#undef I2S - extern I2SClass I2S; #endif From 64a3bb2fbf9ff16ba2c519c7b45562e222c17ef8 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 7 Jul 2021 14:51:26 +0200 Subject: [PATCH 29/94] [BACKUP] Work in progress on overlaying buffer (crashing) --- libraries/I2S/src/I2S.cpp | 188 +++++++++++++++++++++++--------------- libraries/I2S/src/I2S.h | 9 ++ 2 files changed, 122 insertions(+), 75 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c7c8f139eac..82d8a390f67 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,16 +21,11 @@ #include "I2S.h" #include "freertos/semphr.h" -//#define _USE_SYS_VIEW 1 -#ifdef _USE_SYS_VIEW - #include "esp_sysview_trace.h" // debug - //#include "esp_heap_trace.h" // debug - #include "esp_log.h" // debug -#endif // _USE_SYS_VIEW +#define _I2S_EVENT_QUEUE_LENGTH 100 + +#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 +#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER count must be between 2 and 128 -#define _I2S_EVENT_QUEUE_LENGTH 10 -#define _I2S_DMA_BUFFER_SIZE 512 -#define _I2S_DMA_BUFFER_COUNT 8 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -53,11 +48,11 @@ #define PIN_I2S_SD 26 #endif + #ifndef PIN_I2S_SD_IN #define PIN_I2S_SD_IN 35 // 35 can be only input pin #endif - #ifndef PIN_I2S_SD_OUT #define PIN_I2S_SD_OUT 26 #endif @@ -75,6 +70,15 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _sampleRate(0), _mode(I2S_PHILIPS_MODE), + _buffer_byte_size(0), + _output_buffer_pointer(0), + _input_buffer_pointer(0), + _read_available(0), + _in_buf_semaphore(NULL), + _out_buf_semaphore(NULL), + _inputBuffer(NULL), + _outputBuffer(NULL), + _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), @@ -98,6 +102,15 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _sampleRate(0), _mode(I2S_PHILIPS_MODE), + _buffer_byte_size(0), + _output_buffer_pointer(0), + _input_buffer_pointer(0), + _read_available(0), + _in_buf_semaphore(NULL), + _out_buf_semaphore(NULL), + _inputBuffer(NULL), + _outputBuffer(NULL), + _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), @@ -110,7 +123,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, void I2SClass::createCallbackTask() { - int stack_size = 3000; + int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); @@ -189,16 +202,13 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc pin_config.bck_io_num = _sckPin; pin_config.ws_io_num = _fsPin; - Serial.println("I2SClass::begin foo"); // debug if(_mode == I2S_ADC_DAC){ - Serial.println("I2SClass::begin foo adc"); // debug if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode Serial.println("ERROR I2SClass::begin invalid bps for ADC/DAC"); // debug return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); }else{ // End of ADC/DAC mode; start of Normal mode - Serial.println("I2SClass::begin foo normal"); // debug if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ Serial.println("I2S.begin(): invalid bits per second for normal mode"); // ESP does support 24 bps, however for the compatibility @@ -220,22 +230,39 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } } // Normal mode - Serial.println("I2SClass::begin bar"); // debug esp_i2s::i2s_config_t i2s_config = { .mode = i2s_mode, .sample_rate = _sampleRate, .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _I2S_DMA_BUFFER_SIZE }; + + _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8); + _inputBuffer = malloc(_buffer_byte_size); + _outputBuffer = malloc(_buffer_byte_size); + _output_buffer_pointer = 0; + _input_buffer_pointer = 0; + if(_inputBuffer == NULL || _outputBuffer == NULL){ + return 0; // ERR + } + _in_buf_semaphore = xSemaphoreCreateMutex(); + _out_buf_semaphore = xSemaphoreCreateMutex(); + //_in_buf_semaphore = xSemaphoreCreateBinary(); + //_out_buf_semaphore = xSemaphoreCreateBinary(); + + if(_in_buf_semaphore == NULL || _out_buf_semaphore == NULL){ + return 0; // ERR + } + //xSemaphoreGive(_in_buf_semaphore); + //xSemaphoreGive(_out_buf_semaphore); + if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver Serial.println("ERROR I2SClass::begin error installing i2s driver"); // debug - Serial.flush(); - delay(100); return 0; // ERR } @@ -253,7 +280,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); }else{ // End of ADC/DAC mode; start of Normal mode - Serial.println("I2SClass::begin calling set"); // debug if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { Serial.println("i2s_set_pin err"); end(); @@ -262,6 +288,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } createCallbackTask(); _initialized = true; + return 1; // OK } @@ -302,7 +329,6 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - Serial.printf("setFullDuplex() set pins error\n"); // debug return 0; // ERR } } @@ -329,10 +355,8 @@ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - Serial.printf("setFullDuplex() set pins error\n"); // debug return 0; // ERR } - Serial.printf("setFullDuplex(inSdPin=%d, outSdPin=%d) OK\n", inSdPin, outSdPin); // debug } return 1; // OK } @@ -349,10 +373,8 @@ int I2SClass::setSimplex(uint8_t sdPin){ .data_in_num = _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - Serial.printf("setHalfDuplex: err setting pin %d\n", sdPin); // debug return 0; // ERR } - Serial.printf("setHalfDuplex(sdPin=%d) OK\n", sdPin); // debug } return 1; // OK } @@ -374,6 +396,18 @@ void I2SClass::end() } _onTransmit = NULL; _onReceive = NULL; + free(_inputBuffer); + free(_outputBuffer); + _inputBuffer = NULL; + _outputBuffer = NULL; + _buffer_byte_size = 0; + _output_buffer_pointer = 0; + _input_buffer_pointer = 0; + vSemaphoreDelete(_in_buf_semaphore); + vSemaphoreDelete(_out_buf_semaphore); + _in_buf_semaphore = NULL; + _out_buf_semaphore = NULL; + }else{ // TODO log_e with error - destroy task from inside not permitted } @@ -382,9 +416,7 @@ void I2SClass::end() // available to read int I2SClass::available() { - // There is no actual way to tell in ESP - return _I2S_DMA_BUFFER_SIZE; - //uxQueueSpacesAvailable(); + return _read_available; } union i2s_sample_t { @@ -419,21 +451,22 @@ int I2SClass::read(void* buffer, size_t size) return 0; // There was an error switching to receiver } } - int read; -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_read start"); -#endif // _USE_SYS_VIEW - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_read stop"); -#endif // _USE_SYS_VIEW - + //int read; + //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); + size_t cpy_size = size <= _read_available ? size : _read_available; + //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ + if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ + memcpy(buffer, _inputBuffer, cpy_size); + xSemaphoreGive(_in_buf_semaphore); + _input_buffer_pointer = (_input_buffer_pointer + cpy_size) > _buffer_byte_size ? _buffer_byte_size : _input_buffer_pointer + cpy_size; + _read_available -= cpy_size; + } if(_mode == I2S_ADC_DAC){ - for(int i = 0; i < read / 2; ++i){ + for(int i = 0; i < cpy_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; } } - return read; + return cpy_size; } /* @@ -450,20 +483,20 @@ size_t I2SClass::write(int32_t sample) size_t I2SClass::write(const void *buffer, size_t size) { - //Serial.printf("I2SClass::write(): _state = %d\n",_state); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!enableTransmitter()){ return 0; // There was an error switching to transmitter } } size_t bytes_written; -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_write start"); -#endif // _USE_SYS_VIEW - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("i2s_write stop"); -#endif // _USE_SYS_VIEW + //esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); + size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; + if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ + memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); + xSemaphoreGive(_out_buf_semaphore); + }else{ + } + _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; return bytes_written; } @@ -491,9 +524,7 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) int I2SClass::availableForWrite() { - // There is no actual way to tell in ESP - return _I2S_DMA_BUFFER_SIZE; - //uxQueueSpacesAvailable(); + return _buffer_byte_size - _output_buffer_pointer; } void I2SClass::onTransmit(void(*function)(void)) @@ -513,9 +544,7 @@ void I2SClass::setBufferSize(int bufferSize) int I2SClass::enableTransmitter() { - Serial.printf("I2SClass::enableTransmitter(): _state = %d\n",_state); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - Serial.printf("I2SClass::enableTransmitter(): change data out pin to %d\n",_sdPin); esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, @@ -561,48 +590,57 @@ void I2SClass::onTransferComplete() xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - //Serial.printf("I2S:onTransferComplete: loop start\n"); xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); if(xActivatedMember == _task_kill_cmd_semaphore_handle){ - Serial.printf("I2S:onTransferComplete: received kill command, breaking from loop\n"); xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop }else if(xActivatedMember == _i2sEventQueue){ - //Serial.printf("I2S:onTransferComplete: received Q\n"); xQueueReceive(_i2sEventQueue, &i2s_event, 0); - if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + //if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ + if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ + size_t bytes_written; + if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); + _output_buffer_pointer = 0; + if(xSemaphoreGive(_out_buf_semaphore) != pdTRUE){ + // We would not expect this call to fail because we must have + // obtained the semaphore to get here. + } + } if(_onTransmit){ - //Serial.printf("I2S:onTransferComplete: calling _onTransmit\n"); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onTransmit start"); -#endif // _USE_SYS_VIEW _onTransmit(); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onTransmit stop"); -#endif // _USE_SYS_VIEW - } - }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ - if (_onReceive) { - //Serial.printf("I2S:onTransferComplete: calling _onReceive\n"); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onReceive start"); -#endif // _USE_SYS_VIEW - _onReceive(); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("_onReceive stop"); -#endif // _USE_SYS_VIEW - } + } // user callback + //}else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ + //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY == pdTRUE)){ + if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); + _input_buffer_pointer = 0; + if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ // CRASHING HERE on first call + // We would not expect this call to fail because we must have + // obtained the semaphore to get here. + } + if (_onReceive) { + _onReceive(); + } // user callback + } // semaphore } // if event TX or RX } } - Serial.printf("xxxxxxx I2S:onTransferComplete: out of loop - kill my self xxxxxxx\n"); _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) { + +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("onTransferComplete start"); +#endif // _USE_SYS_VIEW I2S.onTransferComplete(); +#ifdef _USE_SYS_VIEW + SEGGER_SYSVIEW_Print("onTransferComplete stop"); +#endif // _USE_SYS_VIEW } #if I2S_INTERFACES_COUNT > 0 diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 2624e1afc17..42a589ca294 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -107,6 +107,15 @@ class I2SClass : public Stream long _sampleRate; int _mode; + uint16_t _buffer_byte_size; + uint16_t _output_buffer_pointer; + uint16_t _input_buffer_pointer; + uint16_t _read_available; + SemaphoreHandle_t _in_buf_semaphore; + SemaphoreHandle_t _out_buf_semaphore; + void *_inputBuffer; + void *_outputBuffer; + bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; From 992c80a7d2dce1bdc2403e3cec295b4cf98bd8a7 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 7 Jul 2021 15:35:33 +0200 Subject: [PATCH 30/94] [BUGFIX] Fixed data type which was causing crashes --- libraries/I2S/src/I2S.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 42a589ca294..edb29a46b68 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -110,7 +110,7 @@ class I2SClass : public Stream uint16_t _buffer_byte_size; uint16_t _output_buffer_pointer; uint16_t _input_buffer_pointer; - uint16_t _read_available; + size_t _read_available; SemaphoreHandle_t _in_buf_semaphore; SemaphoreHandle_t _out_buf_semaphore; void *_inputBuffer; From 64bd07648dc3f585265ec259f246d8ea48dab452 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 8 Jul 2021 09:20:52 +0200 Subject: [PATCH 31/94] [BUGFIX] Fixed usage of new buffer in read function --- .../InputSerialPlotter/InputSerialPlotter.ino | 4 +-- libraries/I2S/src/I2S.cpp | 31 +++++++------------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index 743b1c29175..a50fe0a609f 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -32,14 +32,14 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } + I2S.setAllPins(27,25,33,26); } void loop() { // read a sample int sample = I2S.read(); - if (sample) { - // if it's non-zero print value to serial + if (sample && sample != -1 && sample != 1) { Serial.println(sample); } } diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 82d8a390f67..eaf381259a6 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,9 +22,8 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 100 - #define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 -#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER count must be between 2 and 128 +#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -444,28 +443,27 @@ int I2SClass::read() return 0; } -int I2SClass::read(void* buffer, size_t size) -{ +int I2SClass::read(void* buffer, size_t size){ if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { if(!enableReceiver()){ return 0; // There was an error switching to receiver } } - //int read; + size_t cpy_size = 0; //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); - size_t cpy_size = size <= _read_available ? size : _read_available; //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ - memcpy(buffer, _inputBuffer, cpy_size); - xSemaphoreGive(_in_buf_semaphore); + cpy_size = size <= _read_available ? size : _read_available; + memcpy(buffer, (void*)((uint)_inputBuffer+_input_buffer_pointer), cpy_size); _input_buffer_pointer = (_input_buffer_pointer + cpy_size) > _buffer_byte_size ? _buffer_byte_size : _input_buffer_pointer + cpy_size; _read_available -= cpy_size; + xSemaphoreGive(_in_buf_semaphore); } - if(_mode == I2S_ADC_DAC){ - for(int i = 0; i < cpy_size / 2; ++i){ + if(_mode == I2S_ADC_DAC){ + for(size_t i = 0; i < cpy_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; } - } + } // ADC/DAC mode return cpy_size; } @@ -494,7 +492,6 @@ size_t I2SClass::write(const void *buffer, size_t size) if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); xSemaphoreGive(_out_buf_semaphore); - }else{ } _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; return bytes_written; @@ -616,7 +613,7 @@ void I2SClass::onTransferComplete() if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); _input_buffer_pointer = 0; - if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ // CRASHING HERE on first call + if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ // We would not expect this call to fail because we must have // obtained the semaphore to get here. } @@ -634,13 +631,7 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("onTransferComplete start"); -#endif // _USE_SYS_VIEW I2S.onTransferComplete(); -#ifdef _USE_SYS_VIEW - SEGGER_SYSVIEW_Print("onTransferComplete stop"); -#endif // _USE_SYS_VIEW } #if I2S_INTERFACES_COUNT > 0 @@ -651,4 +642,4 @@ void I2SClass::onDmaTransferComplete(void*) #if I2S_INTERFACES_COUNT > 1 // TODO set default pins for second module //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex -#endif \ No newline at end of file +#endif From 635dc5a22f108b8ed203c0cb412503ee498a8132 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 23 Jul 2021 15:08:50 +0200 Subject: [PATCH 32/94] [BACKUP] Latest experiments with internal buffer --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 32 ++++++------ .../I2S/examples/Callbacks/Callbacks.ino | 33 ++++++------ .../InputSerialPlotter/InputSerialPlotter.ino | 1 - .../I2S/examples/SimpleTone/SimpleTone.ino | 7 ++- libraries/I2S/src/I2S.cpp | 50 +++++-------------- libraries/I2S/src/I2S.h | 17 +++++++ 6 files changed, 66 insertions(+), 74 deletions(-) diff --git a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino index 10a12bdc06c..1c68be33b40 100644 --- a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -1,5 +1,6 @@ /* - This example is only for ESP devices + This example is only for ESP devices. + This example demonstrates usage of integrated Digital to Analog Converter (DAC) You can display sound wave from audio device, or just measure voltage. @@ -41,20 +42,20 @@ Second option to measure voltage on trimmer / potentiometer has following connec | | V GND -Optional resistor will decrease read value. + Optional resistor will decrease read value. -Steps to run: -1. Select target board: - Tools -> Board -> ESP32 Arduino -> your board -2. Upload sketch - Press upload button (arrow in top left corner) - When you see in console line like this: "Connecting........_____.....__" - On your board press and hold Boot button and press EN button shortly. Now you can release both buttons. - You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. - If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. -3. Open plotter - Tools -> Serial Plotter - Enjoy + Steps to run: + 1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board + 2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + On your board press and hold Boot button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. + 3. Open plotter + Tools -> Serial Plotter + Enjoy Created by Tomas Pilny on 17th June 2021 @@ -82,5 +83,4 @@ void loop() { // read a sample int sample = I2S.read(); Serial.println(sample); - delay(100); - } +} diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino index 105ef64133b..5149842fe90 100644 --- a/libraries/I2S/examples/Callbacks/Callbacks.ino +++ b/libraries/I2S/examples/Callbacks/Callbacks.ino @@ -51,10 +51,10 @@ on 23rd June 2021 // If you need to change any of the default pins, simply uncomment chosen line and change the pin number /* -#define PIN_I2S_SCK 33 -#define PIN_I2S_FS 16 -#define PIN_I2S_SD 17 -#define PIN_I2S_SD_OUT 4 +#define PIN_I2S_SCK 5 +#define PIN_I2S_FS 25 +#define PIN_I2S_SD 35 +#define PIN_I2S_SD_OUT 26 */ #include @@ -67,18 +67,21 @@ const int amplitude = 32767; // amplitude of square wave const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave -short sample = amplitude; // current sample value +short out_sample = amplitude; // current sample value int count = 0; void outputCallback(){ + if (count % halfWavelength == 0) { // invert the sample every half wavelength count multiple to generate square wave - sample = -1 * sample; + out_sample = -1 * out_sample; } + Serial.printf("write %d\n", out_sample); // only for debug + // write the same sample twice, once for left and once for the right channel - I2S.write(sample); - I2S.write(sample); + I2S.write(out_sample); + I2S.write(out_sample); // increment the counter for the next sample count++; @@ -87,13 +90,11 @@ void outputCallback(){ // code from InputSerialPlotter example void inputCallback(){ // read a sample - int sample = I2S.read(); + int in_sample = I2S.read(); - Serial.println(sample); // only for debug - - if (sample) { + if (in_sample) { // if it's non-zero print value to serial - Serial.println(sample); + Serial.println(in_sample); } } @@ -111,7 +112,7 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1) delay(100); // do nothing } - I2S.setAllPins(); + I2S.setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); // Register our callback functions I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) @@ -121,5 +122,5 @@ void setup() { void loop() { // loop task remains free for other work - delay(100); // Let the FreeRTOS reset the watchDogTimer -} \ No newline at end of file + delay(10); // Let the FreeRTOS reset the watchDogTimer +} diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index a50fe0a609f..db9f7d0d4f8 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -32,7 +32,6 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } - I2S.setAllPins(27,25,33,26); } void loop() { diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 36464148c33..c60e0aeaca9 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -17,10 +17,9 @@ */ #include - -const int frequency = 1250; // frequency of square wave in Hz -const int amplitude = 32767; // amplitude of square wave -const int sampleRate = 16000; // sample rate in Hz +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 diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index eaf381259a6..5b6138ffd0a 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -24,7 +24,6 @@ #define _I2S_EVENT_QUEUE_LENGTH 100 #define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 #define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 - #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -35,27 +34,6 @@ #define I2S_CLOCK_GENERATOR 0 // does nothing for ESP #endif -#ifndef PIN_I2S_SCK - #define PIN_I2S_SCK 5 -#endif - -#ifndef PIN_I2S_FS - #define PIN_I2S_FS 25 -#endif - -#ifndef PIN_I2S_SD - #define PIN_I2S_SD 26 -#endif - - -#ifndef PIN_I2S_SD_IN - #define PIN_I2S_SD_IN 35 // 35 can be only input pin -#endif - -#ifndef PIN_I2S_SD_OUT - #define PIN_I2S_SD_OUT 26 -#endif - I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), _sdPin(sdPin), // shared data pin @@ -168,7 +146,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { - Serial.println("ERROR I2SClass::begin invalid state"); // debug return 0; // ERR } @@ -182,7 +159,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP default: // invalid mode - Serial.println("ERROR I2SClass::begin invalid mode"); // debug return 0; // ERR } @@ -203,7 +179,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc if(_mode == I2S_ADC_DAC){ if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - Serial.println("ERROR I2SClass::begin invalid bps for ADC/DAC"); // debug return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); @@ -261,7 +236,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc //xSemaphoreGive(_out_buf_semaphore); if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver - Serial.println("ERROR I2SClass::begin error installing i2s driver"); // debug return 0; // ERR } @@ -450,6 +424,7 @@ int I2SClass::read(void* buffer, size_t size){ } } size_t cpy_size = 0; + //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ @@ -488,12 +463,16 @@ size_t I2SClass::write(const void *buffer, size_t size) } size_t bytes_written; //esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); - size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ + size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); + _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; + if(_output_buffer_pointer == _buffer_byte_size){ // when full flush it + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); + _output_buffer_pointer = 0; + } xSemaphoreGive(_out_buf_semaphore); } - _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; return bytes_written; } @@ -593,7 +572,6 @@ void I2SClass::onTransferComplete() break; // from the infinite loop }else if(xActivatedMember == _i2sEventQueue){ xQueueReceive(_i2sEventQueue, &i2s_event, 0); - //if((i2s_event == esp_i2s::I2S_EVENT_TX_DONE) && (_state == I2S_STATE_DUPLEX || _state == I2S_STATE_TRANSMITTER)){ if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ size_t bytes_written; if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ @@ -602,14 +580,12 @@ void I2SClass::onTransferComplete() if(xSemaphoreGive(_out_buf_semaphore) != pdTRUE){ // We would not expect this call to fail because we must have // obtained the semaphore to get here. - } - } + } // semaphore give + } // output buffer semaphore if(_onTransmit){ _onTransmit(); } // user callback - //}else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE && (_state == I2S_STATE_RECEIVER || _state == I2S_STATE_DUPLEX)){ }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ - //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY == pdTRUE)){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); _input_buffer_pointer = 0; @@ -620,10 +596,10 @@ void I2SClass::onTransferComplete() if (_onReceive) { _onReceive(); } // user callback - } // semaphore - } // if event TX or RX - } - } + } // input buffer semaphore + } // RX Done + } // I2S event + } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task vTaskDelete(NULL); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index edb29a46b68..7709abc019f 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -25,6 +25,23 @@ namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver } +// Default pins +#ifndef PIN_I2S_SCK + #define PIN_I2S_SCK 5 +#endif + +#ifndef PIN_I2S_FS + #define PIN_I2S_FS 25 +#endif + +#ifndef PIN_I2S_SD + #define PIN_I2S_SD 35 // Input if used in duplex +#endif + +#ifndef PIN_I2S_SD_OUT + #define PIN_I2S_SD_OUT 26 +#endif + typedef enum { I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, From d84057b8d69373ca1188200bbbd1843303f881bd Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 30 Jul 2021 12:27:40 +0200 Subject: [PATCH 33/94] Improved quality of playback --- .../I2S/examples/SimpleTone/SimpleTone.ino | 22 +-- libraries/I2S/src/I2S.cpp | 136 +++++++++--------- libraries/I2S/src/I2S.h | 5 +- 3 files changed, 83 insertions(+), 80 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index c60e0aeaca9..0967f50dca5 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -38,15 +38,17 @@ void setup() { } void loop() { - if (count % halfWavelength == 0) { - // invert the sample every half wavelength count multiple to generate square wave - sample = -1 * sample; + if(I2S.availableForWrite() >= 2){ + 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++; } - - // 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/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 5b6138ffd0a..5743a60b0cc 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,9 +21,11 @@ #include "I2S.h" #include "freertos/semphr.h" -#define _I2S_EVENT_QUEUE_LENGTH 100 -#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 8 and 1024 -#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 +#define _I2S_EVENT_QUEUE_LENGTH 64 +#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 +// (Theoretically it should be above 8, but for some reason sizes below 200 results in frozen callback task) + +#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -36,11 +38,11 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), - _sdPin(sdPin), // shared data pin - _inSdPin(-1), // input data pin - _outSdPin(-1), // output data pin - _sckPin(sckPin), // clock pin - _fsPin(fsPin), // frame (word) select pin + _sdPin(sdPin), // shared data pin + _inSdPin(-1), // input data pin + _outSdPin(-1), // output data pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_IDLE), _bitsPerSample(0), @@ -48,18 +50,16 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _output_buffer_pointer(0), _input_buffer_pointer(0), _read_available(0), _in_buf_semaphore(NULL), - _out_buf_semaphore(NULL), _inputBuffer(NULL), - _outputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _output_ring_buffer(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -68,11 +68,11 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex _deviceIndex(deviceIndex), - _sdPin(inSdPin), // shared data pin - _inSdPin(inSdPin), // input data pin + _sdPin(inSdPin), // shared data pin + _inSdPin(inSdPin), // input data pin _outSdPin(outSdPin), // output data pin - _sckPin(sckPin), // clock pin - _fsPin(fsPin), // frame (word) select pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_DUPLEX), _bitsPerSample(0), @@ -80,18 +80,16 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _output_buffer_pointer(0), _input_buffer_pointer(0), _read_available(0), _in_buf_semaphore(NULL), - _out_buf_semaphore(NULL), _inputBuffer(NULL), - _outputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _output_ring_buffer(NULL), _onTransmit(NULL), _onReceive(NULL) @@ -131,9 +129,9 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) int I2SClass::begin(int mode, int bitsPerSample) { - Serial.println("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP"); - Serial.println("Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below"); - Serial.println("\tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); + log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ + Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ + \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); return 0; // ERR // slave mode (not driving clock and frame select pin - input) //return begin(mode, 0, bitsPerSample, false); @@ -184,7 +182,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); }else{ // End of ADC/DAC mode; start of Normal mode if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ - Serial.println("I2S.begin(): invalid bits per second for normal mode"); + log_e("I2S.begin(): invalid bits per second for normal mode"); // ESP does support 24 bps, however for the compatibility // with original Arduino implementation it is not allowed return 0; // ERR @@ -216,24 +214,19 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc }; - _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8); + _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; _inputBuffer = malloc(_buffer_byte_size); - _outputBuffer = malloc(_buffer_byte_size); - _output_buffer_pointer = 0; + //_output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_ALLOWSPLIT); + _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _input_buffer_pointer = 0; - if(_inputBuffer == NULL || _outputBuffer == NULL){ + if(_inputBuffer == NULL){ return 0; // ERR } _in_buf_semaphore = xSemaphoreCreateMutex(); - _out_buf_semaphore = xSemaphoreCreateMutex(); - //_in_buf_semaphore = xSemaphoreCreateBinary(); - //_out_buf_semaphore = xSemaphoreCreateBinary(); - if(_in_buf_semaphore == NULL || _out_buf_semaphore == NULL){ + if(_in_buf_semaphore == NULL){ return 0; // ERR } - //xSemaphoreGive(_in_buf_semaphore); - //xSemaphoreGive(_out_buf_semaphore); if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver return 0; // ERR @@ -245,7 +238,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ - Serial.println("i2s_set_pin err"); + log_e("i2s_set_pin err"); return 0; // ERR } @@ -254,11 +247,12 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); }else{ // End of ADC/DAC mode; start of Normal mode if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { - Serial.println("i2s_set_pin err"); + log_e("i2s_set_pin err"); end(); return 0; // ERR } } + createCallbackTask(); _initialized = true; @@ -370,19 +364,14 @@ void I2SClass::end() _onTransmit = NULL; _onReceive = NULL; free(_inputBuffer); - free(_outputBuffer); _inputBuffer = NULL; - _outputBuffer = NULL; _buffer_byte_size = 0; - _output_buffer_pointer = 0; _input_buffer_pointer = 0; vSemaphoreDelete(_in_buf_semaphore); - vSemaphoreDelete(_out_buf_semaphore); _in_buf_semaphore = NULL; - _out_buf_semaphore = NULL; - + vRingbufferDelete(_output_ring_buffer); }else{ - // TODO log_e with error - destroy task from inside not permitted + log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!") } } @@ -402,7 +391,6 @@ int I2SClass::read() { i2s_sample_t sample; sample.b32 = 0; - //int bytes_read = read(&sample, _bitsPerSample / 8); read(&sample, _bitsPerSample / 8); if (_bitsPerSample == 32) { @@ -425,8 +413,6 @@ int I2SClass::read(void* buffer, size_t size){ } size_t cpy_size = 0; - //esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, (size_t*) &read, 0); - //if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, portMAX_DELAY) == pdTRUE){ if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ cpy_size = size <= _read_available ? size : _read_available; memcpy(buffer, (void*)((uint)_inputBuffer+_input_buffer_pointer), cpy_size); @@ -461,19 +447,11 @@ size_t I2SClass::write(const void *buffer, size_t size) return 0; // There was an error switching to transmitter } } - size_t bytes_written; - //esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buffer, size, &bytes_written, 0); - if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ - size_t cpy_size = size <= _buffer_byte_size - _output_buffer_pointer ? size : _buffer_byte_size - _output_buffer_pointer; - memcpy((void*)((uint)_outputBuffer+_output_buffer_pointer), buffer, cpy_size); - _output_buffer_pointer = (_output_buffer_pointer + cpy_size) > _buffer_byte_size ? _output_buffer_pointer : _output_buffer_pointer + cpy_size; - if(_output_buffer_pointer == _buffer_byte_size){ // when full flush it - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); - _output_buffer_pointer = 0; - } - xSemaphoreGive(_out_buf_semaphore); + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + return size; + }else{ + return 0; } - return bytes_written; } int I2SClass::peek() @@ -500,7 +478,7 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) int I2SClass::availableForWrite() { - return _buffer_byte_size - _output_buffer_pointer; + return (int)xRingbufferGetCurFreeSize(_output_ring_buffer); } void I2SClass::onTransmit(void(*function)(void)) @@ -559,6 +537,13 @@ void I2SClass::onTransferComplete() static QueueSetHandle_t xQueueSet; QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; + size_t item_size = 0; + size_t prev_item_size = 0; + void *item = NULL; + bool prev_item_valid = false; + size_t bytes_written; + int prev_item_offset = 0; + uint8_t prev_item[_I2S_DMA_BUFFER_SIZE]; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -573,15 +558,33 @@ void I2SClass::onTransferComplete() }else if(xActivatedMember == _i2sEventQueue){ xQueueReceive(_i2sEventQueue, &i2s_event, 0); if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ - size_t bytes_written; - if(_out_buf_semaphore != NULL && xSemaphoreTake(_out_buf_semaphore, portMAX_DELAY) == pdTRUE){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, _outputBuffer, _output_buffer_pointer, &bytes_written, 0); - _output_buffer_pointer = 0; - if(xSemaphoreGive(_out_buf_semaphore) != pdTRUE){ - // We would not expect this call to fail because we must have - // obtained the semaphore to get here. - } // semaphore give - } // output buffer semaphore + do{ // send to esp i2s driver loop + prev_item_valid = false; + if(prev_item && prev_item_valid){ // use item from previous round + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); + if(prev_item_size != bytes_written){ + prev_item_offset = bytes_written; + prev_item_valid = true; + } + prev_item_size -= bytes_written; + }else{ // load new item from ring buffer + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), _I2S_DMA_BUFFER_SIZE); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ + memcpy(prev_item, item+bytes_written, item_size-bytes_written); + prev_item_size = item_size - bytes_written; + prev_item_offset = 0; + prev_item_valid = true; + } + vRingbufferReturnItem(_output_ring_buffer, item); + //Failed to receive item + } // Check received item + } // old or new item + if(item_size != bytes_written){ + log_w("i2s_write could not write requested amount: requested=%d; written=%d\n", item_size, bytes_written); + } + }while((item_size == bytes_written && item_size == _I2S_DMA_BUFFER_SIZE) || prev_item_size == bytes_written); if(_onTransmit){ _onTransmit(); } // user callback @@ -606,7 +609,6 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 7709abc019f..be093e2c43d 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -20,6 +20,7 @@ #define _I2S_H_INCLUDED #include +#include "freertos/ringbuf.h" namespace esp_i2s { #include "driver/i2s.h" // ESP specific i2s driver @@ -125,18 +126,16 @@ class I2SClass : public Stream int _mode; uint16_t _buffer_byte_size; - uint16_t _output_buffer_pointer; uint16_t _input_buffer_pointer; size_t _read_available; SemaphoreHandle_t _in_buf_semaphore; - SemaphoreHandle_t _out_buf_semaphore; void *_inputBuffer; - void *_outputBuffer; bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; QueueHandle_t _task_kill_cmd_semaphore_handle; + RingbufHandle_t _output_ring_buffer; void (*_onTransmit)(void); void (*_onReceive)(void); From 7da54b81899ad94a83190715729ef262df6b6c05 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 3 Aug 2021 11:17:00 +0200 Subject: [PATCH 34/94] Reimplemented input buffer as ring buffer --- libraries/I2S/src/I2S.cpp | 167 ++++++++++++++++++-------------------- libraries/I2S/src/I2S.h | 5 +- 2 files changed, 82 insertions(+), 90 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 5743a60b0cc..c8103534c89 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,9 +21,9 @@ #include "I2S.h" #include "freertos/semphr.h" -#define _I2S_EVENT_QUEUE_LENGTH 64 -#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 -// (Theoretically it should be above 8, but for some reason sizes below 200 results in frozen callback task) +#define _I2S_EVENT_QUEUE_LENGTH 16 +#define _I2S_DMA_BUFFER_SIZE 256 // BUFFER SIZE must be between 200 and 1024 +// (Theoretically it could be above 8, but for some reason sizes below 200 results in frozen callback task) #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -50,15 +50,12 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _input_buffer_pointer(0), - _read_available(0), - _in_buf_semaphore(NULL), - _inputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _input_ring_buffer(NULL), _output_ring_buffer(NULL), _onTransmit(NULL), @@ -80,15 +77,12 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _mode(I2S_PHILIPS_MODE), _buffer_byte_size(0), - _input_buffer_pointer(0), - _read_available(0), - _in_buf_semaphore(NULL), - _inputBuffer(NULL), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _input_ring_buffer(NULL), _output_ring_buffer(NULL), _onTransmit(NULL), @@ -144,6 +138,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + log_e("I2S.begin: unexpected _state (%d)",_state); return 0; // ERR } @@ -157,6 +152,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP default: // invalid mode + log_e("ERROR I2SClass::begin() unknown mode"); return 0; // ERR } @@ -177,6 +173,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc if(_mode == I2S_ADC_DAC){ if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); @@ -215,20 +212,15 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; - _inputBuffer = malloc(_buffer_byte_size); - //_output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_ALLOWSPLIT); + _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); - _input_buffer_pointer = 0; - if(_inputBuffer == NULL){ - return 0; // ERR - } - _in_buf_semaphore = xSemaphoreCreateMutex(); - - if(_in_buf_semaphore == NULL){ + if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ + log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); return 0; // ERR } if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + log_e("ERROR could not install i2s driver"); return 0; // ERR } @@ -238,7 +230,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ - log_e("i2s_set_pin err"); + log_e("i2s_set_pin failed"); return 0; // ERR } @@ -247,7 +239,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); }else{ // End of ADC/DAC mode; start of Normal mode if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { - log_e("i2s_set_pin err"); + log_e("i2s_set_pin failed"); end(); return 0; // ERR } @@ -296,6 +288,7 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); return 0; // ERR } } @@ -304,6 +297,7 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ int I2SClass::setStateDuplex(){ if(_inSdPin < 0 || _outSdPin < 0){ + log_e("I2S cannot set Duplex-one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); return 0; // ERR } _state = I2S_STATE_DUPLEX; @@ -322,6 +316,7 @@ int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ .data_in_num = _inSdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); return 0; // ERR } } @@ -340,6 +335,7 @@ int I2SClass::setSimplex(uint8_t sdPin){ .data_in_num = _sdPin }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); return 0; // ERR } } @@ -363,22 +359,17 @@ void I2SClass::end() } _onTransmit = NULL; _onReceive = NULL; - free(_inputBuffer); - _inputBuffer = NULL; - _buffer_byte_size = 0; - _input_buffer_pointer = 0; - vSemaphoreDelete(_in_buf_semaphore); - _in_buf_semaphore = NULL; + vRingbufferDelete(_input_ring_buffer); vRingbufferDelete(_output_ring_buffer); }else{ - log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!") + log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!"); } } -// available to read +// Bytes available to read int I2SClass::available() { - return _read_available; + return _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer); } union i2s_sample_t { @@ -411,21 +402,22 @@ int I2SClass::read(void* buffer, size_t size){ return 0; // There was an error switching to receiver } } - size_t cpy_size = 0; - - if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ - cpy_size = size <= _read_available ? size : _read_available; - memcpy(buffer, (void*)((uint)_inputBuffer+_input_buffer_pointer), cpy_size); - _input_buffer_pointer = (_input_buffer_pointer + cpy_size) > _buffer_byte_size ? _buffer_byte_size : _input_buffer_pointer + cpy_size; - _read_available -= cpy_size; - xSemaphoreGive(_in_buf_semaphore); + + size_t item_size = 0; + void *tmp_buffer; + tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); + if(tmp_buffer != NULL){ + memcpy(buffer, tmp_buffer, item_size); + if(_mode == I2S_ADC_DAC){ + for(size_t i = 0; i < item_size / 2; ++i){ + ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; + } + } // ADC/DAC mode + vRingbufferReturnItem(_input_ring_buffer, tmp_buffer); + return item_size; + }else{ + return 0; } - if(_mode == I2S_ADC_DAC){ - for(size_t i = 0; i < cpy_size / 2; ++i){ - ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; - } - } // ADC/DAC mode - return cpy_size; } /* @@ -435,11 +427,21 @@ size_t I2SClass::write(int sample) } */ +size_t I2SClass::write(uint8_t data) +{ + return write((int32_t)data); +} + size_t I2SClass::write(int32_t sample) { return write(&sample, _bitsPerSample/8); } +size_t I2SClass::write(const uint8_t *buffer, size_t size) +{ + return write((const void*)buffer, size); +} + size_t I2SClass::write(const void *buffer, size_t size) { if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { @@ -457,7 +459,7 @@ size_t I2SClass::write(const void *buffer, size_t size) int I2SClass::peek() { // TODO - // peek() is not implemented for ESP + // peek() is not implemented for ESP yet return 0; } @@ -466,16 +468,7 @@ void I2SClass::flush() // do nothing, writes are DMA triggered } -size_t I2SClass::write(uint8_t data) -{ - return write((int32_t)data); -} - -size_t I2SClass::write(const uint8_t *buffer, size_t size) -{ - return write((const void*)buffer, size); -} - +// Bytes available to write int I2SClass::availableForWrite() { return (int)xRingbufferGetCurFreeSize(_output_ring_buffer); @@ -525,6 +518,7 @@ int I2SClass::enableReceiver() }; if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ _state = I2S_STATE_IDLE; + log_e("i2s_set_pin failed"); return 0; // ERR } _state = I2S_STATE_RECEIVER; @@ -535,15 +529,17 @@ int I2SClass::enableReceiver() void I2SClass::onTransferComplete() { static QueueSetHandle_t xQueueSet; + const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; size_t item_size = 0; size_t prev_item_size = 0; void *item = NULL; bool prev_item_valid = false; - size_t bytes_written; + size_t bytes_written, bytes_read; int prev_item_offset = 0; - uint8_t prev_item[_I2S_DMA_BUFFER_SIZE]; + uint8_t prev_item[_I2S_DMA_BUFFER_SIZE*4]; + uint8_t _inputBuffer[_I2S_DMA_BUFFER_SIZE*4]; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -558,48 +554,46 @@ void I2SClass::onTransferComplete() }else if(xActivatedMember == _i2sEventQueue){ xQueueReceive(_i2sEventQueue, &i2s_event, 0); if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ - do{ // send to esp i2s driver loop - prev_item_valid = false; - if(prev_item && prev_item_valid){ // use item from previous round - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); - if(prev_item_size != bytes_written){ - prev_item_offset = bytes_written; - prev_item_valid = true; - } - prev_item_size -= bytes_written; - }else{ // load new item from ring buffer - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), _I2S_DMA_BUFFER_SIZE); + if(prev_item_valid){ // use item from previous round + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); + if(prev_item_size == bytes_written){ + prev_item_valid = false; + } // write size check + prev_item_offset = bytes_written; + prev_item_size -= bytes_written; + } // prev_item_valid + + if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer + do{ // "send to esp i2s driver" loop + bytes_written = 0; + item_size = 0; + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); if (item != NULL){ esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); - if(item_size != bytes_written){ - memcpy(prev_item, item+bytes_written, item_size-bytes_written); + if(item_size != bytes_written){ // save item that was not written correctly for later + memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); prev_item_size = item_size - bytes_written; prev_item_offset = 0; prev_item_valid = true; - } + } // save item that was not written correctly for later vRingbufferReturnItem(_output_ring_buffer, item); - //Failed to receive item } // Check received item - } // old or new item - if(item_size != bytes_written){ - log_w("i2s_write could not write requested amount: requested=%d; written=%d\n", item_size, bytes_written); - } - }while((item_size == bytes_written && item_size == _I2S_DMA_BUFFER_SIZE) || prev_item_size == bytes_written); + }while(item_size == bytes_written && item_size == single_dma_buf); + } // don't read from almost empty buffer if(_onTransmit){ _onTransmit(); } // user callback + }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ - if(_in_buf_semaphore != NULL && xSemaphoreTake(_in_buf_semaphore, 10) == pdTRUE){ - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, _buffer_byte_size, (size_t*) &_read_available, 0); - _input_buffer_pointer = 0; - if(xSemaphoreGive(_in_buf_semaphore) != pdTRUE){ - // We would not expect this call to fail because we must have - // obtained the semaphore to get here. - } + size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ + log_w("I2S failed to send item from DMA to internal buffer\n"); + }else{ if (_onReceive) { _onReceive(); } // user callback - } // input buffer semaphore + } // xRingbufferSendComplete } // RX Done } // I2S event } // infinite loop @@ -609,6 +603,7 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { + I2S.onTransferComplete(); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index be093e2c43d..5412bbc3c45 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -126,15 +126,12 @@ class I2SClass : public Stream int _mode; uint16_t _buffer_byte_size; - uint16_t _input_buffer_pointer; - size_t _read_available; - SemaphoreHandle_t _in_buf_semaphore; - void *_inputBuffer; bool _initialized; TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; QueueHandle_t _task_kill_cmd_semaphore_handle; + RingbufHandle_t _input_ring_buffer; RingbufHandle_t _output_ring_buffer; void (*_onTransmit)(void); From d5a3a05a1cffceb6442fd1f1581c0c42b88066e2 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 5 Aug 2021 14:15:12 +0200 Subject: [PATCH 35/94] Fixed kill semaphore error; added pin getters/setters --- libraries/I2S/src/I2S.cpp | 163 +++++++++++++++++++++++++------------- libraries/I2S/src/I2S.h | 21 ++++- 2 files changed, 124 insertions(+), 60 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c8103534c89..c076114bc3c 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -90,13 +90,18 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, { } -void I2SClass::createCallbackTask() +int I2SClass::createCallbackTask() { int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); + if(_task_kill_cmd_semaphore_handle == NULL){ + log_e("Could not create semaphore"); + return 0; // ERR + } } + xTaskCreate( onDmaTransferComplete, // Function to implement the task "onDmaTransferComplete", // Name of the task @@ -105,13 +110,22 @@ void I2SClass::createCallbackTask() 2, // Priority of the task &_callbackTaskHandle // Task handle. ); + if(_callbackTaskHandle == NULL){ + log_e("Could not create callback task"); + return 0; // ERR + } } + return 1; // OK } void I2SClass::destroyCallbackTask() { if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ - xSemaphoreGive(_task_kill_cmd_semaphore_handle); + while(_callbackTaskHandle != NULL){ + ; // wait + } + vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage + _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore } // callback handle check } @@ -210,7 +224,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc .dma_buf_len = _I2S_DMA_BUFFER_SIZE }; - _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); @@ -245,54 +258,96 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } } - createCallbackTask(); + if(!createCallbackTask()){ + return 0; // ERR + } _initialized = true; return 1; // OK } -int I2SClass::setAllPins(){ - return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); +int I2SClass::_applyPinSetting(){ + if(_initialized){ + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = _sckPin, + .ws_io_num = _fsPin, + .data_out_num = _outSdPin, + .data_in_num = _inSdPin + }; + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ + log_e("i2s_set_pin failed"); + return 0; // ERR + } + } + return 1; // OK } -int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ +void I2SClass::_setSckPin(int sckPin){ if(sckPin >= 0){ _sckPin = sckPin; }else{ _sckPin = PIN_I2S_SCK; } +} + +int I2SClass::setSckPin(int sckPin){ + _setSckPin(sckPin); + return _applyPinSetting(); +} +void I2SClass::_setFsPin(int fsPin){ if(fsPin >= 0){ _fsPin = fsPin; }else{ _fsPin = PIN_I2S_FS; } +} + +int I2SClass::setFsPin(int fsPin){ + _setFsPin(fsPin); + return _applyPinSetting(); +} +void I2SClass::_setDataInPin(int inSdPin){ if(inSdPin >= 0){ _inSdPin = inSdPin; }else{ _inSdPin = PIN_I2S_SD; } +} + +int I2SClass::setDataInPin(int inSdPin){ + _setDataInPin(inSdPin); + pinMode(_inSdPin, INPUT_PULLDOWN); + // TODO if there is any default pinMode - set it to old input + return _applyPinSetting(); +} +void I2SClass::_setDataOutPin(int outSdPin){ if(outSdPin >= 0){ _outSdPin = outSdPin; }else{ _outSdPin = PIN_I2S_SD_OUT; } +} - if(_initialized){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin, - .data_in_num = _inSdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - } - } - return 1; // OK +int I2SClass::setDataOutPin(int outSdPin){ + _setDataOutPin(outSdPin); + return _applyPinSetting(); +} + + +int I2SClass::setAllPins(){ + return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); +} + +int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ + setSckPin(sckPin); + setFsPin(fsPin); + setDataInPin(inSdPin); + setDataOutPin(outSdPin); + + return _applyPinSetting(); } int I2SClass::setStateDuplex(){ @@ -304,42 +359,24 @@ int I2SClass::setStateDuplex(){ return 1; } -//int I2SClass::setHalfDuplex(uint8_t inSdPin=PIN_I2S_SD, uint8_t outSdPin=PIN_I2S_SD_OUT){ -int I2SClass::setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin){ - _inSdPin = inSdPin; - _outSdPin = outSdPin; - if(_initialized){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin, - .data_in_num = _inSdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - } - } - return 1; // OK +int I2SClass::getSckPin(){ + return _sckPin; } -//int I2SClass::setSimplex(uint8_t sdPin=PIN_I2S_SD){ -int I2SClass::setSimplex(uint8_t sdPin){ - _sdPin = sdPin; - if(_initialized){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - //.data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, - .data_out_num = I2S_PIN_NO_CHANGE, - .data_in_num = _sdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - } - } - return 1; // OK +int I2SClass::getFsPin(){ + return _fsPin; +} + +int I2SClass::getDataPin(){ + return _sdPin; +} + +int I2SClass::getDataInPin(){ + return _inSdPin; +} + +int I2SClass::getDataOutPin(){ + return _outSdPin; } void I2SClass::end() @@ -465,7 +502,18 @@ int I2SClass::peek() void I2SClass::flush() { - // do nothing, writes are DMA triggered + const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); + size_t item_size = 0; + size_t bytes_written; + void *item = NULL; + + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ + } + vRingbufferReturnItem(_output_ring_buffer, item); + } } // Bytes available to write @@ -580,6 +628,7 @@ void I2SClass::onTransferComplete() } // Check received item }while(item_size == bytes_written && item_size == single_dma_buf); } // don't read from almost empty buffer + if(_onTransmit){ _onTransmit(); } // user callback @@ -595,16 +644,16 @@ void I2SClass::onTransferComplete() } // user callback } // xRingbufferSendComplete } // RX Done - } // I2S event + } // Queue set (I2S event or kill command) } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task - vTaskDelete(NULL); } void I2SClass::onDmaTransferComplete(void*) { I2S.onTransferComplete(); + vTaskDelete(NULL); } #if I2S_INTERFACES_COUNT > 0 diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 5412bbc3c45..8af28cb59ee 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -64,10 +64,20 @@ class I2SClass : public Stream // change pin setup and mode (default is Half Duplex) // Can be called only on initialized object (after begin) int setStateDuplex(); + + int setSckPin(int sckPin); + int setFsPin(int fsPin); + int setDataInPin(int inSdPin); + int setDataOutPin(int outSdPin); + int setAllPins(); int setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin); - int setHalfDuplex(uint8_t inSdPin, uint8_t outSdPin); - int setSimplex(uint8_t sdPin); + + int getSckPin(); + int getFsPin(); + int getDataPin(); + int getDataInPin(); + int getDataOutPin(); void end(); @@ -101,9 +111,14 @@ class I2SClass : public Stream void onTransferComplete(); void destroyCallbackTask(); - void createCallbackTask(); + int createCallbackTask(); static void onDmaTransferComplete(void*); + void _setSckPin(int sckPin); + void _setFsPin(int fsPin); + void _setDataInPin(int inSdPin); + void _setDataOutPin(int outSdPin); + int _applyPinSetting(); private: typedef enum { From f95fdb62d51e8cf3a99d0e49678c4b4c3a818350 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 5 Aug 2021 15:03:02 +0200 Subject: [PATCH 36/94] Returning accidentally removed mutex give --- libraries/I2S/src/I2S.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c076114bc3c..bc8ffd15cb1 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -121,6 +121,7 @@ int I2SClass::createCallbackTask() void I2SClass::destroyCallbackTask() { if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ + xSemaphoreGive(_task_kill_cmd_semaphore_handle); while(_callbackTaskHandle != NULL){ ; // wait } From 8d99a4de0e0aeb94589b77559c1a6b496f2cb2a9 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 5 Aug 2021 16:16:33 +0200 Subject: [PATCH 37/94] Increased DMA buffer size for better performance --- libraries/I2S/src/I2S.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index bc8ffd15cb1..496d569ce30 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,8 +22,9 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_SIZE 256 // BUFFER SIZE must be between 200 and 1024 +#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 // (Theoretically it could be above 8, but for some reason sizes below 200 results in frozen callback task) +// And values bellow 500 may result in low quality audio #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -123,11 +124,13 @@ void I2SClass::destroyCallbackTask() if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ xSemaphoreGive(_task_kill_cmd_semaphore_handle); while(_callbackTaskHandle != NULL){ - ; // wait + ; // wait until task ends itself properly } vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore - } // callback handle check + }else{ // callback handle check + log_e("Could not destroy callback"); + } } int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) @@ -193,13 +196,17 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); }else{ // End of ADC/DAC mode; start of Normal mode - if(_bitsPerSample != 16 && /*_bitsPerSample != 24 && */ _bitsPerSample != 32){ - log_e("I2S.begin(): invalid bits per second for normal mode"); - // ESP does support 24 bps, however for the compatibility - // with original Arduino implementation it is not allowed + if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ + if(_bitsPerSample == 8){ + log_e("ESP unfortunately does not support 8 bits per sample"); + }else{ + log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 16 | 24 | 32", _bitsPerSample); + } return 0; // ERR } - + if(_bitsPerSample == 24){ + log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); + } if (_state == I2S_STATE_DUPLEX){ // duplex pin_config = { From 8519d6783251fca5e4b22dfbaa89f731fbfd39d9 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 13 Aug 2021 12:38:40 +0200 Subject: [PATCH 38/94] Split begin; changed pin setups and duplex/simplex setup --- .../I2S/examples/SimpleTone/SimpleTone.ino | 6 +- libraries/I2S/src/I2S.cpp | 270 ++++++++++-------- libraries/I2S/src/I2S.h | 13 +- 3 files changed, 160 insertions(+), 129 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 0967f50dca5..e3ad31b8055 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -20,6 +20,7 @@ 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 bps = 16; const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave @@ -31,14 +32,14 @@ void setup() { Serial.println("I2S simple tone"); // start I2S at the sample rate with 16-bits per sample - if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) { + if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bps)) { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } + I2S.setDataOutPin(26); } void loop() { - if(I2S.availableForWrite() >= 2){ if (count % halfWavelength == 0 ) { // invert the sample every half wavelength count multiple to generate square wave sample = -1 * sample; @@ -50,5 +51,4 @@ void loop() { // increment the counter for the next sample count++; - } } diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 496d569ce30..4a5bd958ac8 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,11 +21,8 @@ #include "I2S.h" #include "freertos/semphr.h" -#define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_SIZE 512 // BUFFER SIZE must be between 200 and 1024 -// (Theoretically it could be above 8, but for some reason sizes below 200 results in frozen callback task) -// And values bellow 500 may result in low quality audio +#define _I2S_EVENT_QUEUE_LENGTH 16 #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -58,6 +55,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _task_kill_cmd_semaphore_handle(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), + _i2s_dma_buffer_size(1024), + _driveClock(true), _onTransmit(NULL), _onReceive(NULL) @@ -85,6 +84,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _task_kill_cmd_semaphore_handle(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), + _i2s_dma_buffer_size(1024), + _driveClock(true), _onTransmit(NULL), _onReceive(NULL) @@ -93,7 +94,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, int I2SClass::createCallbackTask() { - int stack_size = 10000; + int stack_size = 15000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); @@ -133,62 +134,15 @@ void I2SClass::destroyCallbackTask() } } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) -{ - // master mode (driving clock and frame select pins - output) - return begin(mode, sampleRate, bitsPerSample, true); -} - -int I2SClass::begin(int mode, int bitsPerSample) -{ - log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ - Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ - \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); - return 0; // ERR - // slave mode (not driving clock and frame select pin - input) - //return begin(mode, 0, bitsPerSample, false); -} - -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) -{ - if(_initialized){ - end(); - } - - if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { - log_e("I2S.begin: unexpected _state (%d)",_state); - return 0; // ERR - } - - // TODO implement left / right justified modes - switch (mode) { - case I2S_PHILIPS_MODE: - case I2S_ADC_DAC: - break; - - case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - default: - // invalid mode - log_e("ERROR I2SClass::begin() unknown mode"); - return 0; // ERR - } - - _mode = mode; - _sampleRate = sampleRate; - _bitsPerSample = bitsPerSample; +int I2SClass::_install_driver(){ esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); - if(driveClock){ + if(_driveClock){ i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER); }else{ // TODO there will much more work with slave mode i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } - esp_i2s::i2s_pin_config_t pin_config; - pin_config.bck_io_num = _sckPin; - pin_config.ws_io_num = _fsPin; - if(_mode == I2S_ADC_DAC){ if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); @@ -208,18 +162,6 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } - if (_state == I2S_STATE_DUPLEX){ // duplex - pin_config = { - .data_out_num = _outSdPin, - .data_in_num = _inSdPin - }; - }else{ // simplex - pin_config = { - .data_out_num = I2S_PIN_NO_CHANGE, - .data_in_num = _sdPin - }; - } - } // Normal mode esp_i2s::i2s_config_t i2s_config = { .mode = i2s_mode, @@ -229,17 +171,9 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, - .dma_buf_len = _I2S_DMA_BUFFER_SIZE + .dma_buf_len = _i2s_dma_buffer_size }; - _buffer_byte_size = _I2S_DMA_BUFFER_SIZE * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; - _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); - _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); - if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ - log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); - return 0; // ERR - } - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver log_e("ERROR could not install i2s driver"); return 0; // ERR @@ -258,18 +192,78 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); + _initialized = true; }else{ // End of ADC/DAC mode; start of Normal mode - if (ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)) { - log_e("i2s_set_pin failed"); + _initialized = true; + if(!_applyPinSetting()){ end(); return 0; // ERR } } + return 1; // OK +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) +{ + // master mode (driving clock and frame select pins - output) + return begin(mode, sampleRate, bitsPerSample, true); +} + +int I2SClass::begin(int mode, int bitsPerSample) +{ + log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ + Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ + \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); + return 0; // ERR + // slave mode (not driving clock and frame select pin - input) + //return begin(mode, 0, bitsPerSample, false); +} + +int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) +{ + if(_initialized){ + end(); + } + _driveClock = driveClock; + _mode = mode; + _sampleRate = sampleRate; + _bitsPerSample = bitsPerSample; + + if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { + log_e("I2S.begin: unexpected _state (%d)",_state); + return 0; // ERR + } + + // TODO implement left / right justified modes + switch (mode) { + case I2S_PHILIPS_MODE: + case I2S_ADC_DAC: + break; + + case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP + default: + // invalid mode + log_e("ERROR I2SClass::begin() unknown mode"); + return 0; // ERR + } + + _buffer_byte_size = _i2s_dma_buffer_size * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; + _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); + _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); + if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ + log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); + return 0; // ERR + } + + if(!_install_driver()){ + return 0; // ERR + } + if(!createCallbackTask()){ return 0; // ERR } - _initialized = true; return 1; // OK } @@ -279,14 +273,31 @@ int I2SClass::_applyPinSetting(){ esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, - .data_out_num = _outSdPin, - .data_in_num = _inSdPin + .data_out_num = I2S_PIN_NO_CHANGE, + .data_in_num = I2S_PIN_NO_CHANGE }; + if (_state == I2S_STATE_DUPLEX){ // duplex + pin_config.data_out_num = _outSdPin; + pin_config.data_in_num = _inSdPin; + }else{ // simplex + if(_state == I2S_STATE_RECEIVER){ + pin_config.data_out_num = I2S_PIN_NO_CHANGE; + pin_config.data_in_num = _inSdPin>0 ? _inSdPin : _sdPin; + }else if(_state == I2S_STATE_TRANSMITTER){ + pin_config.data_out_num = _outSdPin>0 ? _outSdPin : _sdPin; + pin_config.data_in_num = I2S_PIN_NO_CHANGE; + }else{ + } + } + // pinMode(_inSdPin, INPUT_PULLDOWN); + // TODO if there is any default pinMode - set it to old input if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed"); + log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d\n", _sckPin, _fsPin, pin_config.data_in_num, pin_config.data_out_num); return 0; // ERR + }else{ + return 1; // OK } - } + } // Is driver _initialized ? return 1; // OK } @@ -326,8 +337,6 @@ void I2SClass::_setDataInPin(int inSdPin){ int I2SClass::setDataInPin(int inSdPin){ _setDataInPin(inSdPin); - pinMode(_inSdPin, INPUT_PULLDOWN); - // TODO if there is any default pinMode - set it to old input return _applyPinSetting(); } @@ -350,23 +359,38 @@ int I2SClass::setAllPins(){ } int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ - setSckPin(sckPin); - setFsPin(fsPin); - setDataInPin(inSdPin); - setDataOutPin(outSdPin); - - return _applyPinSetting(); + _setSckPin(sckPin); + _setFsPin(fsPin); + _setDataInPin(inSdPin); + _setDataOutPin(outSdPin); + //return _applyPinSetting(); + return 1; // OK } -int I2SClass::setStateDuplex(){ +int I2SClass::setDuplex(){ + /* if(_inSdPin < 0 || _outSdPin < 0){ - log_e("I2S cannot set Duplex-one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); + log_e("I2S cannot set Duplex - one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); return 0; // ERR } + */ _state = I2S_STATE_DUPLEX; return 1; } +int I2SClass::setSimplex(){ + if(_sdPin < 0){ + log_e("I2S cannot set Simplex - shared data pin is not set\n data pin = %d", _sdPin); + return 0; // ERR + } + _state = I2S_STATE_IDLE; + return 1; +} + +int I2SClass::isDuplex(){ + return (int)(_state == I2S_STATE_DUPLEX); +} + int I2SClass::getSckPin(){ return _sckPin; } @@ -387,12 +411,18 @@ int I2SClass::getDataOutPin(){ return _outSdPin; } +int I2SClass::_uninstall_driver(){ + // TODO + return 1; // Ok +} + void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ destroyCallbackTask(); if(_initialized){ + if(_mode == I2S_ADC_DAC){ esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); } @@ -510,7 +540,7 @@ int I2SClass::peek() void I2SClass::flush() { - const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); size_t item_size = 0; size_t bytes_written; void *item = NULL; @@ -540,44 +570,39 @@ void I2SClass::onReceive(void(*function)(void)) _onReceive = function; } -void I2SClass::setBufferSize(int bufferSize) +int I2SClass::setBufferSize(int bufferSize) { - // does nothing in ESP + if(bufferSize >= 8 && bufferSize <= 1024){ + _i2s_dma_buffer_size = bufferSize; + if(_initialized){ + end(); + return begin(_mode, _sampleRate, _bitsPerSample, _driveClock); + } + + return 1; // OK + }else{ + return 0; // ERR + } +} + +int I2SClass::getBufferSize(){ + return _i2s_dma_buffer_size; } int I2SClass::enableTransmitter() { - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = _outSdPin != -1 ? _outSdPin : _sdPin, - .data_in_num = -1 // esp_i2s::I2S_PIN_NO_CHANGE, - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - _state = I2S_STATE_IDLE; - return 0; // ERR - } + if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_TRANSMITTER){ _state = I2S_STATE_TRANSMITTER; + return _applyPinSetting(); } return 1; // Ok } int I2SClass::enableReceiver() { - if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX){ - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = _sckPin, - .ws_io_num = _fsPin, - .data_out_num = -1, // esp_i2s::I2S_PIN_NO_CHANGE, - .data_in_num = _inSdPin != -1 ? _inSdPin : _sdPin - }; - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - _state = I2S_STATE_IDLE; - log_e("i2s_set_pin failed"); - return 0; // ERR - } + if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_RECEIVER){ _state = I2S_STATE_RECEIVER; + return _applyPinSetting(); } return 1; // Ok } @@ -585,7 +610,7 @@ int I2SClass::enableReceiver() void I2SClass::onTransferComplete() { static QueueSetHandle_t xQueueSet; - const size_t single_dma_buf = _I2S_DMA_BUFFER_SIZE*(_bitsPerSample/8); + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_type_t i2s_event; size_t item_size = 0; @@ -594,8 +619,8 @@ void I2SClass::onTransferComplete() bool prev_item_valid = false; size_t bytes_written, bytes_read; int prev_item_offset = 0; - uint8_t prev_item[_I2S_DMA_BUFFER_SIZE*4]; - uint8_t _inputBuffer[_I2S_DMA_BUFFER_SIZE*4]; + uint8_t prev_item[_i2s_dma_buffer_size*4]; + uint8_t _inputBuffer[_i2s_dma_buffer_size*4]; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); @@ -620,7 +645,7 @@ void I2SClass::onTransferComplete() } // prev_item_valid if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer - do{ // "send to esp i2s driver" loop + bytes_written = 0; item_size = 0; item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); @@ -634,7 +659,6 @@ void I2SClass::onTransferComplete() } // save item that was not written correctly for later vRingbufferReturnItem(_output_ring_buffer, item); } // Check received item - }while(item_size == bytes_written && item_size == single_dma_buf); } // don't read from almost empty buffer if(_onTransmit){ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 8af28cb59ee..0be04db2387 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -63,8 +63,6 @@ class I2SClass : public Stream // change pin setup and mode (default is Half Duplex) // Can be called only on initialized object (after begin) - int setStateDuplex(); - int setSckPin(int sckPin); int setFsPin(int fsPin); int setDataInPin(int inSdPin); @@ -79,6 +77,10 @@ class I2SClass : public Stream int getDataInPin(); int getDataOutPin(); + int setDuplex(); + int setSimplex(); + int isDuplex(); + void end(); // from Stream @@ -102,7 +104,8 @@ class I2SClass : public Stream void onTransmit(void(*)(void)); void onReceive(void(*)(void)); - void setBufferSize(int bufferSize); + int setBufferSize(int bufferSize); + int getBufferSize(); private: int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock); @@ -114,6 +117,8 @@ class I2SClass : public Stream int createCallbackTask(); static void onDmaTransferComplete(void*); + int _install_driver(); + int _uninstall_driver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); void _setDataInPin(int inSdPin); @@ -148,6 +153,8 @@ class I2SClass : public Stream QueueHandle_t _task_kill_cmd_semaphore_handle; RingbufHandle_t _input_ring_buffer; RingbufHandle_t _output_ring_buffer; + int _i2s_dma_buffer_size; + bool _driveClock; void (*_onTransmit)(void); void (*_onReceive)(void); From b0d45cef321df2af71531731f86b4b057b4c34a2 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 18 Aug 2021 09:50:39 +0200 Subject: [PATCH 39/94] Initial implementation of PDM support (untested) --- libraries/I2S/src/I2S.cpp | 42 +++++++++++++++++++++++++++++---------- libraries/I2S/src/I2S.h | 7 ++++--- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 4a5bd958ac8..4def0ede2c4 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -134,7 +134,7 @@ void I2SClass::destroyCallbackTask() } } -int I2SClass::_install_driver(){ +int I2SClass::_installDriver(){ esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); if(_driveClock){ @@ -144,12 +144,19 @@ int I2SClass::_install_driver(){ i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } if(_mode == I2S_ADC_DAC){ - if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); + #if SOC_I2S_SUPPORTS_ADC_DAC + if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode + log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); + return 0; // ERR + } + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); + #else + log_e("This chip does not support DAC / ADC"); return 0; // ERR - } - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); - }else{ // End of ADC/DAC mode; start of Normal mode + #endif + }else if(_mode == I2S_PHILIPS_MODE || + _mode == I2S_RIGHT_JUSTIFIED_MODE || + _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal mode if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ if(_bitsPerSample == 8){ log_e("ESP unfortunately does not support 8 bits per sample"); @@ -162,7 +169,14 @@ int I2SClass::_install_driver(){ log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } - } // Normal mode + }else if(_mode == I2S_PDM){ + #if SOC_I2S_SUPPORTS_PDM + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); + #else + log_e("This chip does not support PDM"); + return 0; // ERR + #endif + } // Mode esp_i2s::i2s_config_t i2s_config = { .mode = i2s_mode, .sample_rate = _sampleRate, @@ -174,10 +188,16 @@ int I2SClass::_install_driver(){ .dma_buf_len = _i2s_dma_buffer_size }; - if (ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver + if(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver log_e("ERROR could not install i2s driver"); return 0; // ERR } + if(_i2sEventQueue == NULL){ + log_e("ERROR i2s driver did not create event queue"); + //return 0; // ERR + }else{ + log_d("DEBUG MSG i2s event queue exists"); + } if(_mode == I2S_ADC_DAC){ esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; @@ -239,6 +259,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc switch (mode) { case I2S_PHILIPS_MODE: case I2S_ADC_DAC: + case I2S_PDM: break; case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP @@ -257,7 +278,7 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc return 0; // ERR } - if(!_install_driver()){ + if(!_installDriver()){ return 0; // ERR } @@ -411,7 +432,7 @@ int I2SClass::getDataOutPin(){ return _outSdPin; } -int I2SClass::_uninstall_driver(){ +int I2SClass::_uninstallDriver(){ // TODO return 1; // Ok } @@ -624,6 +645,7 @@ void I2SClass::onTransferComplete() xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); + configASSERT(_i2sEventQueue); xQueueAddToSet(_i2sEventQueue, xQueueSet); xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 0be04db2387..42250d10128 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -47,7 +47,8 @@ typedef enum { I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, I2S_LEFT_JUSTIFIED_MODE, - I2S_ADC_DAC + I2S_ADC_DAC, + I2S_PDM } i2s_mode_t; class I2SClass : public Stream @@ -117,8 +118,8 @@ class I2SClass : public Stream int createCallbackTask(); static void onDmaTransferComplete(void*); - int _install_driver(); - int _uninstall_driver(); + int _installDriver(); + int _uninstallDriver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); void _setDataInPin(int inSdPin); From f45e5bafb31987aedfc91f04c9b314738591808b Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 20 Aug 2021 13:16:26 +0200 Subject: [PATCH 40/94] Added esp-dsp in CMakeLists.txt to compile in IDF --- CMakeLists.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08b8f866c5f..1acc79063be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,15 @@ set(CORE_SRCS set(LIBRARY_SRCS libraries/ArduinoOTA/src/ArduinoOTA.cpp + libraries/ArduinoSound/src/AmplitudeAnalyzer.cpp + libraries/ArduinoSound/src/AudioAnalyzer.cpp + libraries/ArduinoSound/src/AudioIn.cpp + libraries/ArduinoSound/src/AudioInI2S.cpp + libraries/ArduinoSound/src/AudioOut.cpp + libraries/ArduinoSound/src/AudioOutI2S.cpp + libraries/ArduinoSound/src/es8388.cpp + libraries/ArduinoSound/src/FFTAnalyzer.cpp + libraries/ArduinoSound/src/SDWaveFile.cpp libraries/AsyncUDP/src/AsyncUDP.cpp libraries/BluetoothSerial/src/BluetoothSerial.cpp libraries/BluetoothSerial/src/BTAddress.cpp @@ -137,11 +146,11 @@ set(BLE_SRCS libraries/BLE/src/GeneralUtils.cpp ) - set(includedirs variants/${IDF_TARGET}/ cores/esp32/ libraries/ArduinoOTA/src + libraries/ArduinoSound/src/ libraries/AsyncUDP/src libraries/BLE/src libraries/BluetoothSerial/src @@ -210,6 +219,8 @@ function(maybe_add_component component_name) endif() endfunction() +maybe_add_component(esp-dsp) + if(IDF_TARGET MATCHES "esp32" AND CONFIG_ESP_RMAKER_TASK_STACK) maybe_add_component(esp_rainmaker) maybe_add_component(qrcode) From cb6da04c88a0c03e0e399706d5f47bb0ac1d2099 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 1 Sep 2021 17:07:54 +0200 Subject: [PATCH 41/94] Pre-release changes --- libraries/I2S/src/I2S.cpp | 239 ++++++++++++++++++++++---------------- libraries/I2S/src/I2S.h | 16 ++- 2 files changed, 148 insertions(+), 107 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 4def0ede2c4..fcf546f7869 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -21,7 +21,6 @@ #include "I2S.h" #include "freertos/semphr.h" - #define _I2S_EVENT_QUEUE_LENGTH 16 #define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM @@ -94,7 +93,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, int I2SClass::createCallbackTask() { - int stack_size = 15000; + int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); @@ -129,9 +128,7 @@ void I2SClass::destroyCallbackTask() } vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore - }else{ // callback handle check - log_e("Could not destroy callback"); - } + } // callback handle check } int I2SClass::_installDriver(){ @@ -187,17 +184,19 @@ int I2SClass::_installDriver(){ .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _i2s_dma_buffer_size }; - - if(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // Install and start i2s driver - log_e("ERROR could not install i2s driver"); - return 0; // ERR - } - if(_i2sEventQueue == NULL){ - log_e("ERROR i2s driver did not create event queue"); - //return 0; // ERR - }else{ - log_d("DEBUG MSG i2s event queue exists"); - } + // Install and start i2s driver + while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ + // double buffer size + log_w("WARNING i2s driver install failed; Trying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); + if(2*_i2s_dma_buffer_size <= 1024){ + setBufferSize(2*_i2s_dma_buffer_size); + }else if(_i2s_dma_buffer_size < 1024){ + setBufferSize(1024); + }else{ + log_e("ERROR i2s driver install failed"); + return 0; // ERR + } + } //try installing with increasing size if(_mode == I2S_ADC_DAC){ esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; @@ -224,7 +223,7 @@ int I2SClass::_installDriver(){ return 1; // OK } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) +int I2SClass::begin(int mode, int sampleRate, int bitsPerSample) { // master mode (driving clock and frame select pins - output) return begin(mode, sampleRate, bitsPerSample, true); @@ -233,14 +232,14 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample) int I2SClass::begin(int mode, int bitsPerSample) { log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ - Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\ + Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\n\ \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); return 0; // ERR // slave mode (not driving clock and frame select pin - input) //return begin(mode, 0, bitsPerSample, false); } -int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveClock) +int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock) { if(_initialized){ end(); @@ -279,10 +278,12 @@ int I2SClass::begin(int mode, long sampleRate, int bitsPerSample, bool driveCloc } if(!_installDriver()){ + _initialized = false; return 0; // ERR } if(!createCallbackTask()){ + _initialized = false; return 0; // ERR } @@ -389,21 +390,11 @@ int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ } int I2SClass::setDuplex(){ - /* - if(_inSdPin < 0 || _outSdPin < 0){ - log_e("I2S cannot set Duplex - one or both pins not set\n input pin = %d\toutput pin = %d", _inSdPin, _outSdPin); - return 0; // ERR - } - */ _state = I2S_STATE_DUPLEX; return 1; } int I2SClass::setSimplex(){ - if(_sdPin < 0){ - log_e("I2S cannot set Simplex - shared data pin is not set\n data pin = %d", _sdPin); - return 0; // ERR - } _state = I2S_STATE_IDLE; return 1; } @@ -432,9 +423,15 @@ int I2SClass::getDataOutPin(){ return _outSdPin; } -int I2SClass::_uninstallDriver(){ - // TODO - return 1; // Ok +void I2SClass::_uninstallDriver(){ + if(_mode == I2S_ADC_DAC){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } } void I2SClass::end() @@ -443,15 +440,8 @@ void I2SClass::end() destroyCallbackTask(); if(_initialized){ - - if(_mode == I2S_ADC_DAC){ - esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); - } - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + _uninstallDriver(); _initialized = false; - if(_state != I2S_STATE_DUPLEX){ - _state = I2S_STATE_IDLE; - } } _onTransmit = NULL; _onReceive = NULL; @@ -538,13 +528,37 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) return write((const void*)buffer, size); } -size_t I2SClass::write(const void *buffer, size_t size) +// blocking version of write +// TODO add timeout +size_t I2SClass::write_blocking(const void *buffer, size_t size) +{ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { + if(!enableTransmitter()){ + return 0; // There was an error switching to transmitter + } + } + // TODO add timeout + while(availableForWrite() < size){ + yield(); + } + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + return size; + }else{ + return 0; + } +} + +// non-blocking version of write +size_t I2SClass::write_nonblocking(const void *buffer, size_t size) { if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!enableTransmitter()){ return 0; // There was an error switching to transmitter } } + if(availableForWrite() < size){ + flush(); + } if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ return size; }else{ @@ -552,6 +566,11 @@ size_t I2SClass::write(const void *buffer, size_t size) } } +size_t I2SClass::write(const void *buffer, size_t size){ + //return write_blocking(buffer, size); + return write_nonblocking(buffer, size); +} + int I2SClass::peek() { // TODO @@ -596,10 +615,9 @@ int I2SClass::setBufferSize(int bufferSize) if(bufferSize >= 8 && bufferSize <= 1024){ _i2s_dma_buffer_size = bufferSize; if(_initialized){ - end(); - return begin(_mode, _sampleRate, _bitsPerSample, _driveClock); + _uninstallDriver(); + return _installDriver(); } - return 1; // OK }else{ return 0; // ERR @@ -628,21 +646,70 @@ int I2SClass::enableReceiver() return 1; // Ok } +void I2SClass::_tx_done_routine(uint8_t* prev_item){ + static bool prev_item_valid = false; + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + static size_t item_size = 0; + static size_t prev_item_size = 0; + static void *item = NULL; + static int prev_item_offset = 0; + static size_t bytes_written; + + if(prev_item_valid){ // use item from previous round + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); + if(prev_item_size == bytes_written){ + prev_item_valid = false; + } // write size check + prev_item_offset = bytes_written; + prev_item_size -= bytes_written; + } // prev_item_valid + + if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer + bytes_written = 0; + item_size = 0; + //if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ // save item that was not written correctly for later + memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); + prev_item_size = item_size - bytes_written; + prev_item_offset = 0; + prev_item_valid = true; + } // save item that was not written correctly for later + vRingbufferReturnItem(_output_ring_buffer, item); + } // Check received item + } // don't read from almost empty buffer + + if(_onTransmit){ + _onTransmit(); + } // user callback +} + +void I2SClass::_rx_done_routine(){ + static size_t bytes_read; + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4); + + size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ + log_w("I2S failed to send item from DMA to internal buffer\n"); + }else{ + if (_onReceive) { + _onReceive(); + } // user callback + } // xRingbufferSendComplete + free(_inputBuffer); +} + + void I2SClass::onTransferComplete() { + uint8_t prev_item[_i2s_dma_buffer_size*4]; static QueueSetHandle_t xQueueSet; - const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); QueueSetMemberHandle_t xActivatedMember; - esp_i2s::i2s_event_type_t i2s_event; - size_t item_size = 0; - size_t prev_item_size = 0; - void *item = NULL; - bool prev_item_valid = false; - size_t bytes_written, bytes_read; - int prev_item_offset = 0; - uint8_t prev_item[_i2s_dma_buffer_size*4]; - uint8_t _inputBuffer[_i2s_dma_buffer_size*4]; - + esp_i2s::i2s_event_t i2s_event; xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); configASSERT(xQueueSet); configASSERT(_i2sEventQueue); @@ -650,62 +717,30 @@ void I2SClass::onTransferComplete() xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); + //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // defaul + xActivatedMember = xQueueSelectFromSet(xQueueSet, 5); // hack + // TODO try just queue receive at the timeout if(xActivatedMember == _task_kill_cmd_semaphore_handle){ xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop - }else if(xActivatedMember == _i2sEventQueue){ - xQueueReceive(_i2sEventQueue, &i2s_event, 0); - if(i2s_event == esp_i2s::I2S_EVENT_TX_DONE){ - if(prev_item_valid){ // use item from previous round - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); - if(prev_item_size == bytes_written){ - prev_item_valid = false; - } // write size check - prev_item_offset = bytes_written; - prev_item_size -= bytes_written; - } // prev_item_valid - - if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer - - bytes_written = 0; - item_size = 0; - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); - if (item != NULL){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); - if(item_size != bytes_written){ // save item that was not written correctly for later - memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); - prev_item_size = item_size - bytes_written; - prev_item_offset = 0; - prev_item_valid = true; - } // save item that was not written correctly for later - vRingbufferReturnItem(_output_ring_buffer, item); - } // Check received item - } // don't read from almost empty buffer - - if(_onTransmit){ - _onTransmit(); - } // user callback - - }else if(i2s_event == esp_i2s::I2S_EVENT_RX_DONE){ - size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); - if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ - log_w("I2S failed to send item from DMA to internal buffer\n"); - }else{ - if (_onReceive) { - _onReceive(); - } // user callback - } // xRingbufferSendComplete - } // RX Done - } // Queue set (I2S event or kill command) + //}else if(xActivatedMember == _i2sEventQueue){ // default + }else if(xActivatedMember == _i2sEventQueue || xActivatedMember == NULL){ // hack + if(uxQueueMessagesWaiting(_i2sEventQueue)){ + xQueueReceive(_i2sEventQueue, &i2s_event, 0); + if(i2s_event.type == esp_i2s::I2S_EVENT_TX_DONE){ + _tx_done_routine(prev_item); + }else if(i2s_event.type == esp_i2s::I2S_EVENT_RX_DONE){ + _rx_done_routine(); + } // RX Done + } // queue not empty + }else{ + } } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); vTaskDelete(NULL); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 42250d10128..6f8bce077dd 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -58,7 +58,7 @@ class I2SClass : public Stream I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin); I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin); // set duplex // the SCK and FS pins are driven as outputs using the sample rate - int begin(int mode, long sampleRate, int bitsPerSample); + int begin(int mode, int sampleRate, int bitsPerSample); // the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); @@ -101,14 +101,16 @@ class I2SClass : public Stream //size_t write(int); size_t write(int32_t); size_t write(const void *buffer, size_t size); + size_t write_blocking(const void *buffer, size_t size); + size_t write_nonblocking(const void *buffer, size_t size); void onTransmit(void(*)(void)); void onReceive(void(*)(void)); int setBufferSize(int bufferSize); - int getBufferSize(); + int getBufferSize(); private: - int begin(int mode, long sampleRate, int bitsPerSample, bool driveClock); + int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock); int enableTransmitter(); int enableReceiver(); @@ -119,7 +121,7 @@ class I2SClass : public Stream static void onDmaTransferComplete(void*); int _installDriver(); - int _uninstallDriver(); + void _uninstallDriver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); void _setDataInPin(int inSdPin); @@ -143,7 +145,8 @@ class I2SClass : public Stream i2s_state_t _state; int _bitsPerSample; - long _sampleRate; + uint32_t _sampleRate; // + //int _sampleRate; int _mode; uint16_t _buffer_byte_size; @@ -157,6 +160,9 @@ class I2SClass : public Stream int _i2s_dma_buffer_size; bool _driveClock; + void _tx_done_routine(uint8_t* prev_item); + void _rx_done_routine(); + void (*_onTransmit)(void); void (*_onReceive)(void); }; From cdf3a7c85fffda286f3e1f40896498bce01ac9ac Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 2 Sep 2021 09:26:53 +0200 Subject: [PATCH 42/94] Extended SimpleTone example with DAC settings --- .../I2S/examples/SimpleTone/SimpleTone.ino | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index e3ad31b8055..f2b2ab194f9 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -3,7 +3,7 @@ and sample rate. Then outputs the data using the I2S interface to a MAX08357 I2S Amp Breakout board. - Circuit: + I2S Circuit: * Arduino/Genuino Zero, MKR family and Nano 33 IoT * MAX08357: * GND connected GND @@ -12,8 +12,19 @@ * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + DAC Circuit: + * ESP32 or ESP32-S2 + * Audio amplifier + - Note: + - ESP32 has DAC on GPIO pins 25 and 26. + - ESP32-S2 has DAC on GPIO pins 17 and 18. + - Connect speaker(s) or headphones. + created 17 November 2016 by Sandeep Mistry + For ESP extended by + Tomas Pilny + 2nd September 2021 */ #include @@ -27,12 +38,16 @@ const int halfWavelength = (sampleRate / frequency); // half wavelength of squar short sample = amplitude; // current sample value int count = 0; +//i2s_mode_t mode = I2S_PHILIPS_MODE; +i2s_mode_t mode = I2S_ADC_DAC; + + 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, bps)) { + if (!I2S.begin(mode, sampleRate, bps)) { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } @@ -45,9 +60,11 @@ void loop() { sample = -1 * sample; } - // write the same sample twice, once for left and once for the right channel - I2S.write(sample); I2S.write(sample); + // write the same sample twice, once for left and once for the right channel + if(mode == I2S_PHILIPS_MODE){ + I2S.write(sample); + } // increment the counter for the next sample count++; From 991f54cc363371a3e7a30fe0cabe3a2691f0ad53 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 2 Sep 2021 09:59:03 +0200 Subject: [PATCH 43/94] Removed ArduinoSound from CMakeLists --- CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1acc79063be..34f3d5406de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,15 +46,6 @@ set(CORE_SRCS set(LIBRARY_SRCS libraries/ArduinoOTA/src/ArduinoOTA.cpp - libraries/ArduinoSound/src/AmplitudeAnalyzer.cpp - libraries/ArduinoSound/src/AudioAnalyzer.cpp - libraries/ArduinoSound/src/AudioIn.cpp - libraries/ArduinoSound/src/AudioInI2S.cpp - libraries/ArduinoSound/src/AudioOut.cpp - libraries/ArduinoSound/src/AudioOutI2S.cpp - libraries/ArduinoSound/src/es8388.cpp - libraries/ArduinoSound/src/FFTAnalyzer.cpp - libraries/ArduinoSound/src/SDWaveFile.cpp libraries/AsyncUDP/src/AsyncUDP.cpp libraries/BluetoothSerial/src/BluetoothSerial.cpp libraries/BluetoothSerial/src/BTAddress.cpp @@ -150,7 +141,6 @@ set(includedirs variants/${IDF_TARGET}/ cores/esp32/ libraries/ArduinoOTA/src - libraries/ArduinoSound/src/ libraries/AsyncUDP/src libraries/BLE/src libraries/BluetoothSerial/src From 9fd0042e730d33ea51ebd6d2a4eafb2cf62e522e Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 2 Sep 2021 17:12:30 +0200 Subject: [PATCH 44/94] ESP32-S2 support --- .../I2S/examples/SimpleTone/SimpleTone.ino | 4 ++-- libraries/I2S/src/I2S.cpp | 24 ++++++++++++++----- libraries/I2S/src/I2S.h | 6 ++++- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index f2b2ab194f9..f11804a815c 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -38,8 +38,8 @@ const int halfWavelength = (sampleRate / frequency); // half wavelength of squar short sample = amplitude; // current sample value int count = 0; -//i2s_mode_t mode = I2S_PHILIPS_MODE; -i2s_mode_t mode = I2S_ADC_DAC; +i2s_mode_t mode = I2S_PHILIPS_MODE; +//i2s_mode_t mode = I2S_ADC_DAC; void setup() { diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index fcf546f7869..5e3378ea825 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -140,6 +140,7 @@ int I2SClass::_installDriver(){ // TODO there will much more work with slave mode i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } +#if SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_ADC_DAC){ #if SOC_I2S_SUPPORTS_ADC_DAC if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode @@ -151,7 +152,9 @@ int I2SClass::_installDriver(){ log_e("This chip does not support DAC / ADC"); return 0; // ERR #endif - }else if(_mode == I2S_PHILIPS_MODE || + }else +#endif + if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal mode if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ @@ -179,7 +182,7 @@ int I2SClass::_installDriver(){ .sample_rate = _sampleRate, .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S | esp_i2s::I2S_COMM_FORMAT_STAND_PCM_SHORT), // 0x01 | 0x04 // default + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _i2s_dma_buffer_size @@ -198,6 +201,7 @@ int I2SClass::_installDriver(){ } } //try installing with increasing size +#if SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_ADC_DAC){ esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // @@ -212,13 +216,15 @@ int I2SClass::_installDriver(){ esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); _initialized = true; - }else{ // End of ADC/DAC mode; start of Normal mode + }else // End of ADC/DAC mode +#endif + if(_mode == I2S_PHILIPS_MODE){ // if Normal mode _initialized = true; if(!_applyPinSetting()){ end(); return 0; // ERR } - } + } // if _mode == ? return 1; // OK } @@ -257,7 +263,9 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock // TODO implement left / right justified modes switch (mode) { case I2S_PHILIPS_MODE: +#if SOC_I2S_SUPPORTS_ADC_DAC case I2S_ADC_DAC: +#endif case I2S_PDM: break; @@ -309,10 +317,10 @@ int I2SClass::_applyPinSetting(){ pin_config.data_out_num = _outSdPin>0 ? _outSdPin : _sdPin; pin_config.data_in_num = I2S_PIN_NO_CHANGE; }else{ + pin_config.data_out_num = I2S_PIN_NO_CHANGE; + pin_config.data_in_num = _sdPin; } } - // pinMode(_inSdPin, INPUT_PULLDOWN); - // TODO if there is any default pinMode - set it to old input if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d\n", _sckPin, _fsPin, pin_config.data_in_num, pin_config.data_out_num); return 0; // ERR @@ -424,9 +432,11 @@ int I2SClass::getDataOutPin(){ } void I2SClass::_uninstallDriver(){ +#if SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_ADC_DAC){ esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); } +#endif esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); if(_state != I2S_STATE_DUPLEX){ @@ -494,11 +504,13 @@ int I2SClass::read(void* buffer, size_t size){ tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); if(tmp_buffer != NULL){ memcpy(buffer, tmp_buffer, item_size); +#if SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_ADC_DAC){ for(size_t i = 0; i < item_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; } } // ADC/DAC mode +#endif vRingbufferReturnItem(_input_ring_buffer, tmp_buffer); return item_size; }else{ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 6f8bce077dd..ad373a14cce 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -32,7 +32,11 @@ namespace esp_i2s { #endif #ifndef PIN_I2S_FS - #define PIN_I2S_FS 25 + #if CONFIG_IDF_TARGET_ESP32S2 + #define PIN_I2S_FS 27 + #else + #define PIN_I2S_FS 25 + #endif #endif #ifndef PIN_I2S_SD From 7b77310af2ee97b267ad0c5dd2df9462ac3d3f64 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 3 Sep 2021 11:55:36 +0200 Subject: [PATCH 45/94] Removed excessive I2S from CMakeLists --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf185fea2db..34f3d5406de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,6 @@ set(LIBRARY_SRCS libraries/FS/src/vfs_api.cpp libraries/HTTPClient/src/HTTPClient.cpp libraries/HTTPUpdate/src/HTTPUpdate.cpp - libraries/I2S/src/I2S.cpp libraries/LittleFS/src/LittleFS.cpp libraries/I2S/src/I2S.cpp libraries/NetBIOS/src/NetBIOS.cpp From b3fdeecce22a7ab41b7eb0786277d748d551c213 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 20 Sep 2021 17:08:46 +0200 Subject: [PATCH 46/94] Variable pin for ADC --- libraries/I2S/src/I2S.cpp | 191 ++++++++++++++++++++++++++++++++++++-- libraries/I2S/src/I2S.h | 4 +- 2 files changed, 186 insertions(+), 9 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 5e3378ea825..a89f6f72928 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -203,17 +203,35 @@ int I2SClass::_installDriver(){ #if SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_ADC_DAC){ - esp_i2s::adc_unit_t adc_unit = (esp_i2s::adc_unit_t) 1; - esp_i2s::adc1_channel_t adc_channel = (esp_i2s::adc1_channel_t) 6; // esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); - esp_i2s::i2s_set_adc_mode(adc_unit, adc_channel); + esp_i2s::adc_unit_t adc_unit; + if(!gpioToAdcUnit((gpio_num_t)_inSdPin, &adc_unit)){ + log_e("pin to adc unit conversion failed"); + return 0; // ERR + } + esp_i2s::adc_channel_t adc_channel; + if(!gpioToAdcChannel((gpio_num_t)_inSdPin, &adc_channel)){ + log_e("pin to adc channel conversion failed"); + return 0; // ERR + } + if(ESP_OK != esp_i2s::i2s_set_adc_mode(adc_unit, (esp_i2s::adc1_channel_t)adc_channel)){ + log_e("i2s_set_adc_mode failed"); + end(); + return 0; // ERR + } if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ log_e("i2s_set_pin failed"); + end(); return 0; // ERR } - esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); - esp_i2s::adc1_config_channel_atten(adc_channel, esp_i2s::ADC_ATTEN_DB_11); + if(adc_unit == esp_i2s::ADC_UNIT_1){ + esp_i2s::adc1_config_width(esp_i2s::ADC_WIDTH_BIT_12); + esp_i2s::adc1_config_channel_atten((esp_i2s::adc1_channel_t)adc_channel, esp_i2s::ADC_ATTEN_DB_11); + }else if(adc_unit == esp_i2s::ADC_UNIT_2){ + esp_i2s::adc2_config_channel_atten((esp_i2s::adc2_channel_t)adc_channel, esp_i2s::ADC_ATTEN_DB_11); + } + esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); _initialized = true; }else // End of ADC/DAC mode @@ -252,7 +270,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock } _driveClock = driveClock; _mode = mode; - _sampleRate = sampleRate; + _sampleRate = (uint32_t)sampleRate; _bitsPerSample = bitsPerSample; if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { @@ -287,11 +305,13 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock if(!_installDriver()){ _initialized = false; + end(); return 0; // ERR } if(!createCallbackTask()){ _initialized = false; + end(); return 0; // ERR } @@ -455,8 +475,14 @@ void I2SClass::end() } _onTransmit = NULL; _onReceive = NULL; - vRingbufferDelete(_input_ring_buffer); - vRingbufferDelete(_output_ring_buffer); + if(_input_ring_buffer != NULL){ + vRingbufferDelete(_input_ring_buffer); + _input_ring_buffer = NULL; + } + if(_output_ring_buffer != NULL){ + vRingbufferDelete(_output_ring_buffer); + _output_ring_buffer = NULL; + } }else{ log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!"); } @@ -766,3 +792,152 @@ void I2SClass::onDmaTransferComplete(void*) // TODO set default pins for second module //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex #endif + +int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ + switch(gpio_num){ +#ifdef CONFIG_IDF_TARGET_ESP32 + // ADC 1 + case GPIO_NUM_36: + case GPIO_NUM_37: + case GPIO_NUM_38: + case GPIO_NUM_39: + case GPIO_NUM_32: + case GPIO_NUM_33: + case GPIO_NUM_34: + case GPIO_NUM_35: + *adc_unit = esp_i2s::ADC_UNIT_1; + return 1; // OK + + // ADC 2 + case GPIO_NUM_0: + log_w("GPIO 0 for ADC should not be used for dev boards due to external auto program circuits."); + case GPIO_NUM_4: + case GPIO_NUM_2: + case GPIO_NUM_15: + case GPIO_NUM_13: + case GPIO_NUM_12: + case GPIO_NUM_14: + case GPIO_NUM_27: + case GPIO_NUM_25: + case GPIO_NUM_26: + *adc_unit = esp_i2s::ADC_UNIT_2; + return 1; // OK +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + case GPIO_NUM_1: + case GPIO_NUM_2: + case GPIO_NUM_3: + case GPIO_NUM_4: + case GPIO_NUM_5: + case GPIO_NUM_6: + case GPIO_NUM_7: + case GPIO_NUM_8: + case GPIO_NUM_9: + case GPIO_NUM_10: + *adc_unit = esp_i2s::ADC_UNIT_1; + return 1; // OK +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32S2 + case GPIO_NUM_11: + case GPIO_NUM_12: + case GPIO_NUM_13: + case GPIO_NUM_14: + case GPIO_NUM_15: + case GPIO_NUM_16: + case GPIO_NUM_17: + case GPIO_NUM_18: + case GPIO_NUM_19: + case GPIO_NUM_20: + *adc_unit = esp_i2s::ADC_UNIT_2; + return 1; // OK +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + case GPIO_NUM_0: + case GPIO_NUM_1: + case GPIO_NUM_2: + case GPIO_NUM_3: + case GPIO_NUM_4: + *adc_unit = esp_i2s::ADC_UNIT_1; + return 1; // OK + case GPIO_NUM_5: + *adc_unit = esp_i2s::ADC_UNIT_2; + return 1; // OK +#endif + default: + log_e("GPIO %d not usable for ADC!", gpio_num); + log_i("Please refer to documentation https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html"); + return 0; // ERR + } +} + +int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel){ + switch(gpio_num){ +#ifdef CONFIG_IDF_TARGET_ESP32 + // ADC 1 + case GPIO_NUM_36: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK + case GPIO_NUM_37: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK + case GPIO_NUM_38: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK + case GPIO_NUM_39: *adc_channel = esp_i2s::ADC_CHANNEL_3; return 1; // OK + case GPIO_NUM_32: *adc_channel = esp_i2s::ADC_CHANNEL_4; return 1; // OK + case GPIO_NUM_33: *adc_channel = esp_i2s::ADC_CHANNEL_5; return 1; // OK + case GPIO_NUM_34: *adc_channel = esp_i2s::ADC_CHANNEL_6; return 1; // OK + case GPIO_NUM_35: *adc_channel = esp_i2s::ADC_CHANNEL_7; return 1; // OK + + // ADC 2 + case GPIO_NUM_0: + log_w("GPIO 0 for ADC should not be used for dev boards due to external auto program circuits."); + *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK + case GPIO_NUM_4: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK + case GPIO_NUM_2: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK + case GPIO_NUM_15: *adc_channel = esp_i2s::ADC_CHANNEL_3; return 1; // OK + case GPIO_NUM_13: *adc_channel = esp_i2s::ADC_CHANNEL_4; return 1; // OK + case GPIO_NUM_12: *adc_channel = esp_i2s::ADC_CHANNEL_5; return 1; // OK + case GPIO_NUM_14: *adc_channel = esp_i2s::ADC_CHANNEL_6; return 1; // OK + case GPIO_NUM_27: *adc_channel = esp_i2s::ADC_CHANNEL_7; return 1; // OK + case GPIO_NUM_25: *adc_channel = esp_i2s::ADC_CHANNEL_8; return 1; // OK + case GPIO_NUM_26: *adc_channel = esp_i2s::ADC_CHANNEL_9; return 1; // OK +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + case GPIO_NUM_1: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK + case GPIO_NUM_2: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK + case GPIO_NUM_3: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK + case GPIO_NUM_4: *adc_channel = esp_i2s::ADC_CHANNEL_3; return 1; // OK + case GPIO_NUM_5: *adc_channel = esp_i2s::ADC_CHANNEL_4; return 1; // OK + case GPIO_NUM_6: *adc_channel = esp_i2s::ADC_CHANNEL_5; return 1; // OK + case GPIO_NUM_7: *adc_channel = esp_i2s::ADC_CHANNEL_6; return 1; // OK + case GPIO_NUM_8: *adc_channel = esp_i2s::ADC_CHANNEL_7; return 1; // OK + case GPIO_NUM_9: *adc_channel = esp_i2s::ADC_CHANNEL_8; return 1; // OK + case GPIO_NUM_10: *adc_channel = esp_i2s::ADC_CHANNEL_9; return 1; // OK +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32S2 + case GPIO_NUM_11: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK + case GPIO_NUM_12: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK + case GPIO_NUM_13: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK + case GPIO_NUM_14: *adc_channel = esp_i2s::ADC_CHANNEL_3; return 1; // OK + case GPIO_NUM_15: *adc_channel = esp_i2s::ADC_CHANNEL_4; return 1; // OK + case GPIO_NUM_16: *adc_channel = esp_i2s::ADC_CHANNEL_5; return 1; // OK + case GPIO_NUM_17: *adc_channel = esp_i2s::ADC_CHANNEL_6; return 1; // OK + case GPIO_NUM_18: *adc_channel = esp_i2s::ADC_CHANNEL_7; return 1; // OK + case GPIO_NUM_19: *adc_channel = esp_i2s::ADC_CHANNEL_8; return 1; // OK + case GPIO_NUM_20: *adc_channel = esp_i2s::ADC_CHANNEL_9; return 1; // OK +#endif + +#ifdef CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 + case GPIO_NUM_0: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK + case GPIO_NUM_1: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK + case GPIO_NUM_2: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK + case GPIO_NUM_3: *adc_channel = esp_i2s::ADC_CHANNEL_3; return 1; // OK + case GPIO_NUM_4: *adc_channel = esp_i2s::ADC_CHANNEL_4; return 1; // OK + case GPIO_NUM_5: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK +#endif + default: + log_e("GPIO %d not usable for ADC!", gpio_num); + log_i("Please refer to documentation https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html"); + return 0; // ERR + } +} diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index ad373a14cce..9dae9ea359c 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -112,7 +112,9 @@ class I2SClass : public Stream void onReceive(void(*)(void)); int setBufferSize(int bufferSize); - int getBufferSize(); + int getBufferSize(); + int gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit); + int gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel); private: int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock); From cde002fb70f906d485caeb6252dcdd356fac4059 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 21 Sep 2021 14:21:43 +0200 Subject: [PATCH 47/94] Fixed data types for sample_rate inconsistent with IDF i2s driver --- tools/sdk/esp32/include/hal/include/hal/i2s_types.h | 2 +- tools/sdk/esp32c3/include/hal/include/hal/i2s_types.h | 2 +- tools/sdk/esp32s2/include/hal/include/hal/i2s_types.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/sdk/esp32/include/hal/include/hal/i2s_types.h b/tools/sdk/esp32/include/hal/include/hal/i2s_types.h index ba150cc7b34..23640831016 100644 --- a/tools/sdk/esp32/include/hal/include/hal/i2s_types.h +++ b/tools/sdk/esp32/include/hal/include/hal/i2s_types.h @@ -121,7 +121,7 @@ typedef enum { */ typedef struct { i2s_mode_t mode; /*!< I2S work mode*/ - int sample_rate; /*!< I2S sample rate*/ + uint32_t sample_rate; /*!< I2S sample rate*/ i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/ i2s_channel_fmt_t channel_format; /*!< I2S channel format */ i2s_comm_format_t communication_format; /*!< I2S communication format */ diff --git a/tools/sdk/esp32c3/include/hal/include/hal/i2s_types.h b/tools/sdk/esp32c3/include/hal/include/hal/i2s_types.h index ba150cc7b34..23640831016 100644 --- a/tools/sdk/esp32c3/include/hal/include/hal/i2s_types.h +++ b/tools/sdk/esp32c3/include/hal/include/hal/i2s_types.h @@ -121,7 +121,7 @@ typedef enum { */ typedef struct { i2s_mode_t mode; /*!< I2S work mode*/ - int sample_rate; /*!< I2S sample rate*/ + uint32_t sample_rate; /*!< I2S sample rate*/ i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/ i2s_channel_fmt_t channel_format; /*!< I2S channel format */ i2s_comm_format_t communication_format; /*!< I2S communication format */ diff --git a/tools/sdk/esp32s2/include/hal/include/hal/i2s_types.h b/tools/sdk/esp32s2/include/hal/include/hal/i2s_types.h index ba150cc7b34..23640831016 100644 --- a/tools/sdk/esp32s2/include/hal/include/hal/i2s_types.h +++ b/tools/sdk/esp32s2/include/hal/include/hal/i2s_types.h @@ -121,7 +121,7 @@ typedef enum { */ typedef struct { i2s_mode_t mode; /*!< I2S work mode*/ - int sample_rate; /*!< I2S sample rate*/ + uint32_t sample_rate; /*!< I2S sample rate*/ i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/ i2s_channel_fmt_t channel_format; /*!< I2S channel format */ i2s_comm_format_t communication_format; /*!< I2S communication format */ From 14e2c7f242f01668e228ae0f7a78b247e6499594 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 21 Sep 2021 14:40:42 +0200 Subject: [PATCH 48/94] Fixed #ifdef to #if --- libraries/I2S/src/I2S.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index a89f6f72928..82c91a97344 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -795,7 +795,7 @@ void I2SClass::onDmaTransferComplete(void*) int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ switch(gpio_num){ -#ifdef CONFIG_IDF_TARGET_ESP32 +#if CONFIG_IDF_TARGET_ESP32 // ADC 1 case GPIO_NUM_36: case GPIO_NUM_37: @@ -824,7 +824,7 @@ int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ return 1; // OK #endif -#ifdef CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) case GPIO_NUM_1: case GPIO_NUM_2: case GPIO_NUM_3: @@ -854,7 +854,7 @@ int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ return 1; // OK #endif -#ifdef CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 +#if (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2) case GPIO_NUM_0: case GPIO_NUM_1: case GPIO_NUM_2: @@ -901,7 +901,7 @@ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_ case GPIO_NUM_26: *adc_channel = esp_i2s::ADC_CHANNEL_9; return 1; // OK #endif -#ifdef CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) case GPIO_NUM_1: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK case GPIO_NUM_2: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK case GPIO_NUM_3: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK @@ -927,7 +927,7 @@ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_ case GPIO_NUM_20: *adc_channel = esp_i2s::ADC_CHANNEL_9; return 1; // OK #endif -#ifdef CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 +#if (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2) case GPIO_NUM_0: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK case GPIO_NUM_1: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK case GPIO_NUM_2: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK From 2645cf4b8a80f9ebc4de667ce84afce6d5c38e65 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 21 Sep 2021 14:41:31 +0200 Subject: [PATCH 49/94] Fixed forgotten #ifdef to #if --- libraries/I2S/src/I2S.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 82c91a97344..c1f6a017bb2 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -839,7 +839,7 @@ int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ return 1; // OK #endif -#ifdef CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 case GPIO_NUM_11: case GPIO_NUM_12: case GPIO_NUM_13: @@ -875,7 +875,7 @@ int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel){ switch(gpio_num){ -#ifdef CONFIG_IDF_TARGET_ESP32 +#if CONFIG_IDF_TARGET_ESP32 // ADC 1 case GPIO_NUM_36: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK case GPIO_NUM_37: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK @@ -914,7 +914,7 @@ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_ case GPIO_NUM_10: *adc_channel = esp_i2s::ADC_CHANNEL_9; return 1; // OK #endif -#ifdef CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 case GPIO_NUM_11: *adc_channel = esp_i2s::ADC_CHANNEL_0; return 1; // OK case GPIO_NUM_12: *adc_channel = esp_i2s::ADC_CHANNEL_1; return 1; // OK case GPIO_NUM_13: *adc_channel = esp_i2s::ADC_CHANNEL_2; return 1; // OK From 80f49da16e858ee87483782c3459060671c8fcab Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 22 Sep 2021 13:25:36 +0200 Subject: [PATCH 50/94] Updated Keywords --- libraries/I2S/keywords.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libraries/I2S/keywords.txt b/libraries/I2S/keywords.txt index 217868dea36..603dcc0fdfe 100644 --- a/libraries/I2S/keywords.txt +++ b/libraries/I2S/keywords.txt @@ -11,13 +11,40 @@ I2S KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### +I2SClass KEYWORD2 begin KEYWORD2 end KEYWORD2 onReceive KEYWORD2 onTransmit KEYWORD2 +setSckPin KEYWORD2 +setFsPin KEYWORD2 +setDataInPin KEYWORD2 +setDataOutPin KEYWORD2 +setAllPins KEYWORD2 + +getSckPin KEYWORD2 +getFsPin KEYWORD2 +getDataPin KEYWORD2 +getDataInPin KEYWORD2 +getDataOutPin KEYWORD2 + +setDuplex KEYWORD2 +setSimplex KEYWORD2 +isDuplex KEYWORD2 + setBufferSize KEYWORD2 +getBufferSize KEYWORD2 + +write KEYWORD2 +availableForWrite KEYWORD2 + +read KEYWORD2 +available KEYWORD2 + +gpioToAdcUnit KEYWORD2 +gpioToAdcChannel KEYWORD2 ####################################### # Constants (LITERAL1) @@ -26,3 +53,9 @@ I2S_PHILIPS_MODE LITERAL1 I2S_RIGHT_JUSTIFIED_MODE LITERAL1 I2S_LEFT_JUSTIFIED_MODE LITERAL1 I2S_ADC_DAC LITERAL1 +I2S_PDM LITERAL1 + +PIN_I2S_SCK LITERAL1 +PIN_I2S_FS LITERAL1 +PIN_I2S_SD LITERAL1 +PIN_I2S_SD_OUT LITERAL1 From 139aef38bfa26c285a29ac339b016e52c4dfeca9 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 22 Sep 2021 13:27:09 +0200 Subject: [PATCH 51/94] GPIO to ADC conversion methods wrapped in ADC support #if --- libraries/I2S/src/I2S.cpp | 33 +++++++++++++++++---------------- libraries/I2S/src/I2S.h | 6 ++++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c1f6a017bb2..ffc6e71087e 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -140,7 +140,7 @@ int I2SClass::_installDriver(){ // TODO there will much more work with slave mode i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } -#if SOC_I2S_SUPPORTS_ADC_DAC + if(_mode == I2S_ADC_DAC){ #if SOC_I2S_SUPPORTS_ADC_DAC if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode @@ -152,9 +152,7 @@ int I2SClass::_installDriver(){ log_e("This chip does not support DAC / ADC"); return 0; // ERR #endif - }else -#endif - if(_mode == I2S_PHILIPS_MODE || + }else if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal mode if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ @@ -185,7 +183,8 @@ int I2SClass::_installDriver(){ .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, - .dma_buf_len = _i2s_dma_buffer_size + .dma_buf_len = _i2s_dma_buffer_size, + .use_apll = false }; // Install and start i2s driver while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ @@ -235,7 +234,7 @@ int I2SClass::_installDriver(){ esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); _initialized = true; }else // End of ADC/DAC mode -#endif +#endif // SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_PHILIPS_MODE){ // if Normal mode _initialized = true; if(!_applyPinSetting()){ @@ -783,16 +782,7 @@ void I2SClass::onDmaTransferComplete(void*) vTaskDelete(NULL); } -#if I2S_INTERFACES_COUNT > 0 - I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex - //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex -#endif - -#if I2S_INTERFACES_COUNT > 1 - // TODO set default pins for second module - //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex -#endif - +#if SOC_I2S_SUPPORTS_ADC_DAC int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ switch(gpio_num){ #if CONFIG_IDF_TARGET_ESP32 @@ -941,3 +931,14 @@ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_ return 0; // ERR } } +#endif // SOC_I2S_SUPPORTS_ADC_DAC + +#if I2S_INTERFACES_COUNT > 0 + I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex + //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex +#endif + +#if I2S_INTERFACES_COUNT > 1 + // TODO set default pins for second module + //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex +#endif \ No newline at end of file diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9dae9ea359c..18a29ea5b9d 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -113,8 +113,10 @@ class I2SClass : public Stream int setBufferSize(int bufferSize); int getBufferSize(); - int gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit); - int gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel); + #if SOC_I2S_SUPPORTS_ADC_DAC + int gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit); + int gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel); + #endif private: int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock); From 1d481e9d7dfbc525320c77dc2e0516941153ac87 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 22 Sep 2021 13:32:06 +0200 Subject: [PATCH 52/94] Added .skip.esp32c3 files untill C3 is supported --- libraries/I2S/examples/ADCPlotter/.skip.esp32c3 | 0 libraries/I2S/examples/Callbacks/.skip.esp32c3 | 0 libraries/I2S/examples/InputSerialPlotter/.skip.esp32c3 | 0 libraries/I2S/examples/SimpleTone/.skip.esp32c3 | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 libraries/I2S/examples/ADCPlotter/.skip.esp32c3 create mode 100644 libraries/I2S/examples/Callbacks/.skip.esp32c3 create mode 100644 libraries/I2S/examples/InputSerialPlotter/.skip.esp32c3 create mode 100644 libraries/I2S/examples/SimpleTone/.skip.esp32c3 diff --git a/libraries/I2S/examples/ADCPlotter/.skip.esp32c3 b/libraries/I2S/examples/ADCPlotter/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/examples/Callbacks/.skip.esp32c3 b/libraries/I2S/examples/Callbacks/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/examples/InputSerialPlotter/.skip.esp32c3 b/libraries/I2S/examples/InputSerialPlotter/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/examples/SimpleTone/.skip.esp32c3 b/libraries/I2S/examples/SimpleTone/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d From ca0887be19678d12d967b8f9fb3ab35c01b3c91e Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 22 Sep 2021 16:23:57 +0200 Subject: [PATCH 53/94] All private functions have now underscore preceeding their name --- libraries/I2S/src/I2S.cpp | 24 ++++++++++++------------ libraries/I2S/src/I2S.h | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index ffc6e71087e..03b1b24b49a 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -91,7 +91,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, { } -int I2SClass::createCallbackTask() +int I2SClass::_createCallbackTask() { int stack_size = 10000; if(_callbackTaskHandle == NULL){ @@ -119,7 +119,7 @@ int I2SClass::createCallbackTask() return 1; // OK } -void I2SClass::destroyCallbackTask() +void I2SClass::_destroyCallbackTask() { if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ xSemaphoreGive(_task_kill_cmd_semaphore_handle); @@ -308,7 +308,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock return 0; // ERR } - if(!createCallbackTask()){ + if(!_createCallbackTask()){ _initialized = false; end(); return 0; // ERR @@ -466,7 +466,7 @@ void I2SClass::_uninstallDriver(){ void I2SClass::end() { if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ - destroyCallbackTask(); + _destroyCallbackTask(); if(_initialized){ _uninstallDriver(); @@ -519,7 +519,7 @@ int I2SClass::read() int I2SClass::read(void* buffer, size_t size){ if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { - if(!enableReceiver()){ + if(!_enableReceiver()){ return 0; // There was an error switching to receiver } } @@ -570,7 +570,7 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size) size_t I2SClass::write_blocking(const void *buffer, size_t size) { if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { - if(!enableTransmitter()){ + if(!_enableTransmitter()){ return 0; // There was an error switching to transmitter } } @@ -589,7 +589,7 @@ size_t I2SClass::write_blocking(const void *buffer, size_t size) size_t I2SClass::write_nonblocking(const void *buffer, size_t size) { if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { - if(!enableTransmitter()){ + if(!_enableTransmitter()){ return 0; // There was an error switching to transmitter } } @@ -665,7 +665,7 @@ int I2SClass::getBufferSize(){ return _i2s_dma_buffer_size; } -int I2SClass::enableTransmitter() +int I2SClass::_enableTransmitter() { if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_TRANSMITTER){ _state = I2S_STATE_TRANSMITTER; @@ -674,7 +674,7 @@ int I2SClass::enableTransmitter() return 1; // Ok } -int I2SClass::enableReceiver() +int I2SClass::_enableReceiver() { if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_RECEIVER){ _state = I2S_STATE_RECEIVER; @@ -741,7 +741,7 @@ void I2SClass::_rx_done_routine(){ } -void I2SClass::onTransferComplete() +void I2SClass::_onTransferComplete() { uint8_t prev_item[_i2s_dma_buffer_size*4]; static QueueSetHandle_t xQueueSet; @@ -778,7 +778,7 @@ void I2SClass::onTransferComplete() void I2SClass::onDmaTransferComplete(void*) { - I2S.onTransferComplete(); + I2S._onTransferComplete(); vTaskDelete(NULL); } @@ -941,4 +941,4 @@ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_ #if I2S_INTERFACES_COUNT > 1 // TODO set default pins for second module //I2SClass I2S1(I2S_DEVICE+1, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex -#endif \ No newline at end of file +#endif diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 18a29ea5b9d..10967a0aeba 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -120,12 +120,12 @@ class I2SClass : public Stream private: int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock); - int enableTransmitter(); - int enableReceiver(); - void onTransferComplete(); + int _enableTransmitter(); + int _enableReceiver(); + void _onTransferComplete(); - void destroyCallbackTask(); - int createCallbackTask(); + void _destroyCallbackTask(); + int _createCallbackTask(); static void onDmaTransferComplete(void*); int _installDriver(); From 2dfab5f875249c436ad1c0f8d3ce24e1c3bbd58a Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 22 Sep 2021 16:27:19 +0200 Subject: [PATCH 54/94] Proposed wrapping functions for thread safety --- libraries/I2S/src/I2S.cpp | 36 ++++++++++++++++++++++++++++++++++++ libraries/I2S/src/I2S.h | 5 +++++ 2 files changed, 41 insertions(+) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 03b1b24b49a..59ddae48bf9 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -52,14 +52,20 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), _i2s_dma_buffer_size(1024), _driveClock(true), + _dive_counter(0), _onTransmit(NULL), _onReceive(NULL) { + _i2s_general_mutex = xSemaphoreCreateMutex(); + if(_i2s_general_mutex == NULL){ + log_e("I2S could not create internal mutex!"); + } } I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex @@ -81,14 +87,20 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _callbackTaskHandle(NULL), _i2sEventQueue(NULL), _task_kill_cmd_semaphore_handle(NULL), + _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), _i2s_dma_buffer_size(1024), _driveClock(true), + _dive_counter(0), _onTransmit(NULL), _onReceive(NULL) { + _i2s_general_mutex = xSemaphoreCreateMutex(); + if(_i2s_general_mutex == NULL){ + log_e("I2S could not create internal mutex!"); + } } int I2SClass::_createCallbackTask() @@ -782,6 +794,30 @@ void I2SClass::onDmaTransferComplete(void*) vTaskDelete(NULL); } +void I2SClass::_take_if_not_holding(){ + TaskHandle_t mutex_holder = xSemaphoreGetMutexHolder(_i2s_general_mutex); + if(mutex_holder != NULL && mutex_holder == xTaskGetCurrentTaskHandle()){ + ++_dive_counter; + return; // we are already holding this mutex - no need to take it + } + + // we are not holding the mutex - wait for it and take it + if(xSemaphoreTake(_i2s_general_mutex, portMAX_DELAY) != pdTRUE ){ + log_e("I2S internal mutex take returned with error") + } + //_give_if_top_call(); // call after this function +} + +void _give_if_top_call(){ + if(_dive_counter){ + --_dive_counter; + }else{ + if(xSemaphoreGive(_i2s_general_mutex) != pdTRUE){ + log_e("I2S intternal mutex give error"); + } + } +} + #if SOC_I2S_SUPPORTS_ADC_DAC int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ switch(gpio_num){ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 10967a0aeba..9e8fe0874af 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -163,6 +163,7 @@ class I2SClass : public Stream TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; QueueHandle_t _task_kill_cmd_semaphore_handle; + SemaphoreHandle_t _i2s_general_mutex; RingbufHandle_t _input_ring_buffer; RingbufHandle_t _output_ring_buffer; int _i2s_dma_buffer_size; @@ -171,6 +172,10 @@ class I2SClass : public Stream void _tx_done_routine(uint8_t* prev_item); void _rx_done_routine(); + uint16_t _dive_counter; + void _take_if_not_holding(); + void _give_if_top_call(); + void (*_onTransmit)(void); void (*_onReceive)(void); }; From 83d509451d7b4de60cf836af590f807ab18851b4 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 23 Sep 2021 09:28:16 +0200 Subject: [PATCH 55/94] Small fix: Added new function as class member --- libraries/I2S/src/I2S.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 59ddae48bf9..a39b552031c 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -808,7 +808,7 @@ void I2SClass::_take_if_not_holding(){ //_give_if_top_call(); // call after this function } -void _give_if_top_call(){ +void I2SClass::_give_if_top_call(){ if(_dive_counter){ --_dive_counter; }else{ From 28e69479559874dc275f68df45805b073fc282d0 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 23 Sep 2021 09:58:18 +0200 Subject: [PATCH 56/94] Applied nesting locks to all public functions --- libraries/I2S/src/I2S.cpp | 163 +++++++++++++++++++++++++++++++------- libraries/I2S/src/I2S.h | 2 +- 2 files changed, 135 insertions(+), 30 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index a39b552031c..f02f33faf8b 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -57,7 +57,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _output_ring_buffer(NULL), _i2s_dma_buffer_size(1024), _driveClock(true), - _dive_counter(0), + _nesting_counter(0), _onTransmit(NULL), _onReceive(NULL) @@ -92,7 +92,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, _output_ring_buffer(NULL), _i2s_dma_buffer_size(1024), _driveClock(true), - _dive_counter(0), + _nesting_counter(0), _onTransmit(NULL), _onReceive(NULL) @@ -260,8 +260,12 @@ int I2SClass::_installDriver(){ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample) { + _take_if_not_holding(); // master mode (driving clock and frame select pins - output) - return begin(mode, sampleRate, bitsPerSample, true); + int ret = begin(mode, sampleRate, bitsPerSample, true); + _give_if_top_call(); + return ret; + } int I2SClass::begin(int mode, int bitsPerSample) @@ -276,6 +280,7 @@ int I2SClass::begin(int mode, int bitsPerSample) int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock) { + _take_if_not_holding(); if(_initialized){ end(); } @@ -286,6 +291,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { log_e("I2S.begin: unexpected _state (%d)",_state); + _give_if_top_call(); return 0; // ERR } @@ -303,6 +309,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock default: // invalid mode log_e("ERROR I2SClass::begin() unknown mode"); + _give_if_top_call(); return 0; // ERR } @@ -311,21 +318,24 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); + _give_if_top_call(); return 0; // ERR } if(!_installDriver()){ _initialized = false; end(); + _give_if_top_call(); return 0; // ERR } if(!_createCallbackTask()){ _initialized = false; end(); + _give_if_top_call(); return 0; // ERR } - + _give_if_top_call(); return 1; // OK } @@ -371,8 +381,12 @@ void I2SClass::_setSckPin(int sckPin){ } int I2SClass::setSckPin(int sckPin){ + _take_if_not_holding(); _setSckPin(sckPin); - return _applyPinSetting(); + int ret = _applyPinSetting(); + _applyPinSetting(); + _give_if_top_call(); + return ret; } void I2SClass::_setFsPin(int fsPin){ @@ -384,8 +398,11 @@ void I2SClass::_setFsPin(int fsPin){ } int I2SClass::setFsPin(int fsPin){ + _take_if_not_holding(); _setFsPin(fsPin); - return _applyPinSetting(); + int ret = _applyPinSetting(); + _give_if_top_call(); + return ret; } void I2SClass::_setDataInPin(int inSdPin){ @@ -397,8 +414,11 @@ void I2SClass::_setDataInPin(int inSdPin){ } int I2SClass::setDataInPin(int inSdPin){ + _take_if_not_holding(); _setDataInPin(inSdPin); - return _applyPinSetting(); + int ret = _applyPinSetting(); + _give_if_top_call(); + return ret; } void I2SClass::_setDataOutPin(int outSdPin){ @@ -410,56 +430,85 @@ void I2SClass::_setDataOutPin(int outSdPin){ } int I2SClass::setDataOutPin(int outSdPin){ + _take_if_not_holding(); _setDataOutPin(outSdPin); - return _applyPinSetting(); + int ret = _applyPinSetting(); + _give_if_top_call(); + return ret; } int I2SClass::setAllPins(){ - return setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); + _take_if_not_holding(); + int ret = setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); + _give_if_top_call(); + return ret; } int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ + _take_if_not_holding(); _setSckPin(sckPin); _setFsPin(fsPin); _setDataInPin(inSdPin); _setDataOutPin(outSdPin); - //return _applyPinSetting(); + _give_if_top_call(); return 1; // OK } int I2SClass::setDuplex(){ + _take_if_not_holding(); _state = I2S_STATE_DUPLEX; + _give_if_top_call(); return 1; } int I2SClass::setSimplex(){ + _take_if_not_holding(); _state = I2S_STATE_IDLE; + _give_if_top_call(); return 1; } int I2SClass::isDuplex(){ - return (int)(_state == I2S_STATE_DUPLEX); + _take_if_not_holding(); + int ret = (int)(_state == I2S_STATE_DUPLEX); + _give_if_top_call(); + return ret; } int I2SClass::getSckPin(){ - return _sckPin; + _take_if_not_holding(); + int ret = _sckPin; + _give_if_top_call(); + return ret; } int I2SClass::getFsPin(){ - return _fsPin; + _take_if_not_holding(); + int ret = _fsPin; + _give_if_top_call(); + return ret; } int I2SClass::getDataPin(){ - return _sdPin; + _take_if_not_holding(); + int ret = _sdPin; + _give_if_top_call(); + return ret; } int I2SClass::getDataInPin(){ - return _inSdPin; + _take_if_not_holding(); + int ret = _inSdPin; + _give_if_top_call(); + return ret; } int I2SClass::getDataOutPin(){ - return _outSdPin; + _take_if_not_holding(); + int ret = _outSdPin; + _give_if_top_call(); + return ret; } void I2SClass::_uninstallDriver(){ @@ -477,6 +526,7 @@ void I2SClass::_uninstallDriver(){ void I2SClass::end() { + _take_if_not_holding(); if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ _destroyCallbackTask(); @@ -497,12 +547,16 @@ void I2SClass::end() }else{ log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!"); } + _give_if_top_call(); } // Bytes available to read int I2SClass::available() { - return _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer); + _take_if_not_holding(); + int ret = _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer); + _give_if_top_call(); + return ret; } union i2s_sample_t { @@ -513,25 +567,33 @@ union i2s_sample_t { int I2SClass::read() { + _take_if_not_holding(); i2s_sample_t sample; sample.b32 = 0; read(&sample, _bitsPerSample / 8); if (_bitsPerSample == 32) { + _give_if_top_call(); return sample.b32; } else if (_bitsPerSample == 16) { + _give_if_top_call(); return sample.b16; } else if (_bitsPerSample == 8) { + _give_if_top_call(); return sample.b8; } else { + _give_if_top_call(); return 0; } + _give_if_top_call(); return 0; } int I2SClass::read(void* buffer, size_t size){ + _take_if_not_holding(); if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { if(!_enableReceiver()){ + _give_if_top_call(); return 0; // There was an error switching to receiver } } @@ -551,8 +613,10 @@ int I2SClass::read(void* buffer, size_t size){ vRingbufferReturnItem(_input_ring_buffer, tmp_buffer); return item_size; }else{ + _give_if_top_call(); return 0; } + _give_if_top_call(); } /* @@ -564,25 +628,36 @@ size_t I2SClass::write(int sample) size_t I2SClass::write(uint8_t data) { - return write((int32_t)data); + _take_if_not_holding(); + size_t ret = write((int32_t)data); + _give_if_top_call(); + return ret; } size_t I2SClass::write(int32_t sample) { - return write(&sample, _bitsPerSample/8); + _take_if_not_holding(); + size_t ret = write(&sample, _bitsPerSample/8); + _give_if_top_call(); + return ret; } size_t I2SClass::write(const uint8_t *buffer, size_t size) { - return write((const void*)buffer, size); + _take_if_not_holding(); + size_t ret = write((const void*)buffer, size); + _give_if_top_call(); + return ret; } // blocking version of write // TODO add timeout size_t I2SClass::write_blocking(const void *buffer, size_t size) { + _take_if_not_holding(); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!_enableTransmitter()){ + _give_if_top_call(); return 0; // There was an error switching to transmitter } } @@ -591,17 +666,22 @@ size_t I2SClass::write_blocking(const void *buffer, size_t size) yield(); } if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + _give_if_top_call(); return size; }else{ + _give_if_top_call(); return 0; } + _give_if_top_call(); } // non-blocking version of write size_t I2SClass::write_nonblocking(const void *buffer, size_t size) { + _take_if_not_holding(); if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { if(!_enableTransmitter()){ + _give_if_top_call(); return 0; // There was an error switching to transmitter } } @@ -609,26 +689,36 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size) flush(); } if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + _give_if_top_call(); return size; }else{ + _give_if_top_call(); return 0; } + _give_if_top_call(); } size_t I2SClass::write(const void *buffer, size_t size){ - //return write_blocking(buffer, size); - return write_nonblocking(buffer, size); + _take_if_not_holding(); + //size_t ret = write_blocking(buffer, size); + size_t ret = write_nonblocking(buffer, size); + _give_if_top_call(); + return ret; } int I2SClass::peek() { + _take_if_not_holding(); // TODO // peek() is not implemented for ESP yet - return 0; + int ret = 0; + _give_if_top_call(); + return ret; } void I2SClass::flush() { + _take_if_not_holding(); const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); size_t item_size = 0; size_t bytes_written; @@ -641,40 +731,56 @@ void I2SClass::flush() } vRingbufferReturnItem(_output_ring_buffer, item); } + _give_if_top_call(); } // Bytes available to write int I2SClass::availableForWrite() { - return (int)xRingbufferGetCurFreeSize(_output_ring_buffer); + _take_if_not_holding(); + int ret = (int)xRingbufferGetCurFreeSize(_output_ring_buffer); + _give_if_top_call(); + return ret; } void I2SClass::onTransmit(void(*function)(void)) { + _take_if_not_holding(); _onTransmit = function; + _give_if_top_call(); } void I2SClass::onReceive(void(*function)(void)) { + _take_if_not_holding(); _onReceive = function; + _give_if_top_call(); } int I2SClass::setBufferSize(int bufferSize) { + _take_if_not_holding(); if(bufferSize >= 8 && bufferSize <= 1024){ _i2s_dma_buffer_size = bufferSize; if(_initialized){ _uninstallDriver(); + _give_if_top_call(); return _installDriver(); } + _give_if_top_call(); return 1; // OK }else{ + _give_if_top_call(); return 0; // ERR } + _give_if_top_call(); } int I2SClass::getBufferSize(){ - return _i2s_dma_buffer_size; + _take_if_not_holding(); + int ret = _i2s_dma_buffer_size; + _give_if_top_call(); + return ret; } int I2SClass::_enableTransmitter() @@ -752,7 +858,6 @@ void I2SClass::_rx_done_routine(){ free(_inputBuffer); } - void I2SClass::_onTransferComplete() { uint8_t prev_item[_i2s_dma_buffer_size*4]; @@ -797,7 +902,7 @@ void I2SClass::onDmaTransferComplete(void*) void I2SClass::_take_if_not_holding(){ TaskHandle_t mutex_holder = xSemaphoreGetMutexHolder(_i2s_general_mutex); if(mutex_holder != NULL && mutex_holder == xTaskGetCurrentTaskHandle()){ - ++_dive_counter; + ++_nesting_counter; return; // we are already holding this mutex - no need to take it } @@ -809,8 +914,8 @@ void I2SClass::_take_if_not_holding(){ } void I2SClass::_give_if_top_call(){ - if(_dive_counter){ - --_dive_counter; + if(_nesting_counter){ + --_nesting_counter; }else{ if(xSemaphoreGive(_i2s_general_mutex) != pdTRUE){ log_e("I2S intternal mutex give error"); diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9e8fe0874af..c7d1ef11d9f 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -172,7 +172,7 @@ class I2SClass : public Stream void _tx_done_routine(uint8_t* prev_item); void _rx_done_routine(); - uint16_t _dive_counter; + uint16_t _nesting_counter; void _take_if_not_holding(); void _give_if_top_call(); From a8727ad1b8473fe45e1f7a09193768eb88e0cabd Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 29 Sep 2021 14:53:35 +0200 Subject: [PATCH 57/94] Added aditional checks --- libraries/I2S/src/I2S.cpp | 346 ++++++++++++++++++++------------------ 1 file changed, 185 insertions(+), 161 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index f02f33faf8b..0430a78b10d 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -103,8 +103,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, } } -int I2SClass::_createCallbackTask() -{ +int I2SClass::_createCallbackTask(){ int stack_size = 10000; if(_callbackTaskHandle == NULL){ if(_task_kill_cmd_semaphore_handle == NULL){ @@ -131,9 +130,8 @@ int I2SClass::_createCallbackTask() return 1; // OK } -void I2SClass::_destroyCallbackTask() -{ - if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ +void I2SClass::_destroyCallbackTask(){ + if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle && _task_kill_cmd_semaphore_handle != NULL){ xSemaphoreGive(_task_kill_cmd_semaphore_handle); while(_callbackTaskHandle != NULL){ ; // wait until task ends itself properly @@ -258,8 +256,7 @@ int I2SClass::_installDriver(){ return 1; // OK } -int I2SClass::begin(int mode, int sampleRate, int bitsPerSample) -{ +int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){ _take_if_not_holding(); // master mode (driving clock and frame select pins - output) int ret = begin(mode, sampleRate, bitsPerSample, true); @@ -268,8 +265,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample) } -int I2SClass::begin(int mode, int bitsPerSample) -{ +int I2SClass::begin(int mode, int bitsPerSample){ log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\n\ \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); @@ -278,8 +274,7 @@ int I2SClass::begin(int mode, int bitsPerSample) //return begin(mode, 0, bitsPerSample, false); } -int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock) -{ +int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){ _take_if_not_holding(); if(_initialized){ end(); @@ -524,12 +519,10 @@ void I2SClass::_uninstallDriver(){ } } -void I2SClass::end() -{ +void I2SClass::end(){ _take_if_not_holding(); if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ _destroyCallbackTask(); - if(_initialized){ _uninstallDriver(); _initialized = false; @@ -551,10 +544,12 @@ void I2SClass::end() } // Bytes available to read -int I2SClass::available() -{ +int I2SClass::available(){ _take_if_not_holding(); - int ret = _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer); + int ret = 0; + if(_input_ring_buffer != NULL){ + ret = _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer); + } _give_if_top_call(); return ret; } @@ -565,57 +560,63 @@ union i2s_sample_t { int32_t b32; }; -int I2SClass::read() -{ +int I2SClass::read(){ _take_if_not_holding(); i2s_sample_t sample; sample.b32 = 0; - read(&sample, _bitsPerSample / 8); - - if (_bitsPerSample == 32) { - _give_if_top_call(); - return sample.b32; - } else if (_bitsPerSample == 16) { - _give_if_top_call(); - return sample.b16; - } else if (_bitsPerSample == 8) { - _give_if_top_call(); - return sample.b8; - } else { - _give_if_top_call(); - return 0; - } + if(_initialized){ + read(&sample, _bitsPerSample / 8); + if (_bitsPerSample == 32) { + _give_if_top_call(); + return sample.b32; + } else if (_bitsPerSample == 16) { + _give_if_top_call(); + return sample.b16; + } else if (_bitsPerSample == 8) { + _give_if_top_call(); + return sample.b8; + } else { + _give_if_top_call(); + return 0; + } + } // if(_initialized) _give_if_top_call(); return 0; } int I2SClass::read(void* buffer, size_t size){ _take_if_not_holding(); - if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { - if(!_enableReceiver()){ - _give_if_top_call(); - return 0; // There was an error switching to receiver - } - } - - size_t item_size = 0; - void *tmp_buffer; - tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); - if(tmp_buffer != NULL){ - memcpy(buffer, tmp_buffer, item_size); -#if SOC_I2S_SUPPORTS_ADC_DAC - if(_mode == I2S_ADC_DAC){ - for(size_t i = 0; i < item_size / 2; ++i){ - ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; - } - } // ADC/DAC mode -#endif - vRingbufferReturnItem(_input_ring_buffer, tmp_buffer); - return item_size; - }else{ - _give_if_top_call(); - return 0; - } + if(_initialized){ + if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { + if(!_enableReceiver()){ + _give_if_top_call(); + return 0; // There was an error switching to receiver + } // _enableReceiver succeeded ? + } // _state ? + + size_t item_size = 0; + void *tmp_buffer; + if(_input_ring_buffer != NULL){ + tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); + if(tmp_buffer != NULL){ + memcpy(buffer, tmp_buffer, item_size); + #if SOC_I2S_SUPPORTS_ADC_DAC + if(_mode == I2S_ADC_DAC){ + for(size_t i = 0; i < item_size / 2; ++i){ + ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; + } + } // ADC/DAC mode + #endif + vRingbufferReturnItem(_input_ring_buffer, tmp_buffer); + _give_if_top_call(); + return item_size; + }else{ + _give_if_top_call(); + return 0; + } // tmp buffer not NULL ? + } // ring buffer not NULL ? + } // if(_initialized) + return 0; _give_if_top_call(); } @@ -626,154 +627,179 @@ size_t I2SClass::write(int sample) } */ -size_t I2SClass::write(uint8_t data) -{ +size_t I2SClass::write(uint8_t data){ _take_if_not_holding(); - size_t ret = write((int32_t)data); + size_t ret = 0; + if(_initialized){ + ret = write((int32_t)data); + } _give_if_top_call(); return ret; } -size_t I2SClass::write(int32_t sample) -{ +size_t I2SClass::write(int32_t sample){ _take_if_not_holding(); - size_t ret = write(&sample, _bitsPerSample/8); + size_t ret = 0; + if(_initialized){ + ret = write(&sample, _bitsPerSample/8); + } _give_if_top_call(); return ret; } -size_t I2SClass::write(const uint8_t *buffer, size_t size) -{ +size_t I2SClass::write(const uint8_t *buffer, size_t size){ _take_if_not_holding(); - size_t ret = write((const void*)buffer, size); + size_t ret = 0; + if(_initialized){ + ret = write((const void*)buffer, size); + } _give_if_top_call(); return ret; } // blocking version of write // TODO add timeout -size_t I2SClass::write_blocking(const void *buffer, size_t size) -{ +size_t I2SClass::write_blocking(const void *buffer, size_t size){ _take_if_not_holding(); - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { - if(!_enableTransmitter()){ - _give_if_top_call(); - return 0; // There was an error switching to transmitter + if(_initialized){ + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { + if(!_enableTransmitter()){ + _give_if_top_call(); + return 0; // There was an error switching to transmitter + } // _enableTransmitter succeeded ? + } // _state ? + // TODO add timeout + while(availableForWrite() < size){ + yield(); } - } - // TODO add timeout - while(availableForWrite() < size){ - yield(); - } - if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ - _give_if_top_call(); - return size; - }else{ - _give_if_top_call(); - return 0; - } + if(_output_ring_buffer != NULL){ + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + _give_if_top_call(); + return size; + }else{ + _give_if_top_call(); + return 0; + } // ring buffer send ok ? + } // ring buffer not NULL ? + } // if(_initialized) + return 0; _give_if_top_call(); } // non-blocking version of write -size_t I2SClass::write_nonblocking(const void *buffer, size_t size) -{ +size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ _take_if_not_holding(); - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { - if(!_enableTransmitter()){ - _give_if_top_call(); - return 0; // There was an error switching to transmitter + if(_initialized){ + if(_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ + if(!_enableTransmitter()){ + _give_if_top_call(); + return 0; // There was an error switching to transmitter + } } - } - if(availableForWrite() < size){ - flush(); - } - if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ - _give_if_top_call(); - return size; - }else{ - _give_if_top_call(); - return 0; - } - _give_if_top_call(); + if(availableForWrite() < size){ + flush(); + } + if(_output_ring_buffer != NULL){ + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + _give_if_top_call(); + return size; + }else{ + _give_if_top_call(); + return 0; + } + } + } // if(_initialized) + return 0; + _give_if_top_call(); // this should not be needed } size_t I2SClass::write(const void *buffer, size_t size){ _take_if_not_holding(); - //size_t ret = write_blocking(buffer, size); - size_t ret = write_nonblocking(buffer, size); + size_t ret = 0; + if(_initialized){ + //size_t ret = write_blocking(buffer, size); + ret = write_nonblocking(buffer, size); + } // if(_initialized) _give_if_top_call(); return ret; } -int I2SClass::peek() -{ +int I2SClass::peek(){ _take_if_not_holding(); - // TODO - // peek() is not implemented for ESP yet int ret = 0; + if(_initialized){ + // TODO + // peek() is not implemented for ESP yet + ret = 0; + } // if(_initialized) _give_if_top_call(); return ret; } -void I2SClass::flush() -{ +void I2SClass::flush(){ _take_if_not_holding(); - const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); - size_t item_size = 0; - size_t bytes_written; - void *item = NULL; - - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); - if (item != NULL){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); - if(item_size != bytes_written){ + if(_initialized){ + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + size_t item_size = 0; + size_t bytes_written; + void *item = NULL; + if(_output_ring_buffer != NULL){ + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); + if (item != NULL){ + esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + if(item_size != bytes_written){ + } + vRingbufferReturnItem(_output_ring_buffer, item); + } } - vRingbufferReturnItem(_output_ring_buffer, item); - } + } // if(_initialized) _give_if_top_call(); } // Bytes available to write -int I2SClass::availableForWrite() -{ +int I2SClass::availableForWrite(){ _take_if_not_holding(); - int ret = (int)xRingbufferGetCurFreeSize(_output_ring_buffer); + int ret = 0; + if(_initialized){ + if(_output_ring_buffer != NULL){ + ret = (int)xRingbufferGetCurFreeSize(_output_ring_buffer); + } + } // if(_initialized) _give_if_top_call(); return ret; } -void I2SClass::onTransmit(void(*function)(void)) -{ +void I2SClass::onTransmit(void(*function)(void)){ _take_if_not_holding(); _onTransmit = function; _give_if_top_call(); } -void I2SClass::onReceive(void(*function)(void)) -{ +void I2SClass::onReceive(void(*function)(void)){ _take_if_not_holding(); _onReceive = function; _give_if_top_call(); } -int I2SClass::setBufferSize(int bufferSize) -{ +int I2SClass::setBufferSize(int bufferSize){ _take_if_not_holding(); - if(bufferSize >= 8 && bufferSize <= 1024){ - _i2s_dma_buffer_size = bufferSize; - if(_initialized){ + int ret = 0; + if(_initialized){ + if(bufferSize >= 8 && bufferSize <= 1024){ + _i2s_dma_buffer_size = bufferSize; _uninstallDriver(); + ret = _installDriver(); _give_if_top_call(); - return _installDriver(); - } + return ret; _give_if_top_call(); return 1; // OK - }else{ - _give_if_top_call(); - return 0; // ERR - } + }else{ // check requested buffer size + _give_if_top_call(); + return 0; // ERR + } // check requested buffer size + } // if(_initialized) _give_if_top_call(); + return 0; // ERR } int I2SClass::getBufferSize(){ @@ -783,8 +809,7 @@ int I2SClass::getBufferSize(){ return ret; } -int I2SClass::_enableTransmitter() -{ +int I2SClass::_enableTransmitter(){ if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_TRANSMITTER){ _state = I2S_STATE_TRANSMITTER; return _applyPinSetting(); @@ -792,8 +817,7 @@ int I2SClass::_enableTransmitter() return 1; // Ok } -int I2SClass::_enableReceiver() -{ +int I2SClass::_enableReceiver(){ if(_state != I2S_STATE_DUPLEX && _state != I2S_STATE_RECEIVER){ _state = I2S_STATE_RECEIVER; return _applyPinSetting(); @@ -819,7 +843,7 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ prev_item_size -= bytes_written; } // prev_item_valid - if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf){ // fill up the I2S DMA buffer + if(_output_ring_buffer != NULL && (_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf)){ // fill up the I2S DMA buffer bytes_written = 0; item_size = 0; //if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer @@ -844,22 +868,23 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ void I2SClass::_rx_done_routine(){ static size_t bytes_read; const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); - uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4); - size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); - if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ - log_w("I2S failed to send item from DMA to internal buffer\n"); - }else{ - if (_onReceive) { - _onReceive(); - } // user callback - } // xRingbufferSendComplete - free(_inputBuffer); + if(_input_ring_buffer != NULL){ + uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4); + size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ + log_w("I2S failed to send item from DMA to internal buffer\n"); + }else{ + if (_onReceive) { + _onReceive(); + } // user callback + } // xRingbufferSendComplete + free(_inputBuffer); + } } -void I2SClass::_onTransferComplete() -{ +void I2SClass::_onTransferComplete(){ uint8_t prev_item[_i2s_dma_buffer_size*4]; static QueueSetHandle_t xQueueSet; QueueSetMemberHandle_t xActivatedMember; @@ -893,8 +918,7 @@ void I2SClass::_onTransferComplete() _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } -void I2SClass::onDmaTransferComplete(void*) -{ +void I2SClass::onDmaTransferComplete(void*){ I2S._onTransferComplete(); vTaskDelete(NULL); } From b1e138769d2a3877c63e881fb23249a9753f748c Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 30 Sep 2021 09:40:40 +0200 Subject: [PATCH 58/94] Added thread safety example --- .../I2S/examples/thread_safety/.skip.esp32c3 | 0 .../examples/thread_safety/thread_safety.ino | 126 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 libraries/I2S/examples/thread_safety/.skip.esp32c3 create mode 100644 libraries/I2S/examples/thread_safety/thread_safety.ino diff --git a/libraries/I2S/examples/thread_safety/.skip.esp32c3 b/libraries/I2S/examples/thread_safety/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/examples/thread_safety/thread_safety.ino b/libraries/I2S/examples/thread_safety/thread_safety.ino new file mode 100644 index 00000000000..f8f8bf1c2a8 --- /dev/null +++ b/libraries/I2S/examples/thread_safety/thread_safety.ino @@ -0,0 +1,126 @@ +/* + This example is only for ESP devices. + + This sketch verify I2S lib thread safety = when multiple threads are accessing same object + it should not corrupt object state, lead to racing conditions nor crashing. + +Hardware: + 1. Any ESP32 or ESP32-S2 + + Steps to run: + 1. Select target board: + Tools -> Board -> ESP32 Arduino -> your board + 2. Upload sketch + Press upload button (arrow in top left corner) + When you see in console line like this: "Connecting........_____.....__" + If loading doesn't start automatically, you may need to press and hold Boot + button and press EN button shortly. Now you can release both buttons. + You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. + If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. + 3. Open monitor + Tools -> Serial Monitor + 4. Observe + No crash should occur + +Created by Tomas Pilny +on 30th Sep 2021 +*/ + +#include +const int sampleRate = 16000; // sample rate in Hz +const int bitsPerSample = 16; +TaskHandle_t _Task1Handle; +TaskHandle_t _Task2Handle; +TaskHandle_t _Task3Handle; + + +void task1(void*){ + while(true){ + I2S.setAllPins(); // apply default pins + int foo = I2S.read(); + delay(1); + } +} + +void task2(void*){ + while(true){ + I2S.setAllPins(5, 25, 26, 27); + I2S.write(123); + delay(1); + } +} + +void task3(void*){ + // Annoying kid: "What does this switch do?"" + while(true){ + I2S.end(); // flip + delay(100); + if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 16)){ // flop + log_e("Could not start I2S"); + while (1) {delay(100);} // halt + } + delay(100); + } +} + +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 + } + + if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 16)){ + log_e("Could not start I2S"); + while (1) {delay(100);} // halt + } + + _Task1Handle = NULL; + xTaskCreate( + task1, // Function to implement the task + "task1", // Name of the task + 2000, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_Task1Handle // Task handle. + ); + if(_Task1Handle == NULL){ + log_e("Could not create callback task 1"); + while (1) {;} // halt + } + + _Task2Handle = NULL; + xTaskCreate( + task2, // Function to implement the task + "task2", // Name of the task + 2000, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_Task2Handle // Task handle. + ); + if(_Task2Handle == NULL){ + log_e("Could not create callback task 2"); + while (1) {;} // halt + } + + _Task3Handle = NULL; + xTaskCreate( + task3, // Function to implement the task + "task3", // Name of the task + 2000, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_Task3Handle // Task handle. + ); + if(_Task3Handle == NULL){ + log_e("Could not create callback task 3"); + while (1) {;} // halt + } +} + +void loop() { + // loop task remains free for other work + delay(10); // Let the FreeRTOS reset the watchDogTimer +} From 39372eb0688fd2dc5e3fb7fa5008bfeb88afca93 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 30 Sep 2021 09:55:04 +0200 Subject: [PATCH 59/94] Thread safety example: Added 2 tasks to use more public functions --- .../examples/thread_safety/thread_safety.ino | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/libraries/I2S/examples/thread_safety/thread_safety.ino b/libraries/I2S/examples/thread_safety/thread_safety.ino index f8f8bf1c2a8..6ed7fad3bed 100644 --- a/libraries/I2S/examples/thread_safety/thread_safety.ino +++ b/libraries/I2S/examples/thread_safety/thread_safety.ino @@ -32,14 +32,18 @@ const int bitsPerSample = 16; TaskHandle_t _Task1Handle; TaskHandle_t _Task2Handle; TaskHandle_t _Task3Handle; +TaskHandle_t _Task4Handle; +TaskHandle_t _Task5Handle; void task1(void*){ while(true){ I2S.setAllPins(); // apply default pins - int foo = I2S.read(); + if(I2S.available()){ + int foo = I2S.read(); + } delay(1); - } + } // infinite loop } void task2(void*){ @@ -47,7 +51,7 @@ void task2(void*){ I2S.setAllPins(5, 25, 26, 27); I2S.write(123); delay(1); - } + } // infinite loop } void task3(void*){ @@ -60,7 +64,34 @@ void task3(void*){ while (1) {delay(100);} // halt } delay(100); - } + } // infinite loop +} + +void task4(void*){ + const int buf_size = 64; + uint8_t buffer [buf_size]; + while(true){ + if(I2S.available() >= buf_size){ + int read = I2S.read(buffer, buf_size); + if(read != buf_size){ + log_e("task4: did not read complete buffer"); + } + } + delay(5); + } // infinite loop +} + +void task5(void*){ + int i2s_buffer_size; + while(true){ + i2s_buffer_size = I2S.getBufferSize(); + delay(1); + if(!I2S.setBufferSize(i2s_buffer_size + 1)){ + // unsupported buffer size requested -> halve the size + i2s_buffer_size /= 2; + } + delay(25); + } // infinite loop } void setup() { @@ -118,6 +149,35 @@ void setup() { log_e("Could not create callback task 3"); while (1) {;} // halt } + + _Task4Handle = NULL; + xTaskCreate( + task4, // Function to implement the task + "task4", // Name of the task + 2000, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_Task4Handle // Task handle. + ); + if(_Task4Handle == NULL){ + log_e("Could not create callback task 4"); + while (1) {;} // halt + } + + _Task5Handle = NULL; + xTaskCreate( + task5, // Function to implement the task + "task5", // Name of the task + 2000, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &_Task5Handle // Task handle. + ); + if(_Task5Handle == NULL){ + log_e("Could not create callback task 5"); + while (1) {;} // halt + } + } void loop() { From 23e65bd04df83daf554caf5acfd6b0e301212b26 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 8 Oct 2021 09:06:30 +0200 Subject: [PATCH 60/94] Mono channel support --- .../I2S/examples/SimpleTone/SimpleTone.ino | 19 +++++++--- libraries/I2S/src/I2S.cpp | 37 ++++++++++--------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index f11804a815c..a9ebaa6800a 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -38,9 +38,16 @@ const int halfWavelength = (sampleRate / frequency); // half wavelength of squar short sample = amplitude; // current sample value int count = 0; -i2s_mode_t mode = I2S_PHILIPS_MODE; -//i2s_mode_t mode = I2S_ADC_DAC; +i2s_mode_t mode = I2S_PHILIPS_MODE; // I2S decoder is needed +// i2s_mode_t mode = I2S_ADC_DAC; // Audio amplifier is needed +// Mono channel input +// This is ESP specific implementation - +// samples will be automatically copied to both channels inside I2S driver +// If you want to have true mono output use I2S_PHILIPS_MODE and interlay +// second channel with 0-value samples. +// The order of channels is RIGH followed by LEFT +//i2s_mode_t mode = I2S_RIGHT_JUSTIFIED_MODE; // I2S decoder is needed void setup() { Serial.begin(115200); @@ -60,9 +67,11 @@ void loop() { sample = -1 * sample; } - I2S.write(sample); - // write the same sample twice, once for left and once for the right channel - if(mode == I2S_PHILIPS_MODE){ + if(mode == I2S_PHILIPS_MODE){ // write the same sample twice, once for Righ and once for Left channel + I2S.write(sample); // Right channel + I2S.write(sample); // Left channel + }else if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE){ + // write the same only once - it will be automatically copied to the other channel I2S.write(sample); } diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 0430a78b10d..c1fdcd40683 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -164,7 +164,7 @@ int I2SClass::_installDriver(){ #endif }else if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || - _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal mode + _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal Philips mode if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ if(_bitsPerSample == 8){ log_e("ESP unfortunately does not support 8 bits per sample"); @@ -176,8 +176,7 @@ int I2SClass::_installDriver(){ if(_bitsPerSample == 24){ log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } - - }else if(_mode == I2S_PDM){ + }else if(_mode == I2S_PDM){ // end of Normal Philips mode; start of PDM mode #if SOC_I2S_SUPPORTS_PDM i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); #else @@ -197,12 +196,14 @@ int I2SClass::_installDriver(){ .use_apll = false }; // Install and start i2s driver + // TODO solve possible infinite loop while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // double buffer size - log_w("WARNING i2s driver install failed; Trying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); if(2*_i2s_dma_buffer_size <= 1024){ + log_w("WARNING i2s driver install failed; Trying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); setBufferSize(2*_i2s_dma_buffer_size); }else if(_i2s_dma_buffer_size < 1024){ + log_w("WARNING i2s driver install failed; Trying to decrease I2S DMA buffer size from %d to 1024\n", _i2s_dma_buffer_size); setBufferSize(1024); }else{ log_e("ERROR i2s driver install failed"); @@ -210,6 +211,10 @@ int I2SClass::_installDriver(){ } } //try installing with increasing size + if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // end of Normal Philips mode; start of mono/single channel + esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO); + } + #if SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_ADC_DAC){ esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); @@ -245,14 +250,13 @@ int I2SClass::_installDriver(){ _initialized = true; }else // End of ADC/DAC mode #endif // SOC_I2S_SUPPORTS_ADC_DAC - if(_mode == I2S_PHILIPS_MODE){ // if Normal mode - _initialized = true; + if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // if I2S mode + _initialized = true; // must be _initialized before calling _applyPinSetting if(!_applyPinSetting()){ end(); return 0; // ERR } - } // if _mode == ? - + } // if I2S _mode return 1; // OK } @@ -293,16 +297,15 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock // TODO implement left / right justified modes switch (mode) { case I2S_PHILIPS_MODE: + case I2S_RIGHT_JUSTIFIED_MODE: + case I2S_LEFT_JUSTIFIED_MODE: #if SOC_I2S_SUPPORTS_ADC_DAC case I2S_ADC_DAC: #endif case I2S_PDM: break; - case I2S_RIGHT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - case I2S_LEFT_JUSTIFIED_MODE: // normally this should work, but i don't how to set it up for ESP - default: - // invalid mode + default: // invalid mode log_e("ERROR I2SClass::begin() unknown mode"); _give_if_top_call(); return 0; // ERR @@ -704,6 +707,7 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ _give_if_top_call(); return size; }else{ + log_w("I2S could not write all data into ring buffer!"); _give_if_top_call(); return 0; } @@ -896,8 +900,8 @@ void I2SClass::_onTransferComplete(){ xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // defaul - xActivatedMember = xQueueSelectFromSet(xQueueSet, 5); // hack + //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // default + xActivatedMember = xQueueSelectFromSet(xQueueSet, 1); // hack // TODO try just queue receive at the timeout if(xActivatedMember == _task_kill_cmd_semaphore_handle){ xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); @@ -912,8 +916,7 @@ void I2SClass::_onTransferComplete(){ _rx_done_routine(); } // RX Done } // queue not empty - }else{ - } + } // activated member of queue set } // infinite loop _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } @@ -932,7 +935,7 @@ void I2SClass::_take_if_not_holding(){ // we are not holding the mutex - wait for it and take it if(xSemaphoreTake(_i2s_general_mutex, portMAX_DELAY) != pdTRUE ){ - log_e("I2S internal mutex take returned with error") + log_e("I2S internal mutex take returned with error"); } //_give_if_top_call(); // call after this function } From 79144922e00518e45cfb09a2598333e662633b6f Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 11 Oct 2021 10:17:20 +0200 Subject: [PATCH 61/94] FullDuplex changed + added example --- .../I2S/examples/FullDuplex/.skip.esp32c3 | 0 .../I2S/examples/FullDuplex/FullDuplex.ino | 57 +++++++++++++++++++ libraries/I2S/src/I2S.cpp | 53 +++-------------- libraries/I2S/src/I2S.h | 6 +- 4 files changed, 66 insertions(+), 50 deletions(-) create mode 100644 libraries/I2S/examples/FullDuplex/.skip.esp32c3 create mode 100644 libraries/I2S/examples/FullDuplex/FullDuplex.ino diff --git a/libraries/I2S/examples/FullDuplex/.skip.esp32c3 b/libraries/I2S/examples/FullDuplex/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/examples/FullDuplex/FullDuplex.ino b/libraries/I2S/examples/FullDuplex/FullDuplex.ino new file mode 100644 index 00000000000..e288e27b85a --- /dev/null +++ b/libraries/I2S/examples/FullDuplex/FullDuplex.ino @@ -0,0 +1,57 @@ +/* + This example demonstrates simultaneous usage of microphone and speaker using single I2S module. + The application transfers data from input to output + + Circuit: + * ESP32 + * GND connected GND + * VIN connected 5V + * SCK 5 + * FS 25 + * DIN 35 + * DOUT 26 + * I2S microphone + * I2S decoder + headphones / speaker + + created 8 October 2021 + by Tomas Pilny + */ + +#include +const long sampleRate = 16000; +const int bitsPerSample = 32; + +void setup() { + disableCore0WDT(); + Serial.begin(115200); + I2S.setAllPins(5, 25, 35, 26); + if(!I2S.setDuplex()){ + Serial.println("ERROR - could not set duplex"); + while(true){ + vTaskDelay(10); // Cannot continue + } + } + if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) { + Serial.println("Failed to initialize I2S!"); + while(true){ + vTaskDelay(10); // Cannot continue + } + } + uint8_t *buffer; + buffer = (uint8_t*) malloc(I2S.getBufferSize() * (bitsPerSample / 8)); + if(buffer == NULL){ + Serial.println("Failed to allocate buffer!"); + while(true){ + vTaskDelay(10); // Cannot continue + } + } + Serial.println("Setup done"); +} + +void loop() { + //I2S.write(I2S.read()); // primitive implementation sample-by-sample + + // Buffer based implementation + I2S.read(buffer, I2S.getBufferSize()*(bps/8)); + I2S.write(buffer, I2S.getBufferSize()*(bps/8)); +} diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c1fdcd40683..bb4ad0fba56 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -68,41 +68,6 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u } } -I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin) : // set duplex - _deviceIndex(deviceIndex), - _sdPin(inSdPin), // shared data pin - _inSdPin(inSdPin), // input data pin - _outSdPin(outSdPin), // output data pin - _sckPin(sckPin), // clock pin - _fsPin(fsPin), // frame (word) select pin - - _state(I2S_STATE_DUPLEX), - _bitsPerSample(0), - _sampleRate(0), - _mode(I2S_PHILIPS_MODE), - - _buffer_byte_size(0), - - _initialized(false), - _callbackTaskHandle(NULL), - _i2sEventQueue(NULL), - _task_kill_cmd_semaphore_handle(NULL), - _i2s_general_mutex(NULL), - _input_ring_buffer(NULL), - _output_ring_buffer(NULL), - _i2s_dma_buffer_size(1024), - _driveClock(true), - _nesting_counter(0), - - _onTransmit(NULL), - _onReceive(NULL) -{ - _i2s_general_mutex = xSemaphoreCreateMutex(); - if(_i2s_general_mutex == NULL){ - log_e("I2S could not create internal mutex!"); - } -} - int I2SClass::_createCallbackTask(){ int stack_size = 10000; if(_callbackTaskHandle == NULL){ @@ -196,16 +161,15 @@ int I2SClass::_installDriver(){ .use_apll = false }; // Install and start i2s driver - // TODO solve possible infinite loop while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ - // double buffer size + // increase buffer size if(2*_i2s_dma_buffer_size <= 1024){ log_w("WARNING i2s driver install failed; Trying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); setBufferSize(2*_i2s_dma_buffer_size); }else if(_i2s_dma_buffer_size < 1024){ log_w("WARNING i2s driver install failed; Trying to decrease I2S DMA buffer size from %d to 1024\n", _i2s_dma_buffer_size); setBufferSize(1024); - }else{ + }else{ // install failed with max buffer size log_e("ERROR i2s driver install failed"); return 0; // ERR } @@ -661,19 +625,18 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size){ } // blocking version of write -// TODO add timeout size_t I2SClass::write_blocking(const void *buffer, size_t size){ _take_if_not_holding(); if(_initialized){ - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX) { + if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ if(!_enableTransmitter()){ _give_if_top_call(); return 0; // There was an error switching to transmitter } // _enableTransmitter succeeded ? } // _state ? - // TODO add timeout - while(availableForWrite() < size){ - yield(); + uint8_t timeout = 10; // RTOS tics + while(availableForWrite() < size && timeout--){ + vTaskDelay(1); } if(_output_ring_buffer != NULL){ if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ @@ -900,9 +863,8 @@ void I2SClass::_onTransferComplete(){ xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // default + //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // default - member was never selected even when present xActivatedMember = xQueueSelectFromSet(xQueueSet, 1); // hack - // TODO try just queue receive at the timeout if(xActivatedMember == _task_kill_cmd_semaphore_handle){ xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); break; // from the infinite loop @@ -1103,7 +1065,6 @@ int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_ #if I2S_INTERFACES_COUNT > 0 I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SCK, PIN_I2S_FS); // default - half duplex - //I2SClass I2S(I2S_DEVICE, I2S_CLOCK_GENERATOR, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SCK, PIN_I2S_FS); // full duplex #endif #if I2S_INTERFACES_COUNT > 1 diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index c7d1ef11d9f..235b90a9c88 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -40,7 +40,7 @@ namespace esp_i2s { #endif #ifndef PIN_I2S_SD - #define PIN_I2S_SD 35 // Input if used in duplex + #define PIN_I2S_SD 35 // Pin 35 is only input! #endif #ifndef PIN_I2S_SD_OUT @@ -60,7 +60,6 @@ class I2SClass : public Stream public: // the device index and pins must map to the "COM" pads in Table 6-1 of the datasheet I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin); - I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t inSdPin, uint8_t outSdPin, uint8_t sckPin, uint8_t fsPin); // set duplex // the SCK and FS pins are driven as outputs using the sample rate int begin(int mode, int sampleRate, int bitsPerSample); // the SCK and FS pins are inputs, other side controls sample rate @@ -153,8 +152,7 @@ class I2SClass : public Stream i2s_state_t _state; int _bitsPerSample; - uint32_t _sampleRate; // - //int _sampleRate; + uint32_t _sampleRate; int _mode; uint16_t _buffer_byte_size; From f61e30df9b22d84cec32098b5ea2e63b9e1974cb Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 13 Oct 2021 08:38:03 +0200 Subject: [PATCH 62/94] Minor changes to FullDuplex example --- libraries/I2S/examples/FullDuplex/FullDuplex.ino | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/I2S/examples/FullDuplex/FullDuplex.ino b/libraries/I2S/examples/FullDuplex/FullDuplex.ino index e288e27b85a..9b3625fbd60 100644 --- a/libraries/I2S/examples/FullDuplex/FullDuplex.ino +++ b/libraries/I2S/examples/FullDuplex/FullDuplex.ino @@ -1,4 +1,5 @@ /* + This example is only for ESP This example demonstrates simultaneous usage of microphone and speaker using single I2S module. The application transfers data from input to output @@ -20,11 +21,11 @@ #include const long sampleRate = 16000; const int bitsPerSample = 32; +uint8_t *buffer; void setup() { - disableCore0WDT(); Serial.begin(115200); - I2S.setAllPins(5, 25, 35, 26); + //I2S.setAllPins(5, 25, 35, 26); // you can change default pins; order of pins = (CLK, WS, IN, OUT) if(!I2S.setDuplex()){ Serial.println("ERROR - could not set duplex"); while(true){ @@ -37,7 +38,6 @@ void setup() { vTaskDelay(10); // Cannot continue } } - uint8_t *buffer; buffer = (uint8_t*) malloc(I2S.getBufferSize() * (bitsPerSample / 8)); if(buffer == NULL){ Serial.println("Failed to allocate buffer!"); @@ -52,6 +52,8 @@ void loop() { //I2S.write(I2S.read()); // primitive implementation sample-by-sample // Buffer based implementation - I2S.read(buffer, I2S.getBufferSize()*(bps/8)); - I2S.write(buffer, I2S.getBufferSize()*(bps/8)); + I2S.read(buffer, I2S.getBufferSize() * (bitsPerSample / 8)); + I2S.write(buffer, I2S.getBufferSize() * (bitsPerSample / 8)); + + //optimistic_yield(1000); // yield if last yield occurred before CPU clock cycles ago } From ba155a53437fa6b81795f2ea3b5f02009a8cfe5c Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 13 Oct 2021 09:22:16 +0200 Subject: [PATCH 63/94] Added support for 8 bps --- libraries/I2S/src/I2S.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index bb4ad0fba56..d839e8b36d0 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -119,7 +119,7 @@ int I2SClass::_installDriver(){ if(_mode == I2S_ADC_DAC){ #if SOC_I2S_SUPPORTS_ADC_DAC if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - log_e("ERROR I2SClass::begin invalid bps for ADC/DAC"); + log_e("ERROR I2SClass::begin invalid bps for ADC/DAC. Allowed only 16, requested %d", _bitsPerSample); return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); @@ -130,12 +130,8 @@ int I2SClass::_installDriver(){ }else if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // End of ADC/DAC mode; start of Normal Philips mode - if(_bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ - if(_bitsPerSample == 8){ - log_e("ESP unfortunately does not support 8 bits per sample"); - }else{ - log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 16 | 24 | 32", _bitsPerSample); - } + if(_bitsPerSample != 8 && _bitsPerSample != 16 && _bitsPerSample != 24 && _bitsPerSample != 32){ + log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 8 | 16 | 24 | 32", _bitsPerSample); return 0; // ERR } if(_bitsPerSample == 24){ From 4bc26338d4bc756f7db222894a5026a67ebdea2a Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 13 Oct 2021 09:38:12 +0200 Subject: [PATCH 64/94] Moved driver install before ringbuffer init --- libraries/I2S/src/I2S.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index d839e8b36d0..594ab473461 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -271,6 +271,13 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock return 0; // ERR } + if(!_installDriver()){ + _initialized = false; + end(); + _give_if_top_call(); + return 0; // ERR + } + _buffer_byte_size = _i2s_dma_buffer_size * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); @@ -280,13 +287,6 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock return 0; // ERR } - if(!_installDriver()){ - _initialized = false; - end(); - _give_if_top_call(); - return 0; // ERR - } - if(!_createCallbackTask()){ _initialized = false; end(); From ea037b10933d51faa2eb05270f7cd1e7533f92ad Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 13 Oct 2021 19:24:06 +0200 Subject: [PATCH 65/94] BugFix - return was called before mutex release in read --- libraries/I2S/src/I2S.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 594ab473461..f6c50c9c486 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -579,8 +579,8 @@ int I2SClass::read(void* buffer, size_t size){ } // tmp buffer not NULL ? } // ring buffer not NULL ? } // if(_initialized) - return 0; _give_if_top_call(); + return 0; } /* From 0b6640265cf2cc9c7bbcf65dccea352e3efa4bb0 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 14 Oct 2021 12:44:33 +0200 Subject: [PATCH 66/94] Changed #if constants for PDM and ADC/DAC --- libraries/I2S/src/I2S.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index f6c50c9c486..bdaa3c2610a 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -117,7 +117,7 @@ int I2SClass::_installDriver(){ } if(_mode == I2S_ADC_DAC){ - #if SOC_I2S_SUPPORTS_ADC_DAC + #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode log_e("ERROR I2SClass::begin invalid bps for ADC/DAC. Allowed only 16, requested %d", _bitsPerSample); return 0; // ERR @@ -138,7 +138,7 @@ int I2SClass::_installDriver(){ log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } }else if(_mode == I2S_PDM){ // end of Normal Philips mode; start of PDM mode - #if SOC_I2S_SUPPORTS_PDM + #if (SOC_I2S_SUPPORTS_PDM_TX && SOC_I2S_SUPPORTS_PDM_RX) i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); #else log_e("This chip does not support PDM"); @@ -175,7 +175,7 @@ int I2SClass::_installDriver(){ esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO); } -#if SOC_I2S_SUPPORTS_ADC_DAC +#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) if(_mode == I2S_ADC_DAC){ esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::adc_unit_t adc_unit; @@ -259,7 +259,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock case I2S_PHILIPS_MODE: case I2S_RIGHT_JUSTIFIED_MODE: case I2S_LEFT_JUSTIFIED_MODE: -#if SOC_I2S_SUPPORTS_ADC_DAC +#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) case I2S_ADC_DAC: #endif case I2S_PDM: @@ -470,7 +470,7 @@ int I2SClass::getDataOutPin(){ } void I2SClass::_uninstallDriver(){ -#if SOC_I2S_SUPPORTS_ADC_DAC +#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) if(_mode == I2S_ADC_DAC){ esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); } @@ -563,7 +563,7 @@ int I2SClass::read(void* buffer, size_t size){ tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); if(tmp_buffer != NULL){ memcpy(buffer, tmp_buffer, item_size); - #if SOC_I2S_SUPPORTS_ADC_DAC + #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) if(_mode == I2S_ADC_DAC){ for(size_t i = 0; i < item_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; @@ -908,7 +908,7 @@ void I2SClass::_give_if_top_call(){ } } -#if SOC_I2S_SUPPORTS_ADC_DAC +#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ switch(gpio_num){ #if CONFIG_IDF_TARGET_ESP32 From 660656fa505a80769692b8c0475a5df0bf8b265b Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 14 Oct 2021 15:55:22 +0200 Subject: [PATCH 67/94] BugFix: forgotten init flag rise for PDM mode during init + #if const change in I2S.h --- libraries/I2S/src/I2S.cpp | 4 +++- libraries/I2S/src/I2S.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index bdaa3c2610a..22ffcb37e54 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -210,9 +210,10 @@ int I2SClass::_installDriver(){ _initialized = true; }else // End of ADC/DAC mode #endif // SOC_I2S_SUPPORTS_ADC_DAC - if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // if I2S mode + if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == I2S_PDM){ // if I2S mode _initialized = true; // must be _initialized before calling _applyPinSetting if(!_applyPinSetting()){ + log_e("could not apply pin setting during driver install"); end(); return 0; // ERR } @@ -272,6 +273,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock } if(!_installDriver()){ + log_e("ERROR I2SClass::begin() failed to install driver"); _initialized = false; end(); _give_if_top_call(); diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 235b90a9c88..15d0c32b86b 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -112,7 +112,7 @@ class I2SClass : public Stream int setBufferSize(int bufferSize); int getBufferSize(); - #if SOC_I2S_SUPPORTS_ADC_DAC + #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) int gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit); int gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel); #endif From 9da59424d89f501affdf91f8ebcde915f7bf1d22 Mon Sep 17 00:00:00 2001 From: "pedro.minatel" Date: Thu, 21 Oct 2021 19:19:58 +0100 Subject: [PATCH 68/94] bugfix: Changes on the i2s_set_clk and DMA size --- libraries/I2S/src/I2S.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 22ffcb37e54..57bdf8d2aaf 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,7 +22,7 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 +#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -55,7 +55,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), - _i2s_dma_buffer_size(1024), + _i2s_dma_buffer_size(200), _driveClock(true), _nesting_counter(0), @@ -149,7 +149,7 @@ int I2SClass::_installDriver(){ .mode = i2s_mode, .sample_rate = _sampleRate, .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, - .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, // MONO only LEFT .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, @@ -171,8 +171,9 @@ int I2SClass::_installDriver(){ } } //try installing with increasing size - if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE){ // end of Normal Philips mode; start of mono/single channel - esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO); + // Set the clock for MONO. Stereo is not supported yet. + if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ + log_e("Setting the I2S Clock has failed!\n"); } #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) From ef1a825414733f035986f33fb66d9db600fc11d0 Mon Sep 17 00:00:00 2001 From: "pedro.minatel" Date: Fri, 22 Oct 2021 16:20:15 +0100 Subject: [PATCH 69/94] bugfix: Reverted the DMA buffer and fix on the i2s_set_clk for Phillips and PDM mode only --- libraries/I2S/src/I2S.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 57bdf8d2aaf..befdf72b52d 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,7 +22,7 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_COUNT 8 // BUFFER COUNT must be between 2 and 128 +#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -55,7 +55,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), - _i2s_dma_buffer_size(200), + _i2s_dma_buffer_size(1024), _driveClock(true), _nesting_counter(0), @@ -149,7 +149,7 @@ int I2SClass::_installDriver(){ .mode = i2s_mode, .sample_rate = _sampleRate, .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, - .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, // MONO only LEFT + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, @@ -171,9 +171,10 @@ int I2SClass::_installDriver(){ } } //try installing with increasing size - // Set the clock for MONO. Stereo is not supported yet. - if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ - log_e("Setting the I2S Clock has failed!\n"); + if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == I2S_PDM){ + if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ + log_e("Setting the I2S Clock has failed!\n"); + } } #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) From a56be4140d64dd09210118fd404f70905a05b00e Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 25 Oct 2021 08:10:43 +0200 Subject: [PATCH 70/94] Split PDM modes to mono and stereo + renamed ADC mode --- .../I2S/examples/ADCPlotter/ADCPlotter.ino | 2 +- .../I2S/examples/SimpleTone/SimpleTone.ino | 4 +-- libraries/I2S/src/I2S.cpp | 25 ++++++++++--------- libraries/I2S/src/I2S.h | 7 +++--- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino index 1c68be33b40..5f3bd93ca9d 100644 --- a/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino +++ b/libraries/I2S/examples/ADCPlotter/ADCPlotter.ino @@ -73,7 +73,7 @@ void setup() { } // start I2S at 8 kHz with 32-bits per sample - if (!I2S.begin(I2S_ADC_DAC, 8000, 16)) { + if (!I2S.begin(ADC_DAC_MODE, 8000, 16)) { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index a9ebaa6800a..5c483a2e608 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -39,7 +39,7 @@ short sample = amplitude; // current sample value int count = 0; i2s_mode_t mode = I2S_PHILIPS_MODE; // I2S decoder is needed -// i2s_mode_t mode = I2S_ADC_DAC; // Audio amplifier is needed +// i2s_mode_t mode = ADC_DAC_MODE; // Audio amplifier is needed // Mono channel input // This is ESP specific implementation - @@ -67,7 +67,7 @@ void loop() { sample = -1 * sample; } - if(mode == I2S_PHILIPS_MODE){ // write the same sample twice, once for Righ and once for Left channel + if(mode == I2S_PHILIPS_MODE){ // write the same sample twice, once for Right and once for Left channel I2S.write(sample); // Right channel I2S.write(sample); // Left channel }else if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE){ diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index befdf72b52d..2bf08dffa21 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -116,7 +116,7 @@ int I2SClass::_installDriver(){ i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } - if(_mode == I2S_ADC_DAC){ + if(_mode == ADC_DAC_MODE){ #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode log_e("ERROR I2SClass::begin invalid bps for ADC/DAC. Allowed only 16, requested %d", _bitsPerSample); @@ -137,7 +137,7 @@ int I2SClass::_installDriver(){ if(_bitsPerSample == 24){ log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); } - }else if(_mode == I2S_PDM){ // end of Normal Philips mode; start of PDM mode + }else if(_mode == PDM_STEREO_MODE || _mode == PDM_MONO_MODE){ // end of Normal Philips mode; start of PDM mode #if (SOC_I2S_SUPPORTS_PDM_TX && SOC_I2S_SUPPORTS_PDM_RX) i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); #else @@ -171,14 +171,16 @@ int I2SClass::_installDriver(){ } } //try installing with increasing size - if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == I2S_PDM){ + if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == PDM_MONO_MODE){ // mono/single channel + // Set the clock for MONO. Stereo is not supported yet. if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ log_e("Setting the I2S Clock has failed!\n"); + return 0; // ERR } - } + } // mono channel mode #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - if(_mode == I2S_ADC_DAC){ + if(_mode == ADC_DAC_MODE){ esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::adc_unit_t adc_unit; if(!gpioToAdcUnit((gpio_num_t)_inSdPin, &adc_unit)){ @@ -192,12 +194,10 @@ int I2SClass::_installDriver(){ } if(ESP_OK != esp_i2s::i2s_set_adc_mode(adc_unit, (esp_i2s::adc1_channel_t)adc_channel)){ log_e("i2s_set_adc_mode failed"); - end(); return 0; // ERR } if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){ log_e("i2s_set_pin failed"); - end(); return 0; // ERR } @@ -212,7 +212,7 @@ int I2SClass::_installDriver(){ _initialized = true; }else // End of ADC/DAC mode #endif // SOC_I2S_SUPPORTS_ADC_DAC - if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == I2S_PDM){ // if I2S mode + if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == PDM_STEREO_MODE || _mode == PDM_MONO_MODE){ // if I2S mode _initialized = true; // must be _initialized before calling _applyPinSetting if(!_applyPinSetting()){ log_e("could not apply pin setting during driver install"); @@ -263,9 +263,10 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock case I2S_RIGHT_JUSTIFIED_MODE: case I2S_LEFT_JUSTIFIED_MODE: #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - case I2S_ADC_DAC: + case ADC_DAC_MODE: #endif - case I2S_PDM: + case PDM_STEREO_MODE: + case PDM_MONO_MODE: break; default: // invalid mode @@ -475,7 +476,7 @@ int I2SClass::getDataOutPin(){ void I2SClass::_uninstallDriver(){ #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - if(_mode == I2S_ADC_DAC){ + if(_mode == ADC_DAC_MODE){ esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); } #endif @@ -568,7 +569,7 @@ int I2SClass::read(void* buffer, size_t size){ if(tmp_buffer != NULL){ memcpy(buffer, tmp_buffer, item_size); #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - if(_mode == I2S_ADC_DAC){ + if(_mode == ADC_DAC_MODE){ for(size_t i = 0; i < item_size / 2; ++i){ ((uint16_t*)buffer)[i] = ((uint16_t*)buffer)[i] & 0x0FFF; } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 15d0c32b86b..9a545aef715 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -28,7 +28,7 @@ namespace esp_i2s { // Default pins #ifndef PIN_I2S_SCK - #define PIN_I2S_SCK 5 + #define PIN_I2S_SCK 14 #endif #ifndef PIN_I2S_FS @@ -51,8 +51,9 @@ typedef enum { I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, I2S_LEFT_JUSTIFIED_MODE, - I2S_ADC_DAC, - I2S_PDM + ADC_DAC_MODE, + PDM_STEREO_MODE, + PDM_MONO_MODE } i2s_mode_t; class I2SClass : public Stream From 3f9458ce8bc3835d470ec20c691b5d5c11e8676d Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 25 Oct 2021 13:52:15 +0200 Subject: [PATCH 71/94] Modified internal initialization flags and checks --- .../I2S/examples/SimpleTone/SimpleTone.ino | 5 +- libraries/I2S/src/I2S.cpp | 50 +++++++++++-------- libraries/I2S/src/I2S.h | 3 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 5c483a2e608..dd312cf8a82 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -22,7 +22,7 @@ created 17 November 2016 by Sandeep Mistry - For ESP extended by + For ESP extended Tomas Pilny 2nd September 2021 */ @@ -58,7 +58,6 @@ void setup() { Serial.println("Failed to initialize I2S!"); while (1); // do nothing } - I2S.setDataOutPin(26); } void loop() { @@ -67,7 +66,7 @@ void loop() { sample = -1 * sample; } - if(mode == I2S_PHILIPS_MODE){ // write the same sample twice, once for Right and once for Left channel + if(mode == I2S_PHILIPS_MODE || mode == ADC_DAC_MODE){ // write the same sample twice, once for Right and once for Left channel I2S.write(sample); // Right channel I2S.write(sample); // Left channel }else if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE){ diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 2bf08dffa21..0d6a5d244ef 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -48,6 +48,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _buffer_byte_size(0), + _driverInstalled(false), _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), @@ -107,6 +108,11 @@ void I2SClass::_destroyCallbackTask(){ } int I2SClass::_installDriver(){ + if(_driverInstalled){ + log_e("I2S driver is already installed"); + return 0; // ERR + } + esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX); if(_driveClock){ @@ -209,14 +215,14 @@ int I2SClass::_installDriver(){ } esp_i2s::i2s_adc_enable((esp_i2s::i2s_port_t) _deviceIndex); - _initialized = true; + _driverInstalled = true; }else // End of ADC/DAC mode #endif // SOC_I2S_SUPPORTS_ADC_DAC if(_mode == I2S_PHILIPS_MODE || _mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == PDM_STEREO_MODE || _mode == PDM_MONO_MODE){ // if I2S mode - _initialized = true; // must be _initialized before calling _applyPinSetting + _driverInstalled = true; // IDF I2S driver must be installed before calling _applyPinSetting if(!_applyPinSetting()){ log_e("could not apply pin setting during driver install"); - end(); + _uninstallDriver(); return 0; // ERR } } // if I2S _mode @@ -244,7 +250,9 @@ int I2SClass::begin(int mode, int bitsPerSample){ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){ _take_if_not_holding(); if(_initialized){ - end(); + log_e("ERROR I2SClass::begin() object already initialized! Call I2S.end() to deinitialize"); + _give_if_top_call(); + return 0; // ERR } _driveClock = driveClock; _mode = mode; @@ -277,7 +285,6 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock if(!_installDriver()){ log_e("ERROR I2SClass::begin() failed to install driver"); - _initialized = false; end(); _give_if_top_call(); return 0; // ERR @@ -293,17 +300,17 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock } if(!_createCallbackTask()){ - _initialized = false; end(); _give_if_top_call(); return 0; // ERR } + _initialized = true; _give_if_top_call(); return 1; // OK } int I2SClass::_applyPinSetting(){ - if(_initialized){ + if(_driverInstalled){ esp_i2s::i2s_pin_config_t pin_config = { .bck_io_num = _sckPin, .ws_io_num = _fsPin, @@ -331,7 +338,7 @@ int I2SClass::_applyPinSetting(){ }else{ return 1; // OK } - } // Is driver _initialized ? + } // _driverInstalled ? return 1; // OK } @@ -475,26 +482,26 @@ int I2SClass::getDataOutPin(){ } void I2SClass::_uninstallDriver(){ -#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - if(_mode == ADC_DAC_MODE){ - esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); - } -#endif - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); + if(_driverInstalled){ + #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) + if(_mode == ADC_DAC_MODE){ + esp_i2s::i2s_adc_disable((esp_i2s::i2s_port_t) _deviceIndex); + } + #endif + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) _deviceIndex); - if(_state != I2S_STATE_DUPLEX){ - _state = I2S_STATE_IDLE; - } + if(_state != I2S_STATE_DUPLEX){ + _state = I2S_STATE_IDLE; + } + _driverInstalled = false; + } // if(_driverInstalled) } void I2SClass::end(){ _take_if_not_holding(); if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ _destroyCallbackTask(); - if(_initialized){ - _uninstallDriver(); - _initialized = false; - } + _uninstallDriver(); _onTransmit = NULL; _onReceive = NULL; if(_input_ring_buffer != NULL){ @@ -505,6 +512,7 @@ void I2SClass::end(){ vRingbufferDelete(_output_ring_buffer); _output_ring_buffer = NULL; } + _initialized = false; }else{ log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!"); } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 9a545aef715..e8a9996118f 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -158,7 +158,8 @@ class I2SClass : public Stream uint16_t _buffer_byte_size; - bool _initialized; + bool _driverInstalled; // Is IDF I2S driver installed? + bool _initialized; // Is everything initialized (callback task, I2S driver, ring buffers)? TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; QueueHandle_t _task_kill_cmd_semaphore_handle; From 0e9d34e84b2a6c32fa24244da791724bf885b3c9 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 19 Nov 2021 09:52:26 +0100 Subject: [PATCH 72/94] Single-sample writes are now blocking (same as Arduino) --- libraries/I2S/src/I2S.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 0d6a5d244ef..c1599fcf645 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -607,7 +607,7 @@ size_t I2SClass::write(uint8_t data){ _take_if_not_holding(); size_t ret = 0; if(_initialized){ - ret = write((int32_t)data); + ret = write_blocking((int32_t)data); } _give_if_top_call(); return ret; @@ -617,7 +617,7 @@ size_t I2SClass::write(int32_t sample){ _take_if_not_holding(); size_t ret = 0; if(_initialized){ - ret = write(&sample, _bitsPerSample/8); + ret = write_blocking(&sample, _bitsPerSample/8); } _give_if_top_call(); return ret; @@ -633,6 +633,17 @@ size_t I2SClass::write(const uint8_t *buffer, size_t size){ return ret; } +size_t I2SClass::write(const void *buffer, size_t size){ + _take_if_not_holding(); + size_t ret = 0; + if(_initialized){ + //size_t ret = write_blocking(buffer, size); + ret = write_nonblocking(buffer, size); + } // if(_initialized) + _give_if_top_call(); + return ret; +} + // blocking version of write size_t I2SClass::write_blocking(const void *buffer, size_t size){ _take_if_not_holding(); @@ -689,17 +700,6 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ _give_if_top_call(); // this should not be needed } -size_t I2SClass::write(const void *buffer, size_t size){ - _take_if_not_holding(); - size_t ret = 0; - if(_initialized){ - //size_t ret = write_blocking(buffer, size); - ret = write_nonblocking(buffer, size); - } // if(_initialized) - _give_if_top_call(); - return ret; -} - int I2SClass::peek(){ _take_if_not_holding(); int ret = 0; From 0e478b9bc251d1d0b67cf31ed248530c3efde4b1 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 19 Nov 2021 13:40:47 +0100 Subject: [PATCH 73/94] removed kill sempahore for callback task deletion --- libraries/I2S/src/I2S.cpp | 97 +++++++++++++-------------------------- libraries/I2S/src/I2S.h | 2 - 2 files changed, 33 insertions(+), 66 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c1599fcf645..198ffb822fb 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -36,8 +36,12 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), _sdPin(sdPin), // shared data pin - _inSdPin(-1), // input data pin + _inSdPin(sdPin), // input data pin +#ifdef PIN_I2S_SD_OUT + _outSdPin(PIN_I2S_SD_OUT), // output data pin +#else _outSdPin(-1), // output data pin +#endif _sckPin(sckPin), // clock pin _fsPin(fsPin), // frame (word) select pin @@ -52,7 +56,6 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _initialized(false), _callbackTaskHandle(NULL), _i2sEventQueue(NULL), - _task_kill_cmd_semaphore_handle(NULL), _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), @@ -70,43 +73,27 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u } int I2SClass::_createCallbackTask(){ - int stack_size = 10000; - if(_callbackTaskHandle == NULL){ - if(_task_kill_cmd_semaphore_handle == NULL){ - _task_kill_cmd_semaphore_handle = xSemaphoreCreateBinary(); - if(_task_kill_cmd_semaphore_handle == NULL){ - log_e("Could not create semaphore"); - return 0; // ERR - } - } + int stack_size = 20000; + if(_callbackTaskHandle != NULL){ + log_e("Callback task already exists!"); + return 0; // ERR + } - xTaskCreate( - onDmaTransferComplete, // Function to implement the task - "onDmaTransferComplete", // Name of the task - stack_size, // Stack size in words - NULL, // Task input parameter - 2, // Priority of the task - &_callbackTaskHandle // Task handle. - ); - if(_callbackTaskHandle == NULL){ - log_e("Could not create callback task"); - return 0; // ERR - } + xTaskCreate( + onDmaTransferComplete, // Function to implement the task + "onDmaTransferComplete", // Name of the task + stack_size, // Stack size in words + NULL, // Task input parameter + 2, // Priority of the task + &_callbackTaskHandle // Task handle. + ); + if(_callbackTaskHandle == NULL){ + log_e("Could not create callback task"); + return 0; // ERR } return 1; // OK } -void I2SClass::_destroyCallbackTask(){ - if(_callbackTaskHandle != NULL && xTaskGetCurrentTaskHandle() != _callbackTaskHandle && _task_kill_cmd_semaphore_handle != NULL){ - xSemaphoreGive(_task_kill_cmd_semaphore_handle); - while(_callbackTaskHandle != NULL){ - ; // wait until task ends itself properly - } - vSemaphoreDelete(_task_kill_cmd_semaphore_handle); // delete semaphore after usage - _task_kill_cmd_semaphore_handle = NULL; // prevent usage of uninitialized (deleted) semaphore - } // callback handle check -} - int I2SClass::_installDriver(){ if(_driverInstalled){ log_e("I2S driver is already installed"); @@ -500,7 +487,8 @@ void I2SClass::_uninstallDriver(){ void I2SClass::end(){ _take_if_not_holding(); if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ - _destroyCallbackTask(); + vTaskDelete(_callbackTaskHandle); + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task _uninstallDriver(); _onTransmit = NULL; _onReceive = NULL; @@ -607,7 +595,7 @@ size_t I2SClass::write(uint8_t data){ _take_if_not_holding(); size_t ret = 0; if(_initialized){ - ret = write_blocking((int32_t)data); + ret = write_blocking((int32_t*)&data, 1); } _give_if_top_call(); return ret; @@ -654,15 +642,13 @@ size_t I2SClass::write_blocking(const void *buffer, size_t size){ return 0; // There was an error switching to transmitter } // _enableTransmitter succeeded ? } // _state ? - uint8_t timeout = 10; // RTOS tics - while(availableForWrite() < size && timeout--){ - vTaskDelay(1); - } + if(_output_ring_buffer != NULL){ - if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, portMAX_DELAY)){ _give_if_top_call(); return size; }else{ + log_e("xRingbufferSend() with infinite wait returned with error"); _give_if_top_call(); return 0; } // ring buffer send ok ? @@ -865,36 +851,19 @@ void I2SClass::_onTransferComplete(){ static QueueSetHandle_t xQueueSet; QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_t i2s_event; - xQueueSet = xQueueCreateSet(sizeof(i2s_event)*_I2S_EVENT_QUEUE_LENGTH + 1); - configASSERT(xQueueSet); - configASSERT(_i2sEventQueue); - xQueueAddToSet(_i2sEventQueue, xQueueSet); - xQueueAddToSet(_task_kill_cmd_semaphore_handle, xQueueSet); while(true){ - //xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY); // default - member was never selected even when present - xActivatedMember = xQueueSelectFromSet(xQueueSet, 1); // hack - if(xActivatedMember == _task_kill_cmd_semaphore_handle){ - xSemaphoreTake(_task_kill_cmd_semaphore_handle, 0); - break; // from the infinite loop - //}else if(xActivatedMember == _i2sEventQueue){ // default - }else if(xActivatedMember == _i2sEventQueue || xActivatedMember == NULL){ // hack - if(uxQueueMessagesWaiting(_i2sEventQueue)){ - xQueueReceive(_i2sEventQueue, &i2s_event, 0); - if(i2s_event.type == esp_i2s::I2S_EVENT_TX_DONE){ - _tx_done_routine(prev_item); - }else if(i2s_event.type == esp_i2s::I2S_EVENT_RX_DONE){ - _rx_done_routine(); - } // RX Done - } // queue not empty - } // activated member of queue set + xQueueReceive(_i2sEventQueue, &i2s_event, portMAX_DELAY); + if(i2s_event.type == esp_i2s::I2S_EVENT_TX_DONE){ + _tx_done_routine(prev_item); + }else if(i2s_event.type == esp_i2s::I2S_EVENT_RX_DONE){ + _rx_done_routine(); + } // RX Done } // infinite loop - _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task } void I2SClass::onDmaTransferComplete(void*){ I2S._onTransferComplete(); - vTaskDelete(NULL); } void I2SClass::_take_if_not_holding(){ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index e8a9996118f..0d8be3e105c 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -124,7 +124,6 @@ class I2SClass : public Stream int _enableReceiver(); void _onTransferComplete(); - void _destroyCallbackTask(); int _createCallbackTask(); static void onDmaTransferComplete(void*); @@ -162,7 +161,6 @@ class I2SClass : public Stream bool _initialized; // Is everything initialized (callback task, I2S driver, ring buffers)? TaskHandle_t _callbackTaskHandle; QueueHandle_t _i2sEventQueue; - QueueHandle_t _task_kill_cmd_semaphore_handle; SemaphoreHandle_t _i2s_general_mutex; RingbufHandle_t _input_ring_buffer; RingbufHandle_t _output_ring_buffer; From 4cc4e264396bdb460fa14f33151b2d119932b08b Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 19 Nov 2021 13:48:00 +0100 Subject: [PATCH 74/94] added few more checks --- libraries/I2S/src/I2S.cpp | 54 ++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 198ffb822fb..498afba04f0 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -746,19 +746,23 @@ void I2SClass::onReceive(void(*function)(void)){ int I2SClass::setBufferSize(int bufferSize){ _take_if_not_holding(); int ret = 0; + if(bufferSize >= 8 && bufferSize <= 1024){ + Serial.println("I2SClass::setBufferSize() buffer size ok"); + _i2s_dma_buffer_size = bufferSize; + }else{ + log_e("setBufferSize: wrong input! Buffer size must be between 8 and 1024. Requested %d\n ", bufferSize); + _give_if_top_call(); + return 0; // ERR + } // check requested buffer size + if(_initialized){ - if(bufferSize >= 8 && bufferSize <= 1024){ - _i2s_dma_buffer_size = bufferSize; - _uninstallDriver(); - ret = _installDriver(); - _give_if_top_call(); - return ret; + _uninstallDriver(); + ret = _installDriver(); _give_if_top_call(); - return 1; // OK - }else{ // check requested buffer size - _give_if_top_call(); - return 0; // ERR - } // check requested buffer size + return ret; + }else{ // check requested buffer size + _give_if_top_call(); + return 1; // It's ok to change buffer size for uninitialized driver - new size will be used on begin() } // if(_initialized) _give_if_top_call(); return 0; // ERR @@ -788,6 +792,7 @@ int I2SClass::_enableReceiver(){ } void I2SClass::_tx_done_routine(uint8_t* prev_item){ + _take_if_not_holding(); static bool prev_item_valid = false; const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); static size_t item_size = 0; @@ -825,25 +830,32 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ if(_onTransmit){ _onTransmit(); } // user callback + _give_if_top_call(); } void I2SClass::_rx_done_routine(){ - static size_t bytes_read; + _take_if_not_holding(); + size_t bytes_read = 0; const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); if(_input_ring_buffer != NULL){ uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4); size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); - if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ - log_w("I2S failed to send item from DMA to internal buffer\n"); - }else{ - if (_onReceive) { - _onReceive(); - } // user callback - } // xRingbufferSendComplete + if(avail > 0){ + esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + } + + if(bytes_read > 0){ // when read more than 0, then send to ring buffer + if(pdTRUE != xRingbufferSend(_input_ring_buffer, _inputBuffer, bytes_read, 0)){ + log_w("I2S failed to send item from DMA to internal buffer\n"); + } // xRingbufferSendComplete + } // if(bytes_read > 0) free(_inputBuffer); + if (_onReceive && avail < _buffer_byte_size){ // when user callback is registered && and there is some data in ring buffer to read + _onReceive(); + } // user callback } + _give_if_top_call(); } void I2SClass::_onTransferComplete(){ @@ -885,7 +897,7 @@ void I2SClass::_give_if_top_call(){ --_nesting_counter; }else{ if(xSemaphoreGive(_i2s_general_mutex) != pdTRUE){ - log_e("I2S intternal mutex give error"); + log_e("I2S internal mutex give error"); } } } From 3dfa4902169c5ddd35e8e5ae38450cdeec89e50c Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 22 Nov 2021 12:56:18 +0100 Subject: [PATCH 75/94] Removed Callback locks --- libraries/I2S/src/I2S.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 498afba04f0..c50bb9c1d4c 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -747,7 +747,6 @@ int I2SClass::setBufferSize(int bufferSize){ _take_if_not_holding(); int ret = 0; if(bufferSize >= 8 && bufferSize <= 1024){ - Serial.println("I2SClass::setBufferSize() buffer size ok"); _i2s_dma_buffer_size = bufferSize; }else{ log_e("setBufferSize: wrong input! Buffer size must be between 8 and 1024. Requested %d\n ", bufferSize); @@ -792,7 +791,6 @@ int I2SClass::_enableReceiver(){ } void I2SClass::_tx_done_routine(uint8_t* prev_item){ - _take_if_not_holding(); static bool prev_item_valid = false; const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); static size_t item_size = 0; @@ -830,11 +828,9 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ if(_onTransmit){ _onTransmit(); } // user callback - _give_if_top_call(); } void I2SClass::_rx_done_routine(){ - _take_if_not_holding(); size_t bytes_read = 0; const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); @@ -855,7 +851,6 @@ void I2SClass::_rx_done_routine(){ _onReceive(); } // user callback } - _give_if_top_call(); } void I2SClass::_onTransferComplete(){ From 35ec3e40a0951a692eb4e83571c2b8b485411876 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 22 Nov 2021 14:01:14 +0100 Subject: [PATCH 76/94] Implemented peek --- libraries/I2S/src/I2S.cpp | 36 +++++++++++++++++++++++++++++------- libraries/I2S/src/I2S.h | 2 ++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index c50bb9c1d4c..93c27a1902f 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -61,6 +61,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _output_ring_buffer(NULL), _i2s_dma_buffer_size(1024), _driveClock(true), + _peek_buff(0), + _peek_buff_valid(false), _nesting_counter(0), _onTransmit(NULL), @@ -550,6 +552,7 @@ int I2SClass::read(){ int I2SClass::read(void* buffer, size_t size){ _take_if_not_holding(); + size_t requested_size = size; if(_initialized){ if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { if(!_enableReceiver()){ @@ -561,7 +564,12 @@ int I2SClass::read(void* buffer, size_t size){ size_t item_size = 0; void *tmp_buffer; if(_input_ring_buffer != NULL){ - tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), size); + if(_peek_buff_valid){ + memcpy(buffer, &_peek_buff, _bitsPerSample/8); + _peek_buff_valid = false; + requested_size -= _bitsPerSample/8; + } + tmp_buffer = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, pdMS_TO_TICKS(1000), requested_size); if(tmp_buffer != NULL){ memcpy(buffer, tmp_buffer, item_size); #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) @@ -672,7 +680,7 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ flush(); } if(_output_ring_buffer != NULL){ - if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 10)){ + if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 0)){ _give_if_top_call(); return size; }else{ @@ -686,14 +694,28 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ _give_if_top_call(); // this should not be needed } +/* + Read 1 sample from internal buffer and return it. + Repeated peeks will return the same sample until read is called. +*/ int I2SClass::peek(){ _take_if_not_holding(); int ret = 0; - if(_initialized){ - // TODO - // peek() is not implemented for ESP yet - ret = 0; + if(_initialized && _input_ring_buffer != NULL && !_peek_buff_valid){ + size_t item_size = 0; + void *item = NULL; + + item = xRingbufferReceiveUpTo(_input_ring_buffer, &item_size, 0, _bitsPerSample/8); // fetch 1 sample + if (item != NULL && item_size == _bitsPerSample/8){ + _peek_buff = *((int*)item); + vRingbufferReturnItem(_input_ring_buffer, item); + _peek_buff_valid = true; + } + } // if(_initialized) + if(_peek_buff_valid){ + ret = _peek_buff; + } _give_if_top_call(); return ret; } @@ -706,7 +728,7 @@ void I2SClass::flush(){ size_t bytes_written; void *item = NULL; if(_output_ring_buffer != NULL){ - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, 0, single_dma_buf); if (item != NULL){ esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); if(item_size != bytes_written){ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 0d8be3e105c..20c4ab57663 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -166,6 +166,8 @@ class I2SClass : public Stream RingbufHandle_t _output_ring_buffer; int _i2s_dma_buffer_size; bool _driveClock; + uint32_t _peek_buff; + bool _peek_buff_valid; void _tx_done_routine(uint8_t* prev_item); void _rx_done_routine(); From 5d786e7c801972dd3808cf09c62bbb893d683adf Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 23 Nov 2021 10:36:58 +0100 Subject: [PATCH 77/94] Bugfix - check if task exists before deleteing --- libraries/I2S/src/I2S.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 93c27a1902f..0a5bdac15ed 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -489,8 +489,10 @@ void I2SClass::_uninstallDriver(){ void I2SClass::end(){ _take_if_not_holding(); if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){ - vTaskDelete(_callbackTaskHandle); - _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + if(_callbackTaskHandle){ + vTaskDelete(_callbackTaskHandle); + _callbackTaskHandle = NULL; // prevent secondary termination to non-existing task + } _uninstallDriver(); _onTransmit = NULL; _onReceive = NULL; From 39bfd118b65bdfe1a6891806e81a2c21b192b9bb Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 25 Nov 2021 08:57:24 +0100 Subject: [PATCH 78/94] Added draft for unit tests --- libraries/I2S/examples/Testing/.skip.esp32c3 | 0 libraries/I2S/examples/Testing/Testing.ino | 160 ++++++++++++++++++ libraries/I2S/unit_tests/README.md | 37 ++++ .../unit_tests_ino/unit_tests_ino.ino | 76 +++++++++ 4 files changed, 273 insertions(+) create mode 100644 libraries/I2S/examples/Testing/.skip.esp32c3 create mode 100644 libraries/I2S/examples/Testing/Testing.ino create mode 100644 libraries/I2S/unit_tests/README.md create mode 100644 libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino diff --git a/libraries/I2S/examples/Testing/.skip.esp32c3 b/libraries/I2S/examples/Testing/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/examples/Testing/Testing.ino b/libraries/I2S/examples/Testing/Testing.ino new file mode 100644 index 00000000000..8984739b871 --- /dev/null +++ b/libraries/I2S/examples/Testing/Testing.ino @@ -0,0 +1,160 @@ +/* + 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. + + Circuit: + * Arduino/Genuino Zero, MKR family and Nano 33 IoT + * MAX08357: + * GND connected GND + * VIN connected 5V + * LRC connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32) + * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) + * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) + + created 17 November 2016 + by Sandeep Mistry + */ + +#define PLAY +//#define PLOT + +#include +const int frequency = 440; // frequency of square wave in Hz +const int amplitude = 500; // amplitude of square wave +const long sampleRate[] = {8000, 11025, 16000, 22050, 24000, 32000, 44100}; +const int bitsPerSample[] = {8, 16, 24, 32}; + +//const i2s_mode_t mode = I2S_PHILIPS_MODE; +//const i2s_mode_t mode = I2S_RIGHT_JUSTIFIED_MODE; +//const i2s_mode_t mode = I2S_LEFT_JUSTIFIED_MODE; +//const i2s_mode_t mode = ADC_DAC_MODE; +const i2s_mode_t mode = PDM_STEREO_MODE; +//const i2s_mode_t mode = PDM_MONO_MODE; + +void test() +{ + Serial.println("Try double end()"); + I2S.end(); + I2S.end(); + Serial.println(""); + + Serial.println("Try double begin()"); + Serial.printf("First begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); + Serial.printf("Second begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); + I2S.end(); + Serial.println(""); + + Serial.println("Try normal begin -> end twice"); + Serial.printf("First begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); + I2S.end(); + Serial.printf("Second begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); + I2S.end(); + Serial.println(""); + + Serial.println("Try begin with all modes"); + Serial.printf("begin(I2S_PHILIPS_MODE) returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); + I2S.end(); + Serial.printf("begin(I2S_RIGHT_JUSTIFIED_MODE) returned %d\n", I2S.begin(I2S_RIGHT_JUSTIFIED_MODE, 44100, 32)); + I2S.end(); + Serial.printf("begin(I2S_LEFT_JUSTIFIED_MODE) returned %d\n", I2S.begin(I2S_LEFT_JUSTIFIED_MODE, 44100, 32)); + I2S.end(); + Serial.printf("begin(ADC_DAC_MODE) returned %d\n", I2S.begin(ADC_DAC_MODE, 44100, 16)); + I2S.end(); + Serial.printf("begin(PDM_STEREO_MODE) returned %d\n", I2S.begin(PDM_STEREO_MODE, 44100, 32)); + I2S.end(); + Serial.printf("begin(PDM_MONO_MODE) returned %d\n", I2S.begin(PDM_MONO_MODE, 44100, 32)); + I2S.end(); + Serial.println("End of testing"); + Serial.println(""); +} + +void setup() { + Serial.begin(115200); + Serial.println("I2S testing"); + //test(); +} + +int halfWavelength; +void play_a_while(long seconds){ + long start_time = millis(); + long curr_time = 0; + int count = 0; + short sample = amplitude; + while(curr_time - start_time < seconds*1000){ + //Serial.print("curr_time = "); Serial.print(curr_time); Serial.print("; start_time = "); Serial.print(start_time); Serial.print("; seconds*1000 = "); Serial.println(seconds*1000); + if (count % halfWavelength == 0 ){ + // invert the sample every half wavelength count multiple to generate square wave + sample = -1 * sample; + } + + if(mode == I2S_PHILIPS_MODE || mode == ADC_DAC_MODE || mode == PDM_STEREO_MODE){ // write the same sample twice, once for Right and once for Left channel + I2S.write(sample); // Right channel + I2S.write(sample); // Left channel + }else if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE || mode == PDM_MONO_MODE){ + // write the same only once - it will be automatically copied to the other channel + I2S.write(sample); + } + + // increment the counter for the next sample + count++; + + //Serial.print("Played "); Serial.print((curr_time-start_time)/1000); Serial.print(" / "); Serial.print(seconds); Serial.println(" s"); + + curr_time = millis(); + } +} + +void plot_a_while(long seconds){ + long start_time = millis(); + long curr_time = 0; + while(curr_time - start_time < seconds*1000){ + //Serial.print("curr_time = "); Serial.print(curr_time); Serial.print("; start_time = "); Serial.print(start_time); Serial.print("; seconds*1000 = "); Serial.println(seconds*1000); + // read a sample + int sample = I2S.read(); + //Serial.println(sample); + + if (sample && sample != -1 && sample != 1) { + Serial.println(sample); + } + } +} + +void loop() { + for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ + #ifdef PLAY + if(mode == ADC_DAC_MODE && bitsPerSample[bps] != 16){ + continue; + } + #endif + + for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ + #ifdef PLAY + Serial.print("Setting up bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); + #endif + if (!I2S.begin(mode, sampleRate[sr], bitsPerSample[bps])) { + Serial.println("Failed to initialize I2S!"); + }else{ + halfWavelength = (sampleRate[sr] / frequency); // half wavelength of square wave + #ifdef PLAY + Serial.println("Setup done. Starting playback"); + play_a_while(3); + Serial.println("Playback finished"); + #endif + + #ifdef PLOT + plot_a_while(5); + #endif + I2S.end(); + } // begin ok + #ifdef PLAY + Serial.println(""); + #endif + delay(1000); + } // sr + #ifdef PLAY + Serial.println("==============================================="); + #endif + delay(3000); + } // bps +} diff --git a/libraries/I2S/unit_tests/README.md b/libraries/I2S/unit_tests/README.md new file mode 100644 index 00000000000..0068eaeed30 --- /dev/null +++ b/libraries/I2S/unit_tests/README.md @@ -0,0 +1,37 @@ +# I2S unit tests + +Set of both codes and ESPs act as automated unit test to verify Arduino-like I2S lib. +These tests can be run after every commit to catch potential bugs. + +unit_tests_ino contains sketch using Arduino-like I2S library. This sketch is intended to be flashed onto ESP32 (or Arduino supporting I2S) and connected to another ESP32 using code from unit_tests_idf. + +unit_tests_idf contains IDF code acting as a counterpart for Arduino-like I2S lib. + +### Hardware: +- 2 pcs ESP32 +- 7 wires (connecting I2S and I2C + GND) +- 2 USB cables for ESP connections +- Optional: Logic analyzer + oscilloscope +- Optional: breadboard + +### Setup: +- Connect pins: + +Chose if you want to test ESP32 or Arduino and connect one of those to second ESP32 + + Arduino MKR-Zero | ESP32 | ESP32 + ino code | ino code | idf code | note + ? | GND | GND | + ? | 14 | 14 | I2S CLK + ? | 25 | 25 | I2S WS + ? | 35 | 26 | I2S ino-input <- idf-output + ? | 26 | 35 | I2S ino-output -> idf-input + ? | 21 | 21 | I2C SDA + ? | 22 | 22 | I2C SCL +- Connect USB cables and flash +- Boards should detect each other via I2C and start testing automatically and report partial results and overall result when finished. + +### Build IDF counterpart +- Download [esp-idf](https://github.com/espressif/esp-idf) and [install](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html#step-3-set-up-the-tools) +- Build with `idf.py build` +- Flash with `idf.py -p /dev/ttyUSB0 monitor flash` \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino b/libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino new file mode 100644 index 00000000000..cdfb75f027b --- /dev/null +++ b/libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino @@ -0,0 +1,76 @@ +#include "Wire.h" +#include "I2S.h" +int x; +void receiveEvent(int size) +{ + x = Wire.read(); +} + +void requestEvent() { + Wire.write("hello "); // respond with message of 6 bytes +} + +void setup() { + Serial.begin(115200); + while(!Serial){ + delay(10); + } + Serial.println("I2S .ino: Serial connected"); + Wire.begin(42); // Start I2C as slave with address 42 + Wire.onReceive(receiveEvent); + Wire.onRequest(requestEvent); + Serial.println("######################################################################"); + Serial.println("I2S .ino: Testing starting"); +} + +void loop() { +switch(x){ + case 0: + Serial.print("TODO: call test"); Serial.println(x); + // Verify that unexpected calls return with error and don't crash + // Unexpected calls are for example begin() on initialized object... + break; + case 1: + Serial.print("TODO: call test"); Serial.println(x); + // Verify data transfer - send predefined data sequence and wait for confirmation from counterpart + // Receive predefined data sequence and send confirmation to counterpart + // Repeat test for all digital modes + break; + case 2: + Serial.print("TODO: call test"); Serial.println(x); + // Verify buffers - proper available returns, peek and flush + break; + case 3: + Serial.print("TODO: call test"); Serial.println(x); + // Verify callbacks + break; + case 4: + Serial.print("TODO: call test"); Serial.println(x); + // Verify pin setups + break; + case 5: + Serial.print("TODO: call test"); Serial.println(x); + // Verify ADC and DAC + break; + case 6: + Serial.print("TODO: call test"); Serial.println(x); + break; + case 7: + Serial.print("TODO: call test"); Serial.println(x); + break; + case 8: + Serial.print("TODO: call test"); Serial.println(x); + break; + case 9: + Serial.print("TODO: call test"); Serial.println(x); + break; + case 10: + Serial.print("TODO: call test"); Serial.println(x); + break; + + + default: + Serial.print("Unknown command"); Serial.println(x); + break; + } // switch +} \ No newline at end of file From 262b18efc5882cb0927461fa36ae3316fdbe67a9 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 26 Nov 2021 13:55:24 +0100 Subject: [PATCH 79/94] Basic UART comm for unit tests --- libraries/I2S/keywords.txt | 54 +++++------ libraries/I2S/library.properties | 6 +- libraries/I2S/src/I2S.cpp | 5 +- libraries/I2S/unit_tests/README.md | 14 ++- libraries/I2S/unit_tests/cli_monitor.sh | 3 + .../test_counterpart/test_counterpart.ino | 55 +++++++++++ .../test_counterpart/unit_tests_common.h | 1 + libraries/I2S/unit_tests/unit_tests_common.h | 51 +++++++++++ .../unit_tests_ino/unit_tests_ino.ino | 76 ---------------- .../unit_under_test/01_smoke_test.ino | 82 +++++++++++++++++ .../unit_under_test/02_data_transfer.ino | 0 .../unit_under_test/03_buffer_test.ino | 0 .../unit_under_test/unit_tests_common.h | 1 + .../unit_under_test/unit_under_test.ino | 91 +++++++++++++++++++ 14 files changed, 326 insertions(+), 113 deletions(-) create mode 100755 libraries/I2S/unit_tests/cli_monitor.sh create mode 100644 libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino create mode 120000 libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h create mode 100644 libraries/I2S/unit_tests/unit_tests_common.h delete mode 100644 libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino create mode 100644 libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino create mode 100644 libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino create mode 100644 libraries/I2S/unit_tests/unit_under_test/03_buffer_test.ino create mode 120000 libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h create mode 100644 libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino diff --git a/libraries/I2S/keywords.txt b/libraries/I2S/keywords.txt index 603dcc0fdfe..ad1b8028d42 100644 --- a/libraries/I2S/keywords.txt +++ b/libraries/I2S/keywords.txt @@ -11,40 +11,40 @@ I2S KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -I2SClass KEYWORD2 +I2SClass KEYWORD2 begin KEYWORD2 end KEYWORD2 onReceive KEYWORD2 onTransmit KEYWORD2 -setSckPin KEYWORD2 -setFsPin KEYWORD2 -setDataInPin KEYWORD2 -setDataOutPin KEYWORD2 -setAllPins KEYWORD2 +setSckPin KEYWORD2 +setFsPin KEYWORD2 +setDataInPin KEYWORD2 +setDataOutPin KEYWORD2 +setAllPins KEYWORD2 -getSckPin KEYWORD2 -getFsPin KEYWORD2 -getDataPin KEYWORD2 -getDataInPin KEYWORD2 -getDataOutPin KEYWORD2 +getSckPin KEYWORD2 +getFsPin KEYWORD2 +getDataPin KEYWORD2 +getDataInPin KEYWORD2 +getDataOutPin KEYWORD2 -setDuplex KEYWORD2 -setSimplex KEYWORD2 -isDuplex KEYWORD2 +setDuplex KEYWORD2 +setSimplex KEYWORD2 +isDuplex KEYWORD2 setBufferSize KEYWORD2 -getBufferSize KEYWORD2 +getBufferSize KEYWORD2 -write KEYWORD2 -availableForWrite KEYWORD2 +write KEYWORD2 +availableForWrite KEYWORD2 -read KEYWORD2 -available KEYWORD2 +read KEYWORD2 +available KEYWORD2 -gpioToAdcUnit KEYWORD2 -gpioToAdcChannel KEYWORD2 +gpioToAdcUnit KEYWORD2 +gpioToAdcChannel KEYWORD2 ####################################### # Constants (LITERAL1) @@ -52,10 +52,10 @@ gpioToAdcChannel KEYWORD2 I2S_PHILIPS_MODE LITERAL1 I2S_RIGHT_JUSTIFIED_MODE LITERAL1 I2S_LEFT_JUSTIFIED_MODE LITERAL1 -I2S_ADC_DAC LITERAL1 -I2S_PDM LITERAL1 +I2S_ADC_DAC LITERAL1 +I2S_PDM LITERAL1 -PIN_I2S_SCK LITERAL1 -PIN_I2S_FS LITERAL1 -PIN_I2S_SD LITERAL1 -PIN_I2S_SD_OUT LITERAL1 +PIN_I2S_SCK LITERAL1 +PIN_I2S_FS LITERAL1 +PIN_I2S_SD LITERAL1 +PIN_I2S_SD_OUT LITERAL1 diff --git a/libraries/I2S/library.properties b/libraries/I2S/library.properties index 99ac3080db5..bb77e306158 100644 --- a/libraries/I2S/library.properties +++ b/libraries/I2S/library.properties @@ -1,8 +1,8 @@ name=I2S version=1.0 -author=Arduino -maintainer=Arduino -sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for Arduino Zero. +author=Tomas Pilny +maintainer=Tomas Pilny +sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for ESP. paragraph= category=Communication url=http://www.arduino.cc/en/Reference/I2S diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 0a5bdac15ed..3f266e57ad7 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -119,7 +119,7 @@ int I2SClass::_installDriver(){ } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); #else - log_e("This chip does not support DAC / ADC"); + log_e("This chip does not support ADC / DAC mode"); return 0; // ERR #endif }else if(_mode == I2S_PHILIPS_MODE || @@ -682,6 +682,7 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ flush(); } if(_output_ring_buffer != NULL){ + log_d("try to send %d B to ring buffer", size); if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 0)){ _give_if_top_call(); return size; @@ -879,8 +880,6 @@ void I2SClass::_rx_done_routine(){ void I2SClass::_onTransferComplete(){ uint8_t prev_item[_i2s_dma_buffer_size*4]; - static QueueSetHandle_t xQueueSet; - QueueSetMemberHandle_t xActivatedMember; esp_i2s::i2s_event_t i2s_event; while(true){ diff --git a/libraries/I2S/unit_tests/README.md b/libraries/I2S/unit_tests/README.md index 0068eaeed30..701986d9a6c 100644 --- a/libraries/I2S/unit_tests/README.md +++ b/libraries/I2S/unit_tests/README.md @@ -8,7 +8,7 @@ unit_tests_ino contains sketch using Arduino-like I2S library. This sketch is in unit_tests_idf contains IDF code acting as a counterpart for Arduino-like I2S lib. ### Hardware: -- 2 pcs ESP32 +- 2 pcs ESP32, or 1 ESP32 and Arduino supporting I2S - 7 wires (connecting I2S and I2C + GND) - 2 USB cables for ESP connections - Optional: Logic analyzer + oscilloscope @@ -26,12 +26,18 @@ Chose if you want to test ESP32 or Arduino and connect one of those to second ES ? | 25 | 25 | I2S WS ? | 35 | 26 | I2S ino-input <- idf-output ? | 26 | 35 | I2S ino-output -> idf-input - ? | 21 | 21 | I2C SDA - ? | 22 | 22 | I2C SCL + 1(TX) | 21 | 22 | UART ino-TX -> idf-RX + 0(RX) | 22 | 21 | UART ino-RX <- idf-TX - Connect USB cables and flash - Boards should detect each other via I2C and start testing automatically and report partial results and overall result when finished. +- Connect logic analyzer on I2S bus ### Build IDF counterpart - Download [esp-idf](https://github.com/espressif/esp-idf) and [install](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html#step-3-set-up-the-tools) - Build with `idf.py build` -- Flash with `idf.py -p /dev/ttyUSB0 monitor flash` \ No newline at end of file +- Flash with `idf.py -p /dev/ttyUSB0 monitor flash` + + +### TODO +- ADC and DAC connections and verification +- Pin change - connect additional set of I2S bus to different pins to verify pin change works properly \ No newline at end of file diff --git a/libraries/I2S/unit_tests/cli_monitor.sh b/libraries/I2S/unit_tests/cli_monitor.sh new file mode 100755 index 00000000000..b55635699cf --- /dev/null +++ b/libraries/I2S/unit_tests/cli_monitor.sh @@ -0,0 +1,3 @@ +echo "Using device /dev/ttyUSB$1" +stty -F /dev/ttyUSB$1 raw 115200 +cat /dev/ttyUSB$1 diff --git a/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino b/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino new file mode 100644 index 00000000000..741943604ad --- /dev/null +++ b/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino @@ -0,0 +1,55 @@ +#include +#include +#include "esp_log.h" +#include "unit_tests_common.h" +#include "driver/uart.h" + +void setup() +{ + Serial.begin(115200); // Serial monitor + while(!Serial){ + delay(10); + } + + #ifdef ESP_PLATFORM + Serial1.begin(115200, SERIAL_8N1, 22, 21); + #else + Serial.println("This is intended to be used only on ESP!"); + while(1){ ; } + #endif + + while(!Serial1){ + delay(10); + } + Serial.println("I2C initialized successfully"); + Serial.println("I2S unit tests counterpart starting..."); +} + + +void loop() +{ + enum msg_t data = START_TEST_1; + + Serial1.write(data); + Serial.printf("Sent msg num %d = \"%s\"\n", data, txt_msg[data]); + + // Read data from UART. + uint8_t data_read[128]; + while(!Serial1.available()){ + ; // wait + } + + int recv; + while(Serial1.available()){ + recv = Serial1.read(); + } + Serial.print("Received response number "); + Serial.print(recv); + Serial.print(" = "); + Serial.println(txt_msg[recv]); + + // We will continue in later additions... + while(1){ + ; // halt + } +} diff --git a/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h b/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h new file mode 120000 index 00000000000..3190abe20db --- /dev/null +++ b/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h @@ -0,0 +1 @@ +../unit_tests_common.h \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_tests_common.h b/libraries/I2S/unit_tests/unit_tests_common.h new file mode 100644 index 00000000000..094eeb12ec0 --- /dev/null +++ b/libraries/I2S/unit_tests/unit_tests_common.h @@ -0,0 +1,51 @@ +#define UNIT_TESTS_I2C_SLAVE_ADDR 0x42 + +enum msg_t{ +NOP, // No Operation - stay idle +UUT_RDY, // Unit Under Test reports it is ready +START_TEST_1, +TEST_1_FINISHED, +START_TEST_2, +TEST_2_FINISHED, +START_TEST_3, +TEST_3_FINISHED, +START_TEST_4, +TEST_4_FINISHED, +START_TEST_5, +TEST_5_FINISHED, +START_TEST_6, +TEST_6_FINISHED, +START_TEST_7, +TEST_7_FINISHED, +START_TEST_8, +TEST_8_FINISHED, +START_TEST_9, +TEST_9_FINISHED, +START_TEST_10, +TEST_10_FINISHED +}; + +static char txt_msg[22][32] = { +"NOP", // 00 No Operation - stay idle +"UUT_RDY", // 01 Unit Under Test reports it is ready +"START_TEST_1", // 02 +"TEST_1_FINISHED", // 03 +"START_TEST_2", // 04 +"TEST_2_FINISHED", // 05 +"START_TEST_3", // 06 +"TEST_3_FINISHED", // 07 +"START_TEST_4", // 08 +"TEST_4_FINISHED", // 09 +"START_TEST_5", // 10 +"TEST_5_FINISHED", // 11 +"START_TEST_6", // 12 +"TEST_6_FINISHED", // 13 +"START_TEST_7", // 14 +"TEST_7_FINISHED", // 15 +"START_TEST_8", // 16 +"TEST_8_FINISHED", // 17 +"START_TEST_9", // 18 +"TEST_9_FINISHED", // 19 +"START_TEST_10", // 20 +"TEST_10_FINISHED" // 21 +}; \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino b/libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino deleted file mode 100644 index cdfb75f027b..00000000000 --- a/libraries/I2S/unit_tests/unit_tests_ino/unit_tests_ino.ino +++ /dev/null @@ -1,76 +0,0 @@ -#include "Wire.h" -#include "I2S.h" -int x; -void receiveEvent(int size) -{ - x = Wire.read(); -} - -void requestEvent() { - Wire.write("hello "); // respond with message of 6 bytes -} - -void setup() { - Serial.begin(115200); - while(!Serial){ - delay(10); - } - Serial.println("I2S .ino: Serial connected"); - Wire.begin(42); // Start I2C as slave with address 42 - Wire.onReceive(receiveEvent); - Wire.onRequest(requestEvent); - Serial.println("######################################################################"); - Serial.println("I2S .ino: Testing starting"); -} - -void loop() { -switch(x){ - case 0: - Serial.print("TODO: call test"); Serial.println(x); - // Verify that unexpected calls return with error and don't crash - // Unexpected calls are for example begin() on initialized object... - break; - case 1: - Serial.print("TODO: call test"); Serial.println(x); - // Verify data transfer - send predefined data sequence and wait for confirmation from counterpart - // Receive predefined data sequence and send confirmation to counterpart - // Repeat test for all digital modes - break; - case 2: - Serial.print("TODO: call test"); Serial.println(x); - // Verify buffers - proper available returns, peek and flush - break; - case 3: - Serial.print("TODO: call test"); Serial.println(x); - // Verify callbacks - break; - case 4: - Serial.print("TODO: call test"); Serial.println(x); - // Verify pin setups - break; - case 5: - Serial.print("TODO: call test"); Serial.println(x); - // Verify ADC and DAC - break; - case 6: - Serial.print("TODO: call test"); Serial.println(x); - break; - case 7: - Serial.print("TODO: call test"); Serial.println(x); - break; - case 8: - Serial.print("TODO: call test"); Serial.println(x); - break; - case 9: - Serial.print("TODO: call test"); Serial.println(x); - break; - case 10: - Serial.print("TODO: call test"); Serial.println(x); - break; - - - default: - Serial.print("Unknown command"); Serial.println(x); - break; - } // switch -} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino b/libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino new file mode 100644 index 00000000000..3dfe4aa5f27 --- /dev/null +++ b/libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino @@ -0,0 +1,82 @@ +int test_01(){ + int ret = 0; + int tmp; + int tmp2; + // ============================================================================================ + + Serial.println("Try double end()"); + I2S.end(); + I2S.end(); + Serial.println(""); + // ============================================================================================ + + Serial.println("Try double begin()"); + tmp = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); + Serial.print("First begin() returned "); Serial.println(tmp); + tmp2 = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); + Serial.printf("Second begin() returned "); Serial.println(tmp2); + if(tmp != 1 || tmp2 != 0){ + Serial.println("Double begin() test failed"); + ++ret; + } + I2S.end(); + Serial.println(""); + // ============================================================================================ + + Serial.println("Try normal begin -> end; each sequence twice"); + tmp = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); + Serial.printf("First begin() returned "); Serial.println(tmp); + I2S.end(); + tmp2 = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); + Serial.printf("Second begin() returned "); Serial.println(tmp2); + I2S.end(); + if(tmp != 1 && tmp2 != 1){ + Serial.println("Double begin() -> end() test failed"); + ++ret; + } + Serial.println(""); + // ============================================================================================ + + Serial.println("Try begin with all modes"); + + tmp = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); + if(tmp != 1){ + Serial.println("begin with mode I2S_PHILIPS_MODE failed"); + ++ret; + } + I2S.end(); + tmp = I2S.begin(I2S_RIGHT_JUSTIFIED_MODE, 44100, 32); + if(tmp != 1){ + Serial.println("begin with mode I2S_RIGHT_JUSTIFIED_MODE failed"); + ++ret; + } + I2S.end(); + tmp = I2S.begin(I2S_LEFT_JUSTIFIED_MODE, 44100, 32); + if(tmp != 1){ + Serial.println("begin with mode I2S_LEFT_JUSTIFIED_MODE failed"); + ++ret; + } + I2S.end(); + tmp = I2S.begin(ADC_DAC_MODE, 44100, 16); + if(tmp != 1){ + Serial.println("begin with mode ADC_DAC_MODE failed"); + ++ret; + } + I2S.end(); + tmp = I2S.begin(PDM_STEREO_MODE, 44100, 32); + if(tmp != 1){ + Serial.println("begin with mode PDM_STEREO_MODE failed"); + ++ret; + } + I2S.end(); + tmp = I2S.begin(PDM_MONO_MODE, 44100, 32); + if(tmp != 1){ + Serial.println("begin with mode PDM_MONO_MODE failed"); + ++ret; + } + I2S.end(); + // ============================================================================================ + + Serial.print("End of smoke test. ERRORS = "); Serial.println(ret); + return ret; +} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino b/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/unit_tests/unit_under_test/03_buffer_test.ino b/libraries/I2S/unit_tests/unit_under_test/03_buffer_test.ino new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h b/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h new file mode 120000 index 00000000000..3190abe20db --- /dev/null +++ b/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h @@ -0,0 +1 @@ +../unit_tests_common.h \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino b/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino new file mode 100644 index 00000000000..7e01a7105b5 --- /dev/null +++ b/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino @@ -0,0 +1,91 @@ +#include "I2S.h" +#include "unit_tests_common.h" + +int errors; + +void setup() { + Serial.begin(115200); // Serial monitor + while(!Serial){ + delay(10); + } + + #ifdef ESP_PLATFORM + Serial1.begin(115200, SERIAL_8N1, 22, 21); + #else + Serial1.begin(115200); + #endif + while(!Serial1){ + delay(10); + } + errors = 0; + Serial.println("I2S .ino: Both serial interfaces connected"); + Serial.println("######################################################################"); + Serial.println("I2S .ino: Testing starting"); + + + Serial.println("debug: print msg[0] as text"); + Serial.println(txt_msg[0]); +} + +void loop() { + while(!Serial1.available()){ + Serial.print("."); + delay(1000); + //; // do nothing + } + int data = Serial1.read(); + Serial.print("Received number "); Serial.println(data); + enum msg_t msg = (msg_t)data; + + switch(msg){ + case START_TEST_1: + // Verify that unexpected calls return with error and don't crash + // Unexpected calls are for example begin() on initialized object... + Serial.print("Received code "); Serial.print(msg); Serial.print(" = "); Serial.println(txt_msg[msg]); + Serial.println("Start smoke test"); + errors += test_01(); + Serial1.write(TEST_1_FINISHED); + break; + case START_TEST_2: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + // Verify data transfer - send predefined data sequence and wait for confirmation from counterpart + // Receive predefined data sequence and send confirmation to counterpart + // Repeat test for all digital modes + break; + case START_TEST_3: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + // Verify buffers - proper available returns, peek and flush + break; + case START_TEST_4: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + // Verify callbacks + break; + case START_TEST_5: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + // Verify pin setups + break; + case START_TEST_6: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + // Verify ADC and DAC + break; + case START_TEST_7: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + break; + case START_TEST_8: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + break; + case START_TEST_9: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + break; + case START_TEST_10: + Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + break; + case NOP: + break; + default: + Serial.print("Unknown command number "); Serial.println(msg); + break; + } // switch + + msg = NOP; +} \ No newline at end of file From 0afe17c908353c517d1618594723588ff5504b3f Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 2 Dec 2021 11:28:25 +0100 Subject: [PATCH 80/94] Work In Progress on I2S data transfer --- .../test_counterpart/02_data_transfer.ino | 233 ++++++++++++++++++ .../test_counterpart/test_counterpart.ino | 40 ++- .../test_counterpart/unit_tests_common.cpp | 1 + .../I2S/unit_tests/unit_tests_common.cpp | 33 +++ libraries/I2S/unit_tests/unit_tests_common.h | 118 +++++---- .../unit_under_test/02_data_transfer.ino | 128 ++++++++++ .../unit_under_test/unit_tests_common.cpp | 1 + .../unit_under_test/unit_under_test.ino | 39 ++- 8 files changed, 513 insertions(+), 80 deletions(-) create mode 100644 libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino create mode 120000 libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp create mode 100644 libraries/I2S/unit_tests/unit_tests_common.cpp create mode 120000 libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp diff --git a/libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino b/libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino new file mode 100644 index 00000000000..76c93fa52ef --- /dev/null +++ b/libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino @@ -0,0 +1,233 @@ +#include "unit_tests_common.h" + +#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 +#define _i2s_dma_buffer_size 1024 + + +// This is mostly copy from I2S lib, but we will not use the ring buffers and we will read / write directly with idf-i2s driver +int my_i2s_begin(int mode, uint32_t sampleRate, uint32_t bitsPerSample){ + + esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX | esp_i2s::I2S_MODE_SLAVE); + + if(mode == I2S_PHILIPS_MODE || mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE){ + if(bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32){ + log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 8 | 16 | 24 | 32", bitsPerSample); + return 0; // ERR + } + }else if(mode == PDM_STEREO_MODE || mode == PDM_MONO_MODE){ // end of Normal Philips mode; start of PDM mode + #if (SOC_I2S_SUPPORTS_PDM_TX && SOC_I2S_SUPPORTS_PDM_RX) + i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); + #else + log_e("This chip does not support PDM"); + return 0; // ERR + #endif + } // Mode + esp_i2s::i2s_config_t i2s_config = { + .mode = i2s_mode, + .sample_rate = sampleRate, + .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)bitsPerSample, + .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, + .dma_buf_count = _I2S_DMA_BUFFER_COUNT, + .dma_buf_len = _i2s_dma_buffer_size, + .use_apll = true + }; + // Install and start i2s driver + if(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) 0, &i2s_config, 0, NULL)){ + log_e("ERROR i2s driver install failed"); + return 0; // ERR + } //try installing i2s driver + + if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE || mode == PDM_MONO_MODE){ // mono/single channel + // Set the clock for MONO. Stereo is not supported yet. + if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) 0, sampleRate, (esp_i2s::i2s_bits_per_sample_t)bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ + log_e("Setting the I2S Clock has failed!\n"); + return 0; // ERR + } + } // mono channel mode + + esp_i2s::i2s_pin_config_t pin_config = { + .bck_io_num = 14, + .ws_io_num = 25, + .data_out_num = 26, + .data_in_num = 35 + }; + + if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) 0, &pin_config)){ + log_e("i2s_set_pin failed"); + return 0; // ERR + }else{ + return 1; // OK + } + return 1; // OK +} + +int test_02(){ + int errors = 0; + int current_errors = 0; + int data; + int tmp; + int tmp2; + Serial.println("Counterpart for data transfer test"); + Serial.println("=============================================================================="); + // ============================================================================================ + Serial.println("Counterpart reads, UUT writes"); + uint8_t buffer[sizeof(bps32)]; + for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately + for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ + for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ + Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); + if (!my_i2s_begin(mode[m], sampleRate[sr], bitsPerSample[bps])){ + //if (!I2S.begin(mode[m], sampleRate[sr], bitsPerSample[bps], false)){ + Serial.println("Failed to initialize I2S!"); + ++errors; + Serial1.write(C_ERR); + continue; + } + + Serial1.write(C_RDY); + data = wait_and_read(); + if(data == UUT_ERR){ + Serial.println("UUT reports error"); + ++errors; + continue; + } + + if(data != UUT_RDY){ + Serial.print("UUT sends unexpected msg code: "); + Serial.println(data); + halt(); + // what now? + }else{ + Serial.println("UUT reports ready - starting receiving"); + } + + size_t bytes_read; + int zero_samples = 0; + esp_err_t ret; + buffer[0] = 0; + + ret = i2s_read((esp_i2s::i2s_port_t) 0, buffer, 1, &bytes_read, 1000); + //ret = I2S.read(buffer, 1); + if(ret != ESP_OK){Serial.printf("i2s_read returned with error %d\n", ret);} + if(buffer[0] == 0){ + ++zero_samples; + Serial.println("first try - zero sample"); + } + + while(buffer[0] == 0){ + ret = i2s_read((esp_i2s::i2s_port_t) 0, buffer, 1, &bytes_read, 100); + //I2S.read(buffer, 1); + if(ret != ESP_OK){Serial.printf("i2s_read returned with error %d\n", ret);} + ++zero_samples; + Serial.print("zero samples = "); Serial.println(zero_samples); + } + Serial.print("First non-zero sample after "); Serial.print(zero_samples); Serial.print(" zero samples; the sample is "); Serial.println(buffer[0]); + + Serial.println("UUT reports ready - starting read"); + current_errors = 0; + switch(bitsPerSample[bps]){ + case 8: + ret = i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps8), &bytes_read, 100); + //ret = I2S.read(buffer, 1); + if(ret != ESP_OK){Serial.printf("i2s_read returned with error %d\n", ret);} + if(bytes_read != sizeof(bps8)){Serial.printf("bytes_read %d != %d sizeof(bps8); break test\n", bytes_read, sizeof(bps8)); ++current_errors; break;} + for(int i = 0; i < sizeof(bps8); ++i){ if(buffer[i] != bps8[i]){Serial.printf("buffer[%d] %d != %d bps8[%d]\n", i, buffer[i], bps8[i], i); ++current_errors;}} + break; + case 16: + i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps16), &bytes_read, 100); + if(bytes_read != sizeof(bps16)){Serial.printf("bytes_read %d != %d sizeof(bps16); break test\n", bytes_read, sizeof(bps16)); ++current_errors; break;} + for(int i = 0; i < sizeof(bps16)/2; ++i){ if(((uint16_t*)buffer)[i] != bps16[i]){Serial.printf("buffer[i] %d != %d bps16[i]\n", ((uint16_t*)buffer)[i], bps16[i]); ++current_errors;}} + break; + case 24: + i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps24), &bytes_read, 100); + if(bytes_read != sizeof(bps8)){Serial.printf("bytes_read %d != %d sizeof(bps24); break test\n", bytes_read, sizeof(bps24)); ++current_errors; break;} + for(int i = 0; i < sizeof(bps24)/4; ++i){ if(((uint32_t*)buffer)[i] != bps24[i]){Serial.printf("buffer[i] %d != %d bps32[i]\n", ((uint32_t*)buffer)[i], bps24[i]); ++current_errors;}} + break; + case 32: + i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps32), &bytes_read, 100); + if(bytes_read != sizeof(bps32)){Serial.printf("bytes_read %d != %d sizeof(bps32); break test\n", bytes_read, sizeof(bps32)); ++current_errors; break;} + for(int i = 0; i < sizeof(bps32)/4; ++i){ if(((uint32_t*)buffer)[i] != bps32[i]){Serial.printf("buffer[i] %d != %d bps32[i]\n", ((uint32_t*)buffer)[i], bps32[i]); ++current_errors;}} + break; + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) 0); + Serial1.write(current_errors); + errors += current_errors; + } // sr + } // bps + } // i2s mode + // ============================================================================================ + return errors; // only for debug + for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately + for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ + for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ + Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); + if (!my_i2s_begin(mode[m], sampleRate[sr], bitsPerSample[bps])) { + Serial.println("Failed to initialize I2S!"); + ++errors; + Serial1.write(C_ERR); + continue; + } + + Serial1.write(C_RDY); + int data = wait_and_read(); + if(data == UUT_ERR){ + Serial.println("UUT reports error"); + ++errors; + continue; + } + + if(data != UUT_RDY){ + Serial.print("UUT sends unexpected msg code: "); + Serial.println(data); + halt(); + // what now? + }else{ + Serial.println("UUT reports ready - starting transfer"); + } + + size_t bytes_written; + current_errors = 0; + switch(bitsPerSample[bps]){ + + case 8: + esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps8, sizeof(bps8), &bytes_written, 0); + if(sizeof(bps8) != bytes_written){ + Serial.printf("sizeof(bps8) %d != %d bytes_written\n",sizeof(bps8) ,bytes_written); + ++current_errors; + } + break; + case 16: + esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps16, sizeof(bps16), &bytes_written, 0); + if(sizeof(bps16) != bytes_written){ + Serial.printf("sizeof(bps16) %d != %d bytes_written\n",sizeof(bps16) ,bytes_written); + ++current_errors; + } + break; + case 24: + esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps24, sizeof(bps32), &bytes_written, 0); + if(sizeof(bps32) != bytes_written){ + Serial.printf("sizeof(bps32) %d != %d bytes_written\n",sizeof(bps32) ,bytes_written); + ++current_errors; + } + break; + case 32: + esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps32, sizeof(bps32), &bytes_written, 0); + if(sizeof(bps32) != bytes_written){ + Serial.printf("sizeof(bps32) %d != %d bytes_written\n",sizeof(bps32) ,bytes_written); + ++current_errors; + } + break; + } + esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) 0); + Serial1.write(current_errors); + errors += current_errors; + } // sr + } // bps + } // i2s mode + Serial.println("=============================================================================="); + + Serial.print("End of template test. ERRORS = "); Serial.println(errors); + return errors; +} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino b/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino index 741943604ad..b5675ee50b2 100644 --- a/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino +++ b/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino @@ -1,8 +1,4 @@ -#include -#include -#include "esp_log.h" #include "unit_tests_common.h" -#include "driver/uart.h" void setup() { @@ -10,6 +6,7 @@ void setup() while(!Serial){ delay(10); } + Serial.println("Serial ready - connecting UART"); #ifdef ESP_PLATFORM Serial1.begin(115200, SERIAL_8N1, 22, 21); @@ -21,35 +18,26 @@ void setup() while(!Serial1){ delay(10); } - Serial.println("I2C initialized successfully"); Serial.println("I2S unit tests counterpart starting..."); } - void loop() { - enum msg_t data = START_TEST_1; - - Serial1.write(data); - Serial.printf("Sent msg num %d = \"%s\"\n", data, txt_msg[data]); - - // Read data from UART. - uint8_t data_read[128]; - while(!Serial1.available()){ - ; // wait + /* + send_and_print(START_TEST_1); + if(TEST_1_FINISHED != read_and_print()){ + halt(); } - - int recv; - while(Serial1.available()){ - recv = Serial1.read(); + */ + + send_and_print(START_TEST_2); + test_02(); + int recv = read_and_print(); + if(TEST_2_FINISHED != recv){ + Serial.printf("Was expecting TEST_2_FINISHED from UUT, but received %d\n", recv); + halt(); } - Serial.print("Received response number "); - Serial.print(recv); - Serial.print(" = "); - Serial.println(txt_msg[recv]); // We will continue in later additions... - while(1){ - ; // halt - } + halt(); } diff --git a/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp b/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp new file mode 120000 index 00000000000..23b1bb1bd8c --- /dev/null +++ b/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp @@ -0,0 +1 @@ +../unit_tests_common.cpp \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_tests_common.cpp b/libraries/I2S/unit_tests/unit_tests_common.cpp new file mode 100644 index 00000000000..8a27769414b --- /dev/null +++ b/libraries/I2S/unit_tests/unit_tests_common.cpp @@ -0,0 +1,33 @@ +#include "unit_tests_common.h" + +void send_and_print(enum msg_t msg){ + Serial1.write(msg); + Serial.printf("Sent msg num %d = \"%s\"\n", msg, txt_msg[msg]); +} + +enum msg_t read_and_print(){ + while(!Serial1.available()){ + ; // wait + } + int recv = Serial1.read(); + Serial.print("Received response number "); + Serial.print(recv); + Serial.print(" = "); + Serial.println(txt_msg[recv]); + return (enum msg_t)recv; +} + +enum msg_t wait_and_read(){ + while(!Serial1.available()){ + ; // wait + } + int recv = Serial1.read(); + return (enum msg_t)recv; +} + +void halt(){ + Serial.print("Halt..."); + while(1){ + ; // halt + } +} diff --git a/libraries/I2S/unit_tests/unit_tests_common.h b/libraries/I2S/unit_tests/unit_tests_common.h index 094eeb12ec0..6e16068ba3b 100644 --- a/libraries/I2S/unit_tests/unit_tests_common.h +++ b/libraries/I2S/unit_tests/unit_tests_common.h @@ -1,51 +1,79 @@ -#define UNIT_TESTS_I2C_SLAVE_ADDR 0x42 +#pragma once + +#include "I2S.h" enum msg_t{ -NOP, // No Operation - stay idle -UUT_RDY, // Unit Under Test reports it is ready -START_TEST_1, -TEST_1_FINISHED, -START_TEST_2, -TEST_2_FINISHED, -START_TEST_3, -TEST_3_FINISHED, -START_TEST_4, -TEST_4_FINISHED, -START_TEST_5, -TEST_5_FINISHED, -START_TEST_6, -TEST_6_FINISHED, -START_TEST_7, -TEST_7_FINISHED, -START_TEST_8, -TEST_8_FINISHED, -START_TEST_9, -TEST_9_FINISHED, -START_TEST_10, -TEST_10_FINISHED +NOP, // 00 No Operation - stay idle +UUT_RDY, // 01 Unit Under Test reports it is ready +UUT_ERR, // 02 Unit Under Test reports error - cannot proceed with the current setup. Error will be counted by UUT. +C_RDY, // 03 Counterpart reports it is ready +C_ERR, // 04 Counterpart reports reports error - cannot proceed with the current setup. Error will be counted by UUT upon receiving this message. +START_TEST_1, // 05 +TEST_1_FINISHED, // 06 +START_TEST_2, // 07 +TEST_2_FINISHED, // 08 +START_TEST_3, // 09 +TEST_3_FINISHED, // 10 +START_TEST_4, // 11 +TEST_4_FINISHED, // 12 +START_TEST_5, // 13 +TEST_5_FINISHED, // 14 +START_TEST_6, // 15 +TEST_6_FINISHED, // 16 +START_TEST_7, // 17 +TEST_7_FINISHED, // 18 +START_TEST_8, // 19 +TEST_8_FINISHED, // 20 +START_TEST_9, // 21 +TEST_9_FINISHED, // 22 +START_TEST_10, // 23 +TEST_10_FINISHED // 24 }; -static char txt_msg[22][32] = { +static char txt_msg[26][32] = { "NOP", // 00 No Operation - stay idle "UUT_RDY", // 01 Unit Under Test reports it is ready -"START_TEST_1", // 02 -"TEST_1_FINISHED", // 03 -"START_TEST_2", // 04 -"TEST_2_FINISHED", // 05 -"START_TEST_3", // 06 -"TEST_3_FINISHED", // 07 -"START_TEST_4", // 08 -"TEST_4_FINISHED", // 09 -"START_TEST_5", // 10 -"TEST_5_FINISHED", // 11 -"START_TEST_6", // 12 -"TEST_6_FINISHED", // 13 -"START_TEST_7", // 14 -"TEST_7_FINISHED", // 15 -"START_TEST_8", // 16 -"TEST_8_FINISHED", // 17 -"START_TEST_9", // 18 -"TEST_9_FINISHED", // 19 -"START_TEST_10", // 20 -"TEST_10_FINISHED" // 21 -}; \ No newline at end of file +"UUT_ERR", // 02 +"Counterpart ready",// 03 +"Counterpart error",// 04 +"START_TEST_1", // 05 +"TEST_1_FINISHED", // 06 +"START_TEST_2", // 07 +"TEST_2_FINISHED", // 08 +"START_TEST_3", // 09 +"TEST_3_FINISHED", // 10 +"START_TEST_4", // 11 +"TEST_4_FINISHED", // 12 +"START_TEST_5", // 13 +"TEST_5_FINISHED", // 14 +"START_TEST_6", // 15 +"TEST_6_FINISHED", // 16 +"START_TEST_7", // 17 +"TEST_7_FINISHED", // 18 +"START_TEST_8", // 19 +"TEST_8_FINISHED", // 20 +"START_TEST_9", // 21 +"TEST_9_FINISHED", // 22 +"START_TEST_10", // 23 +"TEST_10_FINISHED" // 24 +}; + +//const i2s_mode_t mode[] = {I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, I2S_LEFT_JUSTIFIED_MODE, PDM_STEREO_MODE, PDM_MONO_MODE}; // Note: ADC_DAC_MODE is missing - it will be tested separately +const i2s_mode_t mode[] = {I2S_PHILIPS_MODE}; // debug +//const long sampleRate[] = {8000, 11025, 16000, 22050, 24000, 32000, 44100}; +const long sampleRate[] = {8000}; // debug +//const int bitsPerSample[] = {8, 16, 24, 32}; +const int bitsPerSample[] = {8}; // debug + +//const uint8_t bps8[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255}; +//const uint8_t bps8[] = {0,1,255}; // debug +//const uint8_t bps8[] = {170,170,170,170,170,170}; // debug +const uint8_t bps8[] = {170}; // debug +const uint16_t bps16[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255,256,32767,32768,32769,65534,65535}; +const uint32_t bps24[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255,256,32767,32768,32769,65534,65535,16777214,16777215}; +const uint32_t bps32[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255,256,32767,32768,32769,65534,65535,65536,16777214,16777215,16777216,4294967294,4294967295}; + +void send_and_print(enum msg_t msg); +enum msg_t read_and_print(); +enum msg_t wait_and_read(); +void halt(); diff --git a/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino b/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino index e69de29bb2d..f9a3e7feed6 100644 --- a/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino +++ b/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino @@ -0,0 +1,128 @@ +int test_02(){ + int errors = 0; + int tmp; + int tmp2; + int data; + Serial.println("Data transfer test"); + Serial.println("=============================================================================="); + Serial.println("UUT writes, Counterpart reads"); + // ============================================================================================ + for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately + for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ + for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ + Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); + if (!I2S.begin(mode[m], sampleRate[sr], bitsPerSample[bps])) { + Serial.println("Failed to initialize I2S!"); + ++errors; + Serial1.write(UUT_ERR); + continue; + } + + Serial1.write(UUT_RDY); + int data = wait_and_read(); + if(data == C_ERR){ + Serial.println("Counterpart reports error"); + ++errors; + continue; + } + + if(data != C_RDY){ + Serial.print("Counterpart sends unexpected msg code: "); + Serial.println(data); + // what now? + } + + Serial.println("Counterpart reports ready - starting transfer"); + switch(bitsPerSample[bps]){ + case 8: while(1){I2S.write(bps8, sizeof(bps8));} break; + case 16: I2S.write(bps16, sizeof(bps16)); break; + case 24: I2S.write(bps24, sizeof(bps24)); break; + case 32: I2S.write(bps32, sizeof(bps32)); break; + } + I2S.end(); + + // Receive number of errors detected by counterpart + data = wait_and_read(); + errors += data; + Serial.print("Counterpart reports "); Serial.print(data); Serial.println(" errors"); + } // sr + } // bps + } // i2s mode + Serial.println("=============================================================================="); +return errors; // only for debug + // ============================================================================================ + Serial.println("Counterpart writes, UUT reads"); + //uint8_t buffer[sizeof(bps32)]; + uint8_t buffer[1024]; + for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately + for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ + for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ + Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); + if (!I2S.begin(mode[m], sampleRate[sr], bitsPerSample[bps])) { + Serial.println("Failed to initialize I2S!"); + ++errors; + Serial1.write(UUT_ERR); + continue; + } + + Serial1.write(UUT_RDY); + data = wait_and_read(); + if(data == C_ERR){ + Serial.println("Counterpart reports error"); + ++errors; + continue; + } + + if(data != C_RDY){ + Serial.print("Counterpart sends unexpected msg code: "); + Serial.println(data); + // what now? + } + + int zero_samples = 0; + int ret; + do{ + ret = I2S.read(buffer, 1); + ++zero_samples; + Serial.print("zero samples = "); Serial.println(zero_samples); + }while(buffer[0] == 0); + + Serial.println("Counterpart reports ready - starting read"); + switch(bitsPerSample[bps]){ + case 8: + //ret = I2S.read(buffer, sizeof(bps8)); + ret = I2S.read(buffer, 1024); + if(ret == 0){Serial.println("There was an error reading from I2S: 0 bytes read"); ++errors; break;} + if(ret == 1024){Serial.println("OK, expected and read Bytes match");} + for(int i = 0; i < ret; ++i){ + Serial.print("Read sample [");Serial.print(i);Serial.print("] ");Serial.println(buffer[i]); + } + if(ret != sizeof(bps8)){ + Serial.print("Requested (");Serial.print(sizeof(bps8));Serial.print(") and read (");Serial.print(ret);Serial.println(") Bytes do not match!"); + }else{ + for(int i = 0; i < sizeof(bps8); ++i){ + if(buffer[i] != bps8[i]){ + Serial.print("Read sample [");Serial.print(i);Serial.print("] ");Serial.print(buffer[i]);Serial.print(" does not match expected value ");Serial.println(bps8[i]); + ++errors; + } + } + } + break; + case 16: I2S.read(buffer, sizeof(bps16)); + for(int i = 0; i < sizeof(bps16)/2; ++i){ if(((uint16_t*)buffer)[i] != bps16[i]) ++errors;} + break; + case 24: I2S.read(buffer, sizeof(bps24)); + for(int i = 0; i < sizeof(bps24)/4; ++i){ if(((uint32_t*)buffer)[i] != bps24[i]) ++errors;} + break; + case 32: I2S.read(buffer, sizeof(bps32)); + for(int i = 0; i < sizeof(bps32)/4; ++i){ if(((uint32_t*)buffer)[i] != bps32[i]) ++errors;} + break; + } + I2S.end(); + } // sr + } // bps + } // i2s mode + + Serial.print("End of data transfer test. ERRORS = "); Serial.println(errors); + return errors; +} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp b/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp new file mode 120000 index 00000000000..23b1bb1bd8c --- /dev/null +++ b/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp @@ -0,0 +1 @@ +../unit_tests_common.cpp \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino b/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino index 7e01a7105b5..e8ac3ddb407 100644 --- a/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino +++ b/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino @@ -34,52 +34,73 @@ void loop() { //; // do nothing } int data = Serial1.read(); - Serial.print("Received number "); Serial.println(data); + Serial.print("Received number "); Serial.print(data); Serial.print(": "); enum msg_t msg = (msg_t)data; switch(msg){ case START_TEST_1: // Verify that unexpected calls return with error and don't crash // Unexpected calls are for example begin() on initialized object... - Serial.print("Received code "); Serial.print(msg); Serial.print(" = "); Serial.println(txt_msg[msg]); - Serial.println("Start smoke test"); + Serial.print("Start smoke test "); Serial.println(txt_msg[msg]); errors += test_01(); Serial1.write(TEST_1_FINISHED); break; case START_TEST_2: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); // Verify data transfer - send predefined data sequence and wait for confirmation from counterpart // Receive predefined data sequence and send confirmation to counterpart // Repeat test for all digital modes + Serial.print("Verify data transfer "); Serial.println(txt_msg[msg]); + errors += test_02(); + Serial1.write(TEST_2_FINISHED); break; case START_TEST_3: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); // Verify buffers - proper available returns, peek and flush + Serial.print("Verify buffers "); Serial.println(txt_msg[msg]); + //errors += test_03(); + Serial1.write(TEST_3_FINISHED); break; case START_TEST_4: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); // Verify callbacks + Serial.print("Verify callbacks "); Serial.println(txt_msg[msg]); + //errors += test_04(); + Serial1.write(TEST_4_FINISHED); break; case START_TEST_5: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); // Verify pin setups + Serial.print("Verify pin setups "); Serial.println(txt_msg[msg]); + //errors += test_05(); + Serial1.write(TEST_5_FINISHED); break; case START_TEST_6: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); // Verify ADC and DAC + Serial.print("Verify ADC and DAC "); Serial.println(txt_msg[msg]); + //errors += test_06(); + Serial1.write(TEST_6_FINISHED); break; case START_TEST_7: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + // duplex + Serial.print("Verify duplex "); Serial.println(txt_msg[msg]); + //errors += test_07(); + Serial1.write(TEST_7_FINISHED); break; +/* case START_TEST_8: Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + errors += test_08(); + Serial1.write(TEST_8_FINISHED); break; case START_TEST_9: Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + + errors += test_09(); + Serial1.write(TEST_9_FINISHED); break; case START_TEST_10: Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); + errors += test_10(); + Serial1.write(TEST_10_FINISHED); break; +*/ case NOP: break; default: From cd2c8c809449164a01c1e02c1fa6eb21923f24b4 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 8 Dec 2021 14:10:35 +0100 Subject: [PATCH 81/94] Work In Progress on data fix function --- libraries/I2S/src/I2S.cpp | 133 +++++++++++++++++++++++++++++++------- libraries/I2S/src/I2S.h | 2 + 2 files changed, 111 insertions(+), 24 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 3f266e57ad7..242f46b1161 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -59,7 +59,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), - _i2s_dma_buffer_size(1024), + _i2s_dma_buffer_size(128), _driveClock(true), _peek_buff(0), _peek_buff_valid(false), @@ -107,7 +107,6 @@ int I2SClass::_installDriver(){ if(_driveClock){ i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER); }else{ - // TODO there will much more work with slave mode i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE); } @@ -228,12 +227,8 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){ } int I2SClass::begin(int mode, int bitsPerSample){ - log_e("ERROR I2SClass::begin Audio in Slave mode is not implemented for ESP\n\ - Note: If it is NOT your intention to initialize in slave mode, you are probably missing parameter - see the declaration below\n\ - \tint I2SClass::begin(int mode, long sampleRate, int bitsPerSample)"); - return 0; // ERR // slave mode (not driving clock and frame select pin - input) - //return begin(mode, 0, bitsPerSample, false); + return begin(mode, 0, bitsPerSample, false); } int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){ @@ -545,11 +540,11 @@ int I2SClass::read(){ return sample.b8; } else { _give_if_top_call(); - return 0; + return 0; // sample value } } // if(_initialized) _give_if_top_call(); - return 0; + return 0; // sample value } int I2SClass::read(void* buffer, size_t size){ @@ -585,13 +580,14 @@ int I2SClass::read(void* buffer, size_t size){ _give_if_top_call(); return item_size; }else{ + log_w("input buffer is empty - timed out"); _give_if_top_call(); - return 0; + return 0; // 0 Bytes read / ERR } // tmp buffer not NULL ? } // ring buffer not NULL ? } // if(_initialized) _give_if_top_call(); - return 0; + return 0; // 0 Bytes read / ERR } /* @@ -682,7 +678,6 @@ size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ flush(); } if(_output_ring_buffer != NULL){ - log_d("try to send %d B to ring buffer", size); if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 0)){ _give_if_top_call(); return size; @@ -728,14 +723,11 @@ void I2SClass::flush(){ if(_initialized){ const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); size_t item_size = 0; - size_t bytes_written; void *item = NULL; if(_output_ring_buffer != NULL){ item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, 0, single_dma_buf); if (item != NULL){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); - if(item_size != bytes_written){ - } + _fix_and_write(item, item_size, NULL); vRingbufferReturnItem(_output_ring_buffer, item); } } @@ -825,12 +817,18 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ static size_t bytes_written; if(prev_item_valid){ // use item from previous round - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, prev_item+prev_item_offset, prev_item_size, &bytes_written, 0); - if(prev_item_size == bytes_written){ - prev_item_valid = false; - } // write size check - prev_item_offset = bytes_written; - prev_item_size -= bytes_written; + uint8_t *tmp_buff = (uint8_t*)malloc(prev_item_size); + if(tmp_buff == NULL){ + log_e("Could not allocate memory before write"); + }else{ + _fix_and_write(tmp_buff, prev_item_size, &bytes_written); + if(prev_item_size == bytes_written){ + prev_item_valid = false; + } // write size check + prev_item_offset = bytes_written; + prev_item_size -= bytes_written; + free(tmp_buff); + } // tmp_buff alloc ok } // prev_item_valid if(_output_ring_buffer != NULL && (_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf)){ // fill up the I2S DMA buffer @@ -839,7 +837,7 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ //if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); if (item != NULL){ - esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, item, item_size, &bytes_written, 0); + _fix_and_write(item, item_size, &bytes_written); if(item_size != bytes_written){ // save item that was not written correctly for later memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); prev_item_size = item_size - bytes_written; @@ -863,7 +861,11 @@ void I2SClass::_rx_done_routine(){ uint8_t *_inputBuffer = (uint8_t*)malloc(_i2s_dma_buffer_size*4); size_t avail = xRingbufferGetCurFreeSize(_input_ring_buffer); if(avail > 0){ - esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + esp_err_t ret = esp_i2s::i2s_read((esp_i2s::i2s_port_t) _deviceIndex, _inputBuffer, avail <= single_dma_buf ? avail : single_dma_buf, (size_t*) &bytes_read, 0); + if(ret != ESP_OK){ + log_w("i2s_read returned with error %d", ret); + } + _post_read_data_fix(_inputBuffer, &bytes_read); } if(bytes_read > 0){ // when read more than 0, then send to ring buffer @@ -920,6 +922,89 @@ void I2SClass::_give_if_top_call(){ } } + +// Fixes data in-situ received from esp i2s driver. After fixing they reflect what was on the bus. +// input - bytes as received from i2s_read - this serves as input and output buffer +// size - number of bytes (this may be changed during operation) +void I2SClass::_post_read_data_fix(void *input, size_t *size){ + ulong dst_ptr = 0; + switch(_bitsPerSample){ + case 8: + for(int i = 0; i < *size; i+=4){ + ((uint8_t*)input)[dst_ptr++] = ((uint8_t*)input)[i+3]; + ((uint8_t*)input)[dst_ptr++] = ((uint8_t*)input)[i+1]; + } + *size /= 2; + break; + case 16: + uint16_t tmp; + for(int i = 0; i < *size/2; i+=2){ + tmp = ((uint16_t*)input)[i]; + ((uint16_t*)input)[dst_ptr++] = ((uint16_t*)input)[i+1]; + ((uint16_t*)input)[dst_ptr++] = tmp; + } + break; + default: ; // Do nothing + } // switch +} + +// Prepares data and writes them to IDF i2s driver. +// This counters possible bug in ESP IDF I2S driver +// output - bytes to be sent (input and output for this function) +// size - number of bytes in original buffer (this may change) +// bytes_written - number of bytes written by i2s_write +void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written){ + ulong src_ptr = 0; + uint8_t* buff; + switch(_bitsPerSample){ + case 8: + //log_d("For tmp_buff alocate Bytes %d; ESP.getFreeHeap() = %d", size, ESP.getFreeHeap()); + //vTaskDelay(500); + buff = (uint8_t*)malloc(size *2); + if(buff == NULL){ + log_d("mallock error"); + if(bytes_written != NULL){ *bytes_written = 0; } + return; + } + //log_d("allock ok (ESP.getFreeHeap() = %d)", ESP.getFreeHeap()); + //vTaskDelay(500); + for(int i = 0; i < size; i+=2){ + ((uint16_t*)buff)[i+1] = (uint16_t)((uint8_t*)output)[src_ptr++]; + ((uint16_t*)buff)[i] = (uint16_t)((uint8_t*)output)[src_ptr++]; + } + break; + case 16: + buff = (uint8_t*)malloc(size); + if(buff == NULL){ + log_e("malloc error"); + if(bytes_written != NULL){ *bytes_written = 0; } + return; + } + for(int i = 0; i < size; i += 2){ + ((uint16_t*)buff)[i] = ((uint16_t*)output)[i+1]; // [1] <- [0] + ((uint16_t*)buff)[i+1] = ((uint16_t*)output)[i]; // [0] <- [1] + } + break; + default: ; // Do nothing + } // switch + + size_t _bytes_written; + esp_err_t ret = esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buff, sizeof(buff), &_bytes_written, 0); + if(ret != ESP_OK){ + log_w("Error writing data to i2s - function returned with err code %d", ret); + } + if(ret == ESP_OK && sizeof(buff) != _bytes_written){ + log_w("Error writing data to i2s - written %d B instead of requested %d B", _bytes_written, sizeof(buff)); + } + log_d("free buff"); + vTaskDelay(500); + free(buff); + if(bytes_written != NULL){ + *bytes_written = _bytes_written; + } +} + + #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ switch(gpio_num){ diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 20c4ab57663..794a9581e21 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -175,6 +175,8 @@ class I2SClass : public Stream uint16_t _nesting_counter; void _take_if_not_holding(); void _give_if_top_call(); + void _post_read_data_fix(void *input, size_t *size); + void _fix_and_write(void *output, size_t size, size_t *bytes_written); void (*_onTransmit)(void); void (*_onReceive)(void); From 395fb082d6cb373771f0f4104b4bd76f20a006c3 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 4 Jan 2022 09:46:55 +0100 Subject: [PATCH 82/94] Fixed WDT --- libraries/I2S/src/I2S.cpp | 118 ++++++++++++++++++++------------------ libraries/I2S/src/I2S.h | 2 +- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 242f46b1161..28c45da56c4 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -59,7 +59,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), - _i2s_dma_buffer_size(128), + _i2s_dma_buffer_size(128), // Number of frames in each DMA buffer. Frame size = number of channels * bits (or Bytes) per sample _driveClock(true), _peek_buff(0), _peek_buff_valid(false), @@ -149,6 +149,7 @@ int I2SClass::_installDriver(){ .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _i2s_dma_buffer_size, .use_apll = false + //left_align = true // will it fix ? - no }; // Install and start i2s driver while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ @@ -228,10 +229,12 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){ int I2SClass::begin(int mode, int bitsPerSample){ // slave mode (not driving clock and frame select pin - input) + log_d("begin in slave mode"); return begin(mode, 0, bitsPerSample, false); } int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){ + log_d("starting "); _take_if_not_holding(); if(_initialized){ log_e("ERROR I2SClass::begin() object already initialized! Call I2S.end() to deinitialize"); @@ -244,7 +247,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock _bitsPerSample = bitsPerSample; if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { - log_e("I2S.begin: unexpected _state (%d)",_state); + log_e("Error: unexpected _state (%d)",_state); _give_if_top_call(); return 0; // ERR } @@ -262,13 +265,13 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock break; default: // invalid mode - log_e("ERROR I2SClass::begin() unknown mode"); + log_e("ERROR: unknown mode"); _give_if_top_call(); return 0; // ERR } if(!_installDriver()){ - log_e("ERROR I2SClass::begin() failed to install driver"); + log_e("ERROR: failed to install driver"); end(); _give_if_top_call(); return 0; // ERR @@ -278,12 +281,13 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ - log_e("ERROR I2SClass::begin could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); + log_e("ERROR: could not create one or both internal buffers. Requested size = %d\n", _buffer_byte_size); _give_if_top_call(); return 0; // ERR } if(!_createCallbackTask()){ + log_e("ERROR: failed to create callback task"); end(); _give_if_top_call(); return 0; // ERR @@ -650,7 +654,8 @@ size_t I2SClass::write_blocking(const void *buffer, size_t size){ } // _state ? if(_output_ring_buffer != NULL){ - if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, portMAX_DELAY)){ + int ret = xRingbufferSend(_output_ring_buffer, buffer, size, portMAX_DELAY); + if(pdTRUE == ret){ _give_if_top_call(); return size; }else{ @@ -661,7 +666,9 @@ size_t I2SClass::write_blocking(const void *buffer, size_t size){ } // ring buffer not NULL ? } // if(_initialized) return 0; + log_w("I2S not initialized"); _give_if_top_call(); + return 0; } // non-blocking version of write @@ -727,7 +734,7 @@ void I2SClass::flush(){ if(_output_ring_buffer != NULL){ item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, 0, single_dma_buf); if (item != NULL){ - _fix_and_write(item, item_size, NULL); + _fix_and_write(item, item_size); vRingbufferReturnItem(_output_ring_buffer, item); } } @@ -814,43 +821,37 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ static size_t prev_item_size = 0; static void *item = NULL; static int prev_item_offset = 0; - static size_t bytes_written; + static size_t bytes_written = 0; if(prev_item_valid){ // use item from previous round - uint8_t *tmp_buff = (uint8_t*)malloc(prev_item_size); - if(tmp_buff == NULL){ - log_e("Could not allocate memory before write"); - }else{ - _fix_and_write(tmp_buff, prev_item_size, &bytes_written); - if(prev_item_size == bytes_written){ - prev_item_valid = false; - } // write size check - prev_item_offset = bytes_written; - prev_item_size -= bytes_written; - free(tmp_buff); - } // tmp_buff alloc ok + _fix_and_write(prev_item+prev_item_offset, prev_item_size, &bytes_written); + if(prev_item_size == bytes_written){ + prev_item_valid = false; + } // write size check + prev_item_offset = bytes_written; + prev_item_size -= bytes_written; } // prev_item_valid if(_output_ring_buffer != NULL && (_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= single_dma_buf)){ // fill up the I2S DMA buffer bytes_written = 0; item_size = 0; - //if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer - item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(1000), single_dma_buf); - if (item != NULL){ - _fix_and_write(item, item_size, &bytes_written); - if(item_size != bytes_written){ // save item that was not written correctly for later - memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); - prev_item_size = item_size - bytes_written; - prev_item_offset = 0; - prev_item_valid = true; - } // save item that was not written correctly for later - vRingbufferReturnItem(_output_ring_buffer, item); - } // Check received item - } // don't read from almost empty buffer - - if(_onTransmit){ - _onTransmit(); - } // user callback + if(_buffer_byte_size - xRingbufferGetCurFreeSize(_output_ring_buffer) >= _i2s_dma_buffer_size*(_bitsPerSample/8)){ // don't read from almost empty buffer + item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, pdMS_TO_TICKS(0), single_dma_buf); + if (item != NULL){ + _fix_and_write(item, item_size, &bytes_written); + if(item_size != bytes_written){ // save item that was not written correctly for later + memcpy(prev_item, (void*)&((uint8_t*)item)[bytes_written], item_size-bytes_written); + prev_item_size = item_size - bytes_written; + prev_item_offset = 0; + prev_item_valid = true; + } // save item that was not written correctly for later + vRingbufferReturnItem(_output_ring_buffer, item); + } // Check received item + } // don't read from almost empty buffer + if(_onTransmit){ + _onTransmit(); + } // user callback + } // fill up the I2S DMA buffer } void I2SClass::_rx_done_routine(){ @@ -952,55 +953,58 @@ void I2SClass::_post_read_data_fix(void *input, size_t *size){ // This counters possible bug in ESP IDF I2S driver // output - bytes to be sent (input and output for this function) // size - number of bytes in original buffer (this may change) -// bytes_written - number of bytes written by i2s_write -void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written){ +// bytes_written - number of bytes used from original buffer +// actual_bytes_written - number of bytes written by i2s_write after fix +void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written, size_t *actual_bytes_written){ + long start = millis(); ulong src_ptr = 0; uint8_t* buff; + size_t buff_size = size; switch(_bitsPerSample){ case 8: - //log_d("For tmp_buff alocate Bytes %d; ESP.getFreeHeap() = %d", size, ESP.getFreeHeap()); - //vTaskDelay(500); - buff = (uint8_t*)malloc(size *2); + buff_size = size *2; + buff = (uint8_t*)calloc(buff_size, sizeof(uint8_t)); if(buff == NULL){ - log_d("mallock error"); + log_e("callock error"); if(bytes_written != NULL){ *bytes_written = 0; } return; } - //log_d("allock ok (ESP.getFreeHeap() = %d)", ESP.getFreeHeap()); - //vTaskDelay(500); - for(int i = 0; i < size; i+=2){ - ((uint16_t*)buff)[i+1] = (uint16_t)((uint8_t*)output)[src_ptr++]; - ((uint16_t*)buff)[i] = (uint16_t)((uint8_t*)output)[src_ptr++]; + for(int i = 0; i < buff_size ; i+=4){ + ((uint8_t*)buff)[i+3] = (uint16_t)((uint8_t*)output)[src_ptr++]; + ((uint8_t*)buff)[i+1] = (uint16_t)((uint8_t*)output)[src_ptr++]; } break; case 16: - buff = (uint8_t*)malloc(size); + buff = (uint8_t*)malloc(buff_size); if(buff == NULL){ log_e("malloc error"); if(bytes_written != NULL){ *bytes_written = 0; } return; } - for(int i = 0; i < size; i += 2){ + for(int i = 0; i < size/2; i += 2 ){ ((uint16_t*)buff)[i] = ((uint16_t*)output)[i+1]; // [1] <- [0] ((uint16_t*)buff)[i+1] = ((uint16_t*)output)[i]; // [0] <- [1] } break; + case 32: + buff = (uint8_t*)output; default: ; // Do nothing } // switch size_t _bytes_written; - esp_err_t ret = esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buff, sizeof(buff), &_bytes_written, 0); + esp_err_t ret = esp_i2s::i2s_write((esp_i2s::i2s_port_t) _deviceIndex, buff, buff_size, &_bytes_written, 0); // fixed if(ret != ESP_OK){ - log_w("Error writing data to i2s - function returned with err code %d", ret); + log_e("Error: writing data to i2s - function returned with err code %d", ret); } - if(ret == ESP_OK && sizeof(buff) != _bytes_written){ - log_w("Error writing data to i2s - written %d B instead of requested %d B", _bytes_written, sizeof(buff)); + if(ret == ESP_OK && buff_size != _bytes_written){ + log_w("Warning: writing data to i2s - written %d B instead of requested %d B", _bytes_written, buff_size); } - log_d("free buff"); - vTaskDelay(500); free(buff); if(bytes_written != NULL){ - *bytes_written = _bytes_written; + *bytes_written = _bitsPerSample == 8 ? _bytes_written/2 : _bytes_written; + } + if(actual_bytes_written != NULL){ + *actual_bytes_written = _bytes_written; } } diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 794a9581e21..2727f875c0a 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -176,7 +176,7 @@ class I2SClass : public Stream void _take_if_not_holding(); void _give_if_top_call(); void _post_read_data_fix(void *input, size_t *size); - void _fix_and_write(void *output, size_t size, size_t *bytes_written); + void _fix_and_write(void *output, size_t size, size_t *bytes_written = NULL, size_t *actual_bytes_written = NULL); void (*_onTransmit)(void); void (*_onReceive)(void); From 6420fad8e10238b1fa4b19715f44a546dfe40928 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 6 Jan 2022 09:24:26 +0100 Subject: [PATCH 83/94] Removed unsupported examples and unit tests --- .../I2S/examples/Callbacks/.skip.esp32c3 | 0 .../I2S/examples/Callbacks/Callbacks.ino | 126 ---------- libraries/I2S/examples/Testing/.skip.esp32c3 | 0 libraries/I2S/examples/Testing/Testing.ino | 160 ------------ .../I2S/examples/thread_safety/.skip.esp32c3 | 0 .../examples/thread_safety/thread_safety.ino | 186 -------------- libraries/I2S/unit_tests/README.md | 43 ---- libraries/I2S/unit_tests/cli_monitor.sh | 3 - .../test_counterpart/02_data_transfer.ino | 233 ------------------ .../test_counterpart/test_counterpart.ino | 43 ---- .../test_counterpart/unit_tests_common.cpp | 1 - .../test_counterpart/unit_tests_common.h | 1 - .../I2S/unit_tests/unit_tests_common.cpp | 33 --- libraries/I2S/unit_tests/unit_tests_common.h | 79 ------ .../unit_under_test/01_smoke_test.ino | 82 ------ .../unit_under_test/02_data_transfer.ino | 128 ---------- .../unit_under_test/03_buffer_test.ino | 0 .../unit_under_test/unit_tests_common.cpp | 1 - .../unit_under_test/unit_tests_common.h | 1 - .../unit_under_test/unit_under_test.ino | 112 --------- 20 files changed, 1232 deletions(-) delete mode 100644 libraries/I2S/examples/Callbacks/.skip.esp32c3 delete mode 100644 libraries/I2S/examples/Callbacks/Callbacks.ino delete mode 100644 libraries/I2S/examples/Testing/.skip.esp32c3 delete mode 100644 libraries/I2S/examples/Testing/Testing.ino delete mode 100644 libraries/I2S/examples/thread_safety/.skip.esp32c3 delete mode 100644 libraries/I2S/examples/thread_safety/thread_safety.ino delete mode 100644 libraries/I2S/unit_tests/README.md delete mode 100755 libraries/I2S/unit_tests/cli_monitor.sh delete mode 100644 libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino delete mode 100644 libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino delete mode 120000 libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp delete mode 120000 libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h delete mode 100644 libraries/I2S/unit_tests/unit_tests_common.cpp delete mode 100644 libraries/I2S/unit_tests/unit_tests_common.h delete mode 100644 libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino delete mode 100644 libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino delete mode 100644 libraries/I2S/unit_tests/unit_under_test/03_buffer_test.ino delete mode 120000 libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp delete mode 120000 libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h delete mode 100644 libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino diff --git a/libraries/I2S/examples/Callbacks/.skip.esp32c3 b/libraries/I2S/examples/Callbacks/.skip.esp32c3 deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libraries/I2S/examples/Callbacks/Callbacks.ino b/libraries/I2S/examples/Callbacks/Callbacks.ino deleted file mode 100644 index 5149842fe90..00000000000 --- a/libraries/I2S/examples/Callbacks/Callbacks.ino +++ /dev/null @@ -1,126 +0,0 @@ -/* - This example is only for ESP devices. - - This example demonstrates usage of callback functions and duplex operation. - Callback functions allow you to perform audio events pseudo parallel to your main loop. - This way you no longer need to poll for reading or writing data from/to I2S module. - - Unlike Arduino, ESP allows you to operate input and output simultaneously on separate pins. - -Hardware: - 1. ESP board with at least 4 GPIOs available - 2. I2S mirophone (for example this one https://www.adafruit.com/product/3421) - 3. I2S decoder (for example this one https://www.adafruit.com/product/3678) - 4. Headphones, or speaker(s) to be connected into the decoder - 5. Some connecting wires, USB cable and optionally a breadboard - - Wiring: - Note: If you need to use other than default pins you can change the default definition below this comment block - 1. Connect pins of both the microphone and decoder to common SCK and FS pins on ESP - 1.a SCK (Source Clock, or Bit Clock) connects by default to pin 5 - 1.b FS (Frame Select, or Left-Right Select) connects by default to pin 25 - 2. Connect data pin of your microphone to pin 35 - 3. Connect data pin of your decoder to pin 26 - 4. Connect power pins and other remaining pins of your modules according to their specific instructions - 5. Connect headphones/speaker(s) to decoder - 6. Connect ESP board to your computer via USB cable - - Steps to run: - 1. Select target board: - Tools -> Board -> ESP32 Arduino -> your board - 2. Upload sketch - Press upload button (arrow in top left corner) - When you see in console line like this: "Connecting........_____.....__" - If loading doesn't start automatically, you may need to press and hold Boot - button and press EN button shortly. Now you can release both buttons. - You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. - If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. - 3. Open plotter - Tools -> Serial Plotter - 4. Enjoy - Listen to generated square wave signal, while observing the wave signal recorded by your microphone - both at the same time thanks to ESP duplex ability. - - Tip: - Install ArduinoSound library and discover extended functionality of I2S - you can try ESP WiFi telephone, Record on SD card and Play back from it... - -Created by Tomas Pilny -on 23rd June 2021 -*/ - -// If you need to change any of the default pins, simply uncomment chosen line and change the pin number -/* -#define PIN_I2S_SCK 5 -#define PIN_I2S_FS 25 -#define PIN_I2S_SD 35 -#define PIN_I2S_SD_OUT 26 -*/ - -#include -const int sampleRate = 16000; // sample rate in Hz -const int bitsPerSample = 16; - -// code from SimpleTone example -const int frequency = 1250; // frequency of square wave in Hz -const int amplitude = 32767; // amplitude of square wave - -const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave - -short out_sample = amplitude; // current sample value -int count = 0; - -void outputCallback(){ - - if (count % halfWavelength == 0) { - // invert the sample every half wavelength count multiple to generate square wave - out_sample = -1 * out_sample; - } - - Serial.printf("write %d\n", out_sample); // only for debug - - // write the same sample twice, once for left and once for the right channel - I2S.write(out_sample); - I2S.write(out_sample); - - // increment the counter for the next sample - count++; -} - -// code from InputSerialPlotter example -void inputCallback(){ - // read a sample - int in_sample = I2S.read(); - - if (in_sample) { - // if it's non-zero print value to serial - Serial.println(in_sample); - } -} - -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 32-bits per sample - if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, bitsPerSample)) { - Serial.println("Failed to initialize I2S!"); - while (1) delay(100); // do nothing - } - I2S.setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); - - // Register our callback functions - I2S.onTransmit(outputCallback); // Function outputCallback will be called each time I2S finishes transmit operation (audio output) - I2S.onReceive(inputCallback); // Function inputCallback will be called each time I2S finishes receive operation (audio input) - Serial.println("Callbacks example setup done."); -} - -void loop() { - // loop task remains free for other work - delay(10); // Let the FreeRTOS reset the watchDogTimer -} diff --git a/libraries/I2S/examples/Testing/.skip.esp32c3 b/libraries/I2S/examples/Testing/.skip.esp32c3 deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libraries/I2S/examples/Testing/Testing.ino b/libraries/I2S/examples/Testing/Testing.ino deleted file mode 100644 index 8984739b871..00000000000 --- a/libraries/I2S/examples/Testing/Testing.ino +++ /dev/null @@ -1,160 +0,0 @@ -/* - 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. - - Circuit: - * Arduino/Genuino Zero, MKR family and Nano 33 IoT - * MAX08357: - * GND connected GND - * VIN connected 5V - * LRC connected to pin 0 (Zero) or 3 (MKR), A2 (Nano) or 25 (ESP32) - * BCLK connected to pin 1 (Zero) or 2 (MKR), A3 (Nano) or 5 (ESP32) - * DIN connected to pin 9 (Zero) or A6 (MKR), 4 (Nano) or 26 (ESP32) - - created 17 November 2016 - by Sandeep Mistry - */ - -#define PLAY -//#define PLOT - -#include -const int frequency = 440; // frequency of square wave in Hz -const int amplitude = 500; // amplitude of square wave -const long sampleRate[] = {8000, 11025, 16000, 22050, 24000, 32000, 44100}; -const int bitsPerSample[] = {8, 16, 24, 32}; - -//const i2s_mode_t mode = I2S_PHILIPS_MODE; -//const i2s_mode_t mode = I2S_RIGHT_JUSTIFIED_MODE; -//const i2s_mode_t mode = I2S_LEFT_JUSTIFIED_MODE; -//const i2s_mode_t mode = ADC_DAC_MODE; -const i2s_mode_t mode = PDM_STEREO_MODE; -//const i2s_mode_t mode = PDM_MONO_MODE; - -void test() -{ - Serial.println("Try double end()"); - I2S.end(); - I2S.end(); - Serial.println(""); - - Serial.println("Try double begin()"); - Serial.printf("First begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); - Serial.printf("Second begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); - I2S.end(); - Serial.println(""); - - Serial.println("Try normal begin -> end twice"); - Serial.printf("First begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); - I2S.end(); - Serial.printf("Second begin() returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); - I2S.end(); - Serial.println(""); - - Serial.println("Try begin with all modes"); - Serial.printf("begin(I2S_PHILIPS_MODE) returned %d\n", I2S.begin(I2S_PHILIPS_MODE, 44100, 32)); - I2S.end(); - Serial.printf("begin(I2S_RIGHT_JUSTIFIED_MODE) returned %d\n", I2S.begin(I2S_RIGHT_JUSTIFIED_MODE, 44100, 32)); - I2S.end(); - Serial.printf("begin(I2S_LEFT_JUSTIFIED_MODE) returned %d\n", I2S.begin(I2S_LEFT_JUSTIFIED_MODE, 44100, 32)); - I2S.end(); - Serial.printf("begin(ADC_DAC_MODE) returned %d\n", I2S.begin(ADC_DAC_MODE, 44100, 16)); - I2S.end(); - Serial.printf("begin(PDM_STEREO_MODE) returned %d\n", I2S.begin(PDM_STEREO_MODE, 44100, 32)); - I2S.end(); - Serial.printf("begin(PDM_MONO_MODE) returned %d\n", I2S.begin(PDM_MONO_MODE, 44100, 32)); - I2S.end(); - Serial.println("End of testing"); - Serial.println(""); -} - -void setup() { - Serial.begin(115200); - Serial.println("I2S testing"); - //test(); -} - -int halfWavelength; -void play_a_while(long seconds){ - long start_time = millis(); - long curr_time = 0; - int count = 0; - short sample = amplitude; - while(curr_time - start_time < seconds*1000){ - //Serial.print("curr_time = "); Serial.print(curr_time); Serial.print("; start_time = "); Serial.print(start_time); Serial.print("; seconds*1000 = "); Serial.println(seconds*1000); - if (count % halfWavelength == 0 ){ - // invert the sample every half wavelength count multiple to generate square wave - sample = -1 * sample; - } - - if(mode == I2S_PHILIPS_MODE || mode == ADC_DAC_MODE || mode == PDM_STEREO_MODE){ // write the same sample twice, once for Right and once for Left channel - I2S.write(sample); // Right channel - I2S.write(sample); // Left channel - }else if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE || mode == PDM_MONO_MODE){ - // write the same only once - it will be automatically copied to the other channel - I2S.write(sample); - } - - // increment the counter for the next sample - count++; - - //Serial.print("Played "); Serial.print((curr_time-start_time)/1000); Serial.print(" / "); Serial.print(seconds); Serial.println(" s"); - - curr_time = millis(); - } -} - -void plot_a_while(long seconds){ - long start_time = millis(); - long curr_time = 0; - while(curr_time - start_time < seconds*1000){ - //Serial.print("curr_time = "); Serial.print(curr_time); Serial.print("; start_time = "); Serial.print(start_time); Serial.print("; seconds*1000 = "); Serial.println(seconds*1000); - // read a sample - int sample = I2S.read(); - //Serial.println(sample); - - if (sample && sample != -1 && sample != 1) { - Serial.println(sample); - } - } -} - -void loop() { - for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ - #ifdef PLAY - if(mode == ADC_DAC_MODE && bitsPerSample[bps] != 16){ - continue; - } - #endif - - for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ - #ifdef PLAY - Serial.print("Setting up bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); - #endif - if (!I2S.begin(mode, sampleRate[sr], bitsPerSample[bps])) { - Serial.println("Failed to initialize I2S!"); - }else{ - halfWavelength = (sampleRate[sr] / frequency); // half wavelength of square wave - #ifdef PLAY - Serial.println("Setup done. Starting playback"); - play_a_while(3); - Serial.println("Playback finished"); - #endif - - #ifdef PLOT - plot_a_while(5); - #endif - I2S.end(); - } // begin ok - #ifdef PLAY - Serial.println(""); - #endif - delay(1000); - } // sr - #ifdef PLAY - Serial.println("==============================================="); - #endif - delay(3000); - } // bps -} diff --git a/libraries/I2S/examples/thread_safety/.skip.esp32c3 b/libraries/I2S/examples/thread_safety/.skip.esp32c3 deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libraries/I2S/examples/thread_safety/thread_safety.ino b/libraries/I2S/examples/thread_safety/thread_safety.ino deleted file mode 100644 index 6ed7fad3bed..00000000000 --- a/libraries/I2S/examples/thread_safety/thread_safety.ino +++ /dev/null @@ -1,186 +0,0 @@ -/* - This example is only for ESP devices. - - This sketch verify I2S lib thread safety = when multiple threads are accessing same object - it should not corrupt object state, lead to racing conditions nor crashing. - -Hardware: - 1. Any ESP32 or ESP32-S2 - - Steps to run: - 1. Select target board: - Tools -> Board -> ESP32 Arduino -> your board - 2. Upload sketch - Press upload button (arrow in top left corner) - When you see in console line like this: "Connecting........_____.....__" - If loading doesn't start automatically, you may need to press and hold Boot - button and press EN button shortly. Now you can release both buttons. - You should see lines like this: "Writing at 0x00010000... (12 %)" with rising percentage on each line. - If this fails, try the board buttons right after pressing upload button, or reconnect the USB cable. - 3. Open monitor - Tools -> Serial Monitor - 4. Observe - No crash should occur - -Created by Tomas Pilny -on 30th Sep 2021 -*/ - -#include -const int sampleRate = 16000; // sample rate in Hz -const int bitsPerSample = 16; -TaskHandle_t _Task1Handle; -TaskHandle_t _Task2Handle; -TaskHandle_t _Task3Handle; -TaskHandle_t _Task4Handle; -TaskHandle_t _Task5Handle; - - -void task1(void*){ - while(true){ - I2S.setAllPins(); // apply default pins - if(I2S.available()){ - int foo = I2S.read(); - } - delay(1); - } // infinite loop -} - -void task2(void*){ - while(true){ - I2S.setAllPins(5, 25, 26, 27); - I2S.write(123); - delay(1); - } // infinite loop -} - -void task3(void*){ - // Annoying kid: "What does this switch do?"" - while(true){ - I2S.end(); // flip - delay(100); - if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 16)){ // flop - log_e("Could not start I2S"); - while (1) {delay(100);} // halt - } - delay(100); - } // infinite loop -} - -void task4(void*){ - const int buf_size = 64; - uint8_t buffer [buf_size]; - while(true){ - if(I2S.available() >= buf_size){ - int read = I2S.read(buffer, buf_size); - if(read != buf_size){ - log_e("task4: did not read complete buffer"); - } - } - delay(5); - } // infinite loop -} - -void task5(void*){ - int i2s_buffer_size; - while(true){ - i2s_buffer_size = I2S.getBufferSize(); - delay(1); - if(!I2S.setBufferSize(i2s_buffer_size + 1)){ - // unsupported buffer size requested -> halve the size - i2s_buffer_size /= 2; - } - delay(25); - } // infinite loop -} - -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 - } - - if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 16)){ - log_e("Could not start I2S"); - while (1) {delay(100);} // halt - } - - _Task1Handle = NULL; - xTaskCreate( - task1, // Function to implement the task - "task1", // Name of the task - 2000, // Stack size in words - NULL, // Task input parameter - 1, // Priority of the task - &_Task1Handle // Task handle. - ); - if(_Task1Handle == NULL){ - log_e("Could not create callback task 1"); - while (1) {;} // halt - } - - _Task2Handle = NULL; - xTaskCreate( - task2, // Function to implement the task - "task2", // Name of the task - 2000, // Stack size in words - NULL, // Task input parameter - 1, // Priority of the task - &_Task2Handle // Task handle. - ); - if(_Task2Handle == NULL){ - log_e("Could not create callback task 2"); - while (1) {;} // halt - } - - _Task3Handle = NULL; - xTaskCreate( - task3, // Function to implement the task - "task3", // Name of the task - 2000, // Stack size in words - NULL, // Task input parameter - 1, // Priority of the task - &_Task3Handle // Task handle. - ); - if(_Task3Handle == NULL){ - log_e("Could not create callback task 3"); - while (1) {;} // halt - } - - _Task4Handle = NULL; - xTaskCreate( - task4, // Function to implement the task - "task4", // Name of the task - 2000, // Stack size in words - NULL, // Task input parameter - 1, // Priority of the task - &_Task4Handle // Task handle. - ); - if(_Task4Handle == NULL){ - log_e("Could not create callback task 4"); - while (1) {;} // halt - } - - _Task5Handle = NULL; - xTaskCreate( - task5, // Function to implement the task - "task5", // Name of the task - 2000, // Stack size in words - NULL, // Task input parameter - 1, // Priority of the task - &_Task5Handle // Task handle. - ); - if(_Task5Handle == NULL){ - log_e("Could not create callback task 5"); - while (1) {;} // halt - } - -} - -void loop() { - // loop task remains free for other work - delay(10); // Let the FreeRTOS reset the watchDogTimer -} diff --git a/libraries/I2S/unit_tests/README.md b/libraries/I2S/unit_tests/README.md deleted file mode 100644 index 701986d9a6c..00000000000 --- a/libraries/I2S/unit_tests/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# I2S unit tests - -Set of both codes and ESPs act as automated unit test to verify Arduino-like I2S lib. -These tests can be run after every commit to catch potential bugs. - -unit_tests_ino contains sketch using Arduino-like I2S library. This sketch is intended to be flashed onto ESP32 (or Arduino supporting I2S) and connected to another ESP32 using code from unit_tests_idf. - -unit_tests_idf contains IDF code acting as a counterpart for Arduino-like I2S lib. - -### Hardware: -- 2 pcs ESP32, or 1 ESP32 and Arduino supporting I2S -- 7 wires (connecting I2S and I2C + GND) -- 2 USB cables for ESP connections -- Optional: Logic analyzer + oscilloscope -- Optional: breadboard - -### Setup: -- Connect pins: - -Chose if you want to test ESP32 or Arduino and connect one of those to second ESP32 - - Arduino MKR-Zero | ESP32 | ESP32 - ino code | ino code | idf code | note - ? | GND | GND | - ? | 14 | 14 | I2S CLK - ? | 25 | 25 | I2S WS - ? | 35 | 26 | I2S ino-input <- idf-output - ? | 26 | 35 | I2S ino-output -> idf-input - 1(TX) | 21 | 22 | UART ino-TX -> idf-RX - 0(RX) | 22 | 21 | UART ino-RX <- idf-TX -- Connect USB cables and flash -- Boards should detect each other via I2C and start testing automatically and report partial results and overall result when finished. -- Connect logic analyzer on I2S bus - -### Build IDF counterpart -- Download [esp-idf](https://github.com/espressif/esp-idf) and [install](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html#step-3-set-up-the-tools) -- Build with `idf.py build` -- Flash with `idf.py -p /dev/ttyUSB0 monitor flash` - - -### TODO -- ADC and DAC connections and verification -- Pin change - connect additional set of I2S bus to different pins to verify pin change works properly \ No newline at end of file diff --git a/libraries/I2S/unit_tests/cli_monitor.sh b/libraries/I2S/unit_tests/cli_monitor.sh deleted file mode 100755 index b55635699cf..00000000000 --- a/libraries/I2S/unit_tests/cli_monitor.sh +++ /dev/null @@ -1,3 +0,0 @@ -echo "Using device /dev/ttyUSB$1" -stty -F /dev/ttyUSB$1 raw 115200 -cat /dev/ttyUSB$1 diff --git a/libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino b/libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino deleted file mode 100644 index 76c93fa52ef..00000000000 --- a/libraries/I2S/unit_tests/test_counterpart/02_data_transfer.ino +++ /dev/null @@ -1,233 +0,0 @@ -#include "unit_tests_common.h" - -#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 -#define _i2s_dma_buffer_size 1024 - - -// This is mostly copy from I2S lib, but we will not use the ring buffers and we will read / write directly with idf-i2s driver -int my_i2s_begin(int mode, uint32_t sampleRate, uint32_t bitsPerSample){ - - esp_i2s::i2s_mode_t i2s_mode = (esp_i2s::i2s_mode_t)(esp_i2s::I2S_MODE_RX | esp_i2s::I2S_MODE_TX | esp_i2s::I2S_MODE_SLAVE); - - if(mode == I2S_PHILIPS_MODE || mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE){ - if(bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32){ - log_e("Invalid bits per sample for normal mode (requested %d)\nAllowed bps = 8 | 16 | 24 | 32", bitsPerSample); - return 0; // ERR - } - }else if(mode == PDM_STEREO_MODE || mode == PDM_MONO_MODE){ // end of Normal Philips mode; start of PDM mode - #if (SOC_I2S_SUPPORTS_PDM_TX && SOC_I2S_SUPPORTS_PDM_RX) - i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_PDM); - #else - log_e("This chip does not support PDM"); - return 0; // ERR - #endif - } // Mode - esp_i2s::i2s_config_t i2s_config = { - .mode = i2s_mode, - .sample_rate = sampleRate, - .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)bitsPerSample, - .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, - .dma_buf_count = _I2S_DMA_BUFFER_COUNT, - .dma_buf_len = _i2s_dma_buffer_size, - .use_apll = true - }; - // Install and start i2s driver - if(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) 0, &i2s_config, 0, NULL)){ - log_e("ERROR i2s driver install failed"); - return 0; // ERR - } //try installing i2s driver - - if(mode == I2S_RIGHT_JUSTIFIED_MODE || mode == I2S_LEFT_JUSTIFIED_MODE || mode == PDM_MONO_MODE){ // mono/single channel - // Set the clock for MONO. Stereo is not supported yet. - if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) 0, sampleRate, (esp_i2s::i2s_bits_per_sample_t)bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ - log_e("Setting the I2S Clock has failed!\n"); - return 0; // ERR - } - } // mono channel mode - - esp_i2s::i2s_pin_config_t pin_config = { - .bck_io_num = 14, - .ws_io_num = 25, - .data_out_num = 26, - .data_in_num = 35 - }; - - if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) 0, &pin_config)){ - log_e("i2s_set_pin failed"); - return 0; // ERR - }else{ - return 1; // OK - } - return 1; // OK -} - -int test_02(){ - int errors = 0; - int current_errors = 0; - int data; - int tmp; - int tmp2; - Serial.println("Counterpart for data transfer test"); - Serial.println("=============================================================================="); - // ============================================================================================ - Serial.println("Counterpart reads, UUT writes"); - uint8_t buffer[sizeof(bps32)]; - for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately - for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ - for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ - Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); - if (!my_i2s_begin(mode[m], sampleRate[sr], bitsPerSample[bps])){ - //if (!I2S.begin(mode[m], sampleRate[sr], bitsPerSample[bps], false)){ - Serial.println("Failed to initialize I2S!"); - ++errors; - Serial1.write(C_ERR); - continue; - } - - Serial1.write(C_RDY); - data = wait_and_read(); - if(data == UUT_ERR){ - Serial.println("UUT reports error"); - ++errors; - continue; - } - - if(data != UUT_RDY){ - Serial.print("UUT sends unexpected msg code: "); - Serial.println(data); - halt(); - // what now? - }else{ - Serial.println("UUT reports ready - starting receiving"); - } - - size_t bytes_read; - int zero_samples = 0; - esp_err_t ret; - buffer[0] = 0; - - ret = i2s_read((esp_i2s::i2s_port_t) 0, buffer, 1, &bytes_read, 1000); - //ret = I2S.read(buffer, 1); - if(ret != ESP_OK){Serial.printf("i2s_read returned with error %d\n", ret);} - if(buffer[0] == 0){ - ++zero_samples; - Serial.println("first try - zero sample"); - } - - while(buffer[0] == 0){ - ret = i2s_read((esp_i2s::i2s_port_t) 0, buffer, 1, &bytes_read, 100); - //I2S.read(buffer, 1); - if(ret != ESP_OK){Serial.printf("i2s_read returned with error %d\n", ret);} - ++zero_samples; - Serial.print("zero samples = "); Serial.println(zero_samples); - } - Serial.print("First non-zero sample after "); Serial.print(zero_samples); Serial.print(" zero samples; the sample is "); Serial.println(buffer[0]); - - Serial.println("UUT reports ready - starting read"); - current_errors = 0; - switch(bitsPerSample[bps]){ - case 8: - ret = i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps8), &bytes_read, 100); - //ret = I2S.read(buffer, 1); - if(ret != ESP_OK){Serial.printf("i2s_read returned with error %d\n", ret);} - if(bytes_read != sizeof(bps8)){Serial.printf("bytes_read %d != %d sizeof(bps8); break test\n", bytes_read, sizeof(bps8)); ++current_errors; break;} - for(int i = 0; i < sizeof(bps8); ++i){ if(buffer[i] != bps8[i]){Serial.printf("buffer[%d] %d != %d bps8[%d]\n", i, buffer[i], bps8[i], i); ++current_errors;}} - break; - case 16: - i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps16), &bytes_read, 100); - if(bytes_read != sizeof(bps16)){Serial.printf("bytes_read %d != %d sizeof(bps16); break test\n", bytes_read, sizeof(bps16)); ++current_errors; break;} - for(int i = 0; i < sizeof(bps16)/2; ++i){ if(((uint16_t*)buffer)[i] != bps16[i]){Serial.printf("buffer[i] %d != %d bps16[i]\n", ((uint16_t*)buffer)[i], bps16[i]); ++current_errors;}} - break; - case 24: - i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps24), &bytes_read, 100); - if(bytes_read != sizeof(bps8)){Serial.printf("bytes_read %d != %d sizeof(bps24); break test\n", bytes_read, sizeof(bps24)); ++current_errors; break;} - for(int i = 0; i < sizeof(bps24)/4; ++i){ if(((uint32_t*)buffer)[i] != bps24[i]){Serial.printf("buffer[i] %d != %d bps32[i]\n", ((uint32_t*)buffer)[i], bps24[i]); ++current_errors;}} - break; - case 32: - i2s_read((esp_i2s::i2s_port_t) 0, buffer, sizeof(bps32), &bytes_read, 100); - if(bytes_read != sizeof(bps32)){Serial.printf("bytes_read %d != %d sizeof(bps32); break test\n", bytes_read, sizeof(bps32)); ++current_errors; break;} - for(int i = 0; i < sizeof(bps32)/4; ++i){ if(((uint32_t*)buffer)[i] != bps32[i]){Serial.printf("buffer[i] %d != %d bps32[i]\n", ((uint32_t*)buffer)[i], bps32[i]); ++current_errors;}} - break; - } - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) 0); - Serial1.write(current_errors); - errors += current_errors; - } // sr - } // bps - } // i2s mode - // ============================================================================================ - return errors; // only for debug - for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately - for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ - for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ - Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); - if (!my_i2s_begin(mode[m], sampleRate[sr], bitsPerSample[bps])) { - Serial.println("Failed to initialize I2S!"); - ++errors; - Serial1.write(C_ERR); - continue; - } - - Serial1.write(C_RDY); - int data = wait_and_read(); - if(data == UUT_ERR){ - Serial.println("UUT reports error"); - ++errors; - continue; - } - - if(data != UUT_RDY){ - Serial.print("UUT sends unexpected msg code: "); - Serial.println(data); - halt(); - // what now? - }else{ - Serial.println("UUT reports ready - starting transfer"); - } - - size_t bytes_written; - current_errors = 0; - switch(bitsPerSample[bps]){ - - case 8: - esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps8, sizeof(bps8), &bytes_written, 0); - if(sizeof(bps8) != bytes_written){ - Serial.printf("sizeof(bps8) %d != %d bytes_written\n",sizeof(bps8) ,bytes_written); - ++current_errors; - } - break; - case 16: - esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps16, sizeof(bps16), &bytes_written, 0); - if(sizeof(bps16) != bytes_written){ - Serial.printf("sizeof(bps16) %d != %d bytes_written\n",sizeof(bps16) ,bytes_written); - ++current_errors; - } - break; - case 24: - esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps24, sizeof(bps32), &bytes_written, 0); - if(sizeof(bps32) != bytes_written){ - Serial.printf("sizeof(bps32) %d != %d bytes_written\n",sizeof(bps32) ,bytes_written); - ++current_errors; - } - break; - case 32: - esp_i2s::i2s_write((esp_i2s::i2s_port_t) 0, bps32, sizeof(bps32), &bytes_written, 0); - if(sizeof(bps32) != bytes_written){ - Serial.printf("sizeof(bps32) %d != %d bytes_written\n",sizeof(bps32) ,bytes_written); - ++current_errors; - } - break; - } - esp_i2s::i2s_driver_uninstall((esp_i2s::i2s_port_t) 0); - Serial1.write(current_errors); - errors += current_errors; - } // sr - } // bps - } // i2s mode - Serial.println("=============================================================================="); - - Serial.print("End of template test. ERRORS = "); Serial.println(errors); - return errors; -} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino b/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino deleted file mode 100644 index b5675ee50b2..00000000000 --- a/libraries/I2S/unit_tests/test_counterpart/test_counterpart.ino +++ /dev/null @@ -1,43 +0,0 @@ -#include "unit_tests_common.h" - -void setup() -{ - Serial.begin(115200); // Serial monitor - while(!Serial){ - delay(10); - } - Serial.println("Serial ready - connecting UART"); - - #ifdef ESP_PLATFORM - Serial1.begin(115200, SERIAL_8N1, 22, 21); - #else - Serial.println("This is intended to be used only on ESP!"); - while(1){ ; } - #endif - - while(!Serial1){ - delay(10); - } - Serial.println("I2S unit tests counterpart starting..."); -} - -void loop() -{ - /* - send_and_print(START_TEST_1); - if(TEST_1_FINISHED != read_and_print()){ - halt(); - } - */ - - send_and_print(START_TEST_2); - test_02(); - int recv = read_and_print(); - if(TEST_2_FINISHED != recv){ - Serial.printf("Was expecting TEST_2_FINISHED from UUT, but received %d\n", recv); - halt(); - } - - // We will continue in later additions... - halt(); -} diff --git a/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp b/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp deleted file mode 120000 index 23b1bb1bd8c..00000000000 --- a/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.cpp +++ /dev/null @@ -1 +0,0 @@ -../unit_tests_common.cpp \ No newline at end of file diff --git a/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h b/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h deleted file mode 120000 index 3190abe20db..00000000000 --- a/libraries/I2S/unit_tests/test_counterpart/unit_tests_common.h +++ /dev/null @@ -1 +0,0 @@ -../unit_tests_common.h \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_tests_common.cpp b/libraries/I2S/unit_tests/unit_tests_common.cpp deleted file mode 100644 index 8a27769414b..00000000000 --- a/libraries/I2S/unit_tests/unit_tests_common.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "unit_tests_common.h" - -void send_and_print(enum msg_t msg){ - Serial1.write(msg); - Serial.printf("Sent msg num %d = \"%s\"\n", msg, txt_msg[msg]); -} - -enum msg_t read_and_print(){ - while(!Serial1.available()){ - ; // wait - } - int recv = Serial1.read(); - Serial.print("Received response number "); - Serial.print(recv); - Serial.print(" = "); - Serial.println(txt_msg[recv]); - return (enum msg_t)recv; -} - -enum msg_t wait_and_read(){ - while(!Serial1.available()){ - ; // wait - } - int recv = Serial1.read(); - return (enum msg_t)recv; -} - -void halt(){ - Serial.print("Halt..."); - while(1){ - ; // halt - } -} diff --git a/libraries/I2S/unit_tests/unit_tests_common.h b/libraries/I2S/unit_tests/unit_tests_common.h deleted file mode 100644 index 6e16068ba3b..00000000000 --- a/libraries/I2S/unit_tests/unit_tests_common.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "I2S.h" - -enum msg_t{ -NOP, // 00 No Operation - stay idle -UUT_RDY, // 01 Unit Under Test reports it is ready -UUT_ERR, // 02 Unit Under Test reports error - cannot proceed with the current setup. Error will be counted by UUT. -C_RDY, // 03 Counterpart reports it is ready -C_ERR, // 04 Counterpart reports reports error - cannot proceed with the current setup. Error will be counted by UUT upon receiving this message. -START_TEST_1, // 05 -TEST_1_FINISHED, // 06 -START_TEST_2, // 07 -TEST_2_FINISHED, // 08 -START_TEST_3, // 09 -TEST_3_FINISHED, // 10 -START_TEST_4, // 11 -TEST_4_FINISHED, // 12 -START_TEST_5, // 13 -TEST_5_FINISHED, // 14 -START_TEST_6, // 15 -TEST_6_FINISHED, // 16 -START_TEST_7, // 17 -TEST_7_FINISHED, // 18 -START_TEST_8, // 19 -TEST_8_FINISHED, // 20 -START_TEST_9, // 21 -TEST_9_FINISHED, // 22 -START_TEST_10, // 23 -TEST_10_FINISHED // 24 -}; - -static char txt_msg[26][32] = { -"NOP", // 00 No Operation - stay idle -"UUT_RDY", // 01 Unit Under Test reports it is ready -"UUT_ERR", // 02 -"Counterpart ready",// 03 -"Counterpart error",// 04 -"START_TEST_1", // 05 -"TEST_1_FINISHED", // 06 -"START_TEST_2", // 07 -"TEST_2_FINISHED", // 08 -"START_TEST_3", // 09 -"TEST_3_FINISHED", // 10 -"START_TEST_4", // 11 -"TEST_4_FINISHED", // 12 -"START_TEST_5", // 13 -"TEST_5_FINISHED", // 14 -"START_TEST_6", // 15 -"TEST_6_FINISHED", // 16 -"START_TEST_7", // 17 -"TEST_7_FINISHED", // 18 -"START_TEST_8", // 19 -"TEST_8_FINISHED", // 20 -"START_TEST_9", // 21 -"TEST_9_FINISHED", // 22 -"START_TEST_10", // 23 -"TEST_10_FINISHED" // 24 -}; - -//const i2s_mode_t mode[] = {I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, I2S_LEFT_JUSTIFIED_MODE, PDM_STEREO_MODE, PDM_MONO_MODE}; // Note: ADC_DAC_MODE is missing - it will be tested separately -const i2s_mode_t mode[] = {I2S_PHILIPS_MODE}; // debug -//const long sampleRate[] = {8000, 11025, 16000, 22050, 24000, 32000, 44100}; -const long sampleRate[] = {8000}; // debug -//const int bitsPerSample[] = {8, 16, 24, 32}; -const int bitsPerSample[] = {8}; // debug - -//const uint8_t bps8[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255}; -//const uint8_t bps8[] = {0,1,255}; // debug -//const uint8_t bps8[] = {170,170,170,170,170,170}; // debug -const uint8_t bps8[] = {170}; // debug -const uint16_t bps16[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255,256,32767,32768,32769,65534,65535}; -const uint32_t bps24[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255,256,32767,32768,32769,65534,65535,16777214,16777215}; -const uint32_t bps32[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,127,128,129,255,256,32767,32768,32769,65534,65535,65536,16777214,16777215,16777216,4294967294,4294967295}; - -void send_and_print(enum msg_t msg); -enum msg_t read_and_print(); -enum msg_t wait_and_read(); -void halt(); diff --git a/libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino b/libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino deleted file mode 100644 index 3dfe4aa5f27..00000000000 --- a/libraries/I2S/unit_tests/unit_under_test/01_smoke_test.ino +++ /dev/null @@ -1,82 +0,0 @@ -int test_01(){ - int ret = 0; - int tmp; - int tmp2; - // ============================================================================================ - - Serial.println("Try double end()"); - I2S.end(); - I2S.end(); - Serial.println(""); - // ============================================================================================ - - Serial.println("Try double begin()"); - tmp = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); - Serial.print("First begin() returned "); Serial.println(tmp); - tmp2 = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); - Serial.printf("Second begin() returned "); Serial.println(tmp2); - if(tmp != 1 || tmp2 != 0){ - Serial.println("Double begin() test failed"); - ++ret; - } - I2S.end(); - Serial.println(""); - // ============================================================================================ - - Serial.println("Try normal begin -> end; each sequence twice"); - tmp = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); - Serial.printf("First begin() returned "); Serial.println(tmp); - I2S.end(); - tmp2 = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); - Serial.printf("Second begin() returned "); Serial.println(tmp2); - I2S.end(); - if(tmp != 1 && tmp2 != 1){ - Serial.println("Double begin() -> end() test failed"); - ++ret; - } - Serial.println(""); - // ============================================================================================ - - Serial.println("Try begin with all modes"); - - tmp = I2S.begin(I2S_PHILIPS_MODE, 44100, 32); - if(tmp != 1){ - Serial.println("begin with mode I2S_PHILIPS_MODE failed"); - ++ret; - } - I2S.end(); - tmp = I2S.begin(I2S_RIGHT_JUSTIFIED_MODE, 44100, 32); - if(tmp != 1){ - Serial.println("begin with mode I2S_RIGHT_JUSTIFIED_MODE failed"); - ++ret; - } - I2S.end(); - tmp = I2S.begin(I2S_LEFT_JUSTIFIED_MODE, 44100, 32); - if(tmp != 1){ - Serial.println("begin with mode I2S_LEFT_JUSTIFIED_MODE failed"); - ++ret; - } - I2S.end(); - tmp = I2S.begin(ADC_DAC_MODE, 44100, 16); - if(tmp != 1){ - Serial.println("begin with mode ADC_DAC_MODE failed"); - ++ret; - } - I2S.end(); - tmp = I2S.begin(PDM_STEREO_MODE, 44100, 32); - if(tmp != 1){ - Serial.println("begin with mode PDM_STEREO_MODE failed"); - ++ret; - } - I2S.end(); - tmp = I2S.begin(PDM_MONO_MODE, 44100, 32); - if(tmp != 1){ - Serial.println("begin with mode PDM_MONO_MODE failed"); - ++ret; - } - I2S.end(); - // ============================================================================================ - - Serial.print("End of smoke test. ERRORS = "); Serial.println(ret); - return ret; -} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino b/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino deleted file mode 100644 index f9a3e7feed6..00000000000 --- a/libraries/I2S/unit_tests/unit_under_test/02_data_transfer.ino +++ /dev/null @@ -1,128 +0,0 @@ -int test_02(){ - int errors = 0; - int tmp; - int tmp2; - int data; - Serial.println("Data transfer test"); - Serial.println("=============================================================================="); - Serial.println("UUT writes, Counterpart reads"); - // ============================================================================================ - for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately - for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ - for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ - Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); - if (!I2S.begin(mode[m], sampleRate[sr], bitsPerSample[bps])) { - Serial.println("Failed to initialize I2S!"); - ++errors; - Serial1.write(UUT_ERR); - continue; - } - - Serial1.write(UUT_RDY); - int data = wait_and_read(); - if(data == C_ERR){ - Serial.println("Counterpart reports error"); - ++errors; - continue; - } - - if(data != C_RDY){ - Serial.print("Counterpart sends unexpected msg code: "); - Serial.println(data); - // what now? - } - - Serial.println("Counterpart reports ready - starting transfer"); - switch(bitsPerSample[bps]){ - case 8: while(1){I2S.write(bps8, sizeof(bps8));} break; - case 16: I2S.write(bps16, sizeof(bps16)); break; - case 24: I2S.write(bps24, sizeof(bps24)); break; - case 32: I2S.write(bps32, sizeof(bps32)); break; - } - I2S.end(); - - // Receive number of errors detected by counterpart - data = wait_and_read(); - errors += data; - Serial.print("Counterpart reports "); Serial.print(data); Serial.println(" errors"); - } // sr - } // bps - } // i2s mode - Serial.println("=============================================================================="); -return errors; // only for debug - // ============================================================================================ - Serial.println("Counterpart writes, UUT reads"); - //uint8_t buffer[sizeof(bps32)]; - uint8_t buffer[1024]; - for(int m = 0; m < sizeof(mode)/sizeof(i2s_mode_t); ++m){ // Note: ADC_DAC_MODE is missing - it will be tested separately - for(int bps = 0; bps < sizeof(bitsPerSample)/sizeof(int); ++bps){ - for(int sr = 0; sr < sizeof(sampleRate)/sizeof(long); ++sr){ - Serial.print("Testing mode ="); Serial.print(mode[m]); Serial.print("; bps="); Serial.print(bitsPerSample[bps]); Serial.print("; f="); Serial.print(sampleRate[sr]); Serial.println(" Hz"); - if (!I2S.begin(mode[m], sampleRate[sr], bitsPerSample[bps])) { - Serial.println("Failed to initialize I2S!"); - ++errors; - Serial1.write(UUT_ERR); - continue; - } - - Serial1.write(UUT_RDY); - data = wait_and_read(); - if(data == C_ERR){ - Serial.println("Counterpart reports error"); - ++errors; - continue; - } - - if(data != C_RDY){ - Serial.print("Counterpart sends unexpected msg code: "); - Serial.println(data); - // what now? - } - - int zero_samples = 0; - int ret; - do{ - ret = I2S.read(buffer, 1); - ++zero_samples; - Serial.print("zero samples = "); Serial.println(zero_samples); - }while(buffer[0] == 0); - - Serial.println("Counterpart reports ready - starting read"); - switch(bitsPerSample[bps]){ - case 8: - //ret = I2S.read(buffer, sizeof(bps8)); - ret = I2S.read(buffer, 1024); - if(ret == 0){Serial.println("There was an error reading from I2S: 0 bytes read"); ++errors; break;} - if(ret == 1024){Serial.println("OK, expected and read Bytes match");} - for(int i = 0; i < ret; ++i){ - Serial.print("Read sample [");Serial.print(i);Serial.print("] ");Serial.println(buffer[i]); - } - if(ret != sizeof(bps8)){ - Serial.print("Requested (");Serial.print(sizeof(bps8));Serial.print(") and read (");Serial.print(ret);Serial.println(") Bytes do not match!"); - }else{ - for(int i = 0; i < sizeof(bps8); ++i){ - if(buffer[i] != bps8[i]){ - Serial.print("Read sample [");Serial.print(i);Serial.print("] ");Serial.print(buffer[i]);Serial.print(" does not match expected value ");Serial.println(bps8[i]); - ++errors; - } - } - } - break; - case 16: I2S.read(buffer, sizeof(bps16)); - for(int i = 0; i < sizeof(bps16)/2; ++i){ if(((uint16_t*)buffer)[i] != bps16[i]) ++errors;} - break; - case 24: I2S.read(buffer, sizeof(bps24)); - for(int i = 0; i < sizeof(bps24)/4; ++i){ if(((uint32_t*)buffer)[i] != bps24[i]) ++errors;} - break; - case 32: I2S.read(buffer, sizeof(bps32)); - for(int i = 0; i < sizeof(bps32)/4; ++i){ if(((uint32_t*)buffer)[i] != bps32[i]) ++errors;} - break; - } - I2S.end(); - } // sr - } // bps - } // i2s mode - - Serial.print("End of data transfer test. ERRORS = "); Serial.println(errors); - return errors; -} \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/03_buffer_test.ino b/libraries/I2S/unit_tests/unit_under_test/03_buffer_test.ino deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp b/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp deleted file mode 120000 index 23b1bb1bd8c..00000000000 --- a/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.cpp +++ /dev/null @@ -1 +0,0 @@ -../unit_tests_common.cpp \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h b/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h deleted file mode 120000 index 3190abe20db..00000000000 --- a/libraries/I2S/unit_tests/unit_under_test/unit_tests_common.h +++ /dev/null @@ -1 +0,0 @@ -../unit_tests_common.h \ No newline at end of file diff --git a/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino b/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino deleted file mode 100644 index e8ac3ddb407..00000000000 --- a/libraries/I2S/unit_tests/unit_under_test/unit_under_test.ino +++ /dev/null @@ -1,112 +0,0 @@ -#include "I2S.h" -#include "unit_tests_common.h" - -int errors; - -void setup() { - Serial.begin(115200); // Serial monitor - while(!Serial){ - delay(10); - } - - #ifdef ESP_PLATFORM - Serial1.begin(115200, SERIAL_8N1, 22, 21); - #else - Serial1.begin(115200); - #endif - while(!Serial1){ - delay(10); - } - errors = 0; - Serial.println("I2S .ino: Both serial interfaces connected"); - Serial.println("######################################################################"); - Serial.println("I2S .ino: Testing starting"); - - - Serial.println("debug: print msg[0] as text"); - Serial.println(txt_msg[0]); -} - -void loop() { - while(!Serial1.available()){ - Serial.print("."); - delay(1000); - //; // do nothing - } - int data = Serial1.read(); - Serial.print("Received number "); Serial.print(data); Serial.print(": "); - enum msg_t msg = (msg_t)data; - - switch(msg){ - case START_TEST_1: - // Verify that unexpected calls return with error and don't crash - // Unexpected calls are for example begin() on initialized object... - Serial.print("Start smoke test "); Serial.println(txt_msg[msg]); - errors += test_01(); - Serial1.write(TEST_1_FINISHED); - break; - case START_TEST_2: - // Verify data transfer - send predefined data sequence and wait for confirmation from counterpart - // Receive predefined data sequence and send confirmation to counterpart - // Repeat test for all digital modes - Serial.print("Verify data transfer "); Serial.println(txt_msg[msg]); - errors += test_02(); - Serial1.write(TEST_2_FINISHED); - break; - case START_TEST_3: - // Verify buffers - proper available returns, peek and flush - Serial.print("Verify buffers "); Serial.println(txt_msg[msg]); - //errors += test_03(); - Serial1.write(TEST_3_FINISHED); - break; - case START_TEST_4: - // Verify callbacks - Serial.print("Verify callbacks "); Serial.println(txt_msg[msg]); - //errors += test_04(); - Serial1.write(TEST_4_FINISHED); - break; - case START_TEST_5: - // Verify pin setups - Serial.print("Verify pin setups "); Serial.println(txt_msg[msg]); - //errors += test_05(); - Serial1.write(TEST_5_FINISHED); - break; - case START_TEST_6: - // Verify ADC and DAC - Serial.print("Verify ADC and DAC "); Serial.println(txt_msg[msg]); - //errors += test_06(); - Serial1.write(TEST_6_FINISHED); - break; - case START_TEST_7: - // duplex - Serial.print("Verify duplex "); Serial.println(txt_msg[msg]); - //errors += test_07(); - Serial1.write(TEST_7_FINISHED); - break; -/* - case START_TEST_8: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); - errors += test_08(); - Serial1.write(TEST_8_FINISHED); - break; - case START_TEST_9: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); - - errors += test_09(); - Serial1.write(TEST_9_FINISHED); - break; - case START_TEST_10: - Serial.print("TODO: call test"); Serial.println(txt_msg[msg]); - errors += test_10(); - Serial1.write(TEST_10_FINISHED); - break; -*/ - case NOP: - break; - default: - Serial.print("Unknown command number "); Serial.println(msg); - break; - } // switch - - msg = NOP; -} \ No newline at end of file From cedff7dc93c4b49e05e986d434274c870d1af315 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 6 Jan 2022 10:57:03 +0100 Subject: [PATCH 84/94] Initial code polish --- libraries/I2S/src/I2S.cpp | 81 +++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 28c45da56c4..d92a3612b84 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -35,15 +35,15 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), - _sdPin(sdPin), // shared data pin - _inSdPin(sdPin), // input data pin + _sdPin(sdPin), // shared data pin + _inSdPin(sdPin), // input data pin #ifdef PIN_I2S_SD_OUT - _outSdPin(PIN_I2S_SD_OUT), // output data pin + _outSdPin(PIN_I2S_SD_OUT), // output data pin #else - _outSdPin(-1), // output data pin + _outSdPin(-1), // output data pin #endif - _sckPin(sckPin), // clock pin - _fsPin(fsPin), // frame (word) select pin + _sckPin(sckPin), // clock pin + _fsPin(fsPin), // frame (word) select pin _state(I2S_STATE_IDLE), _bitsPerSample(0), @@ -59,7 +59,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), - _i2s_dma_buffer_size(128), // Number of frames in each DMA buffer. Frame size = number of channels * bits (or Bytes) per sample + _i2s_dma_buffer_size(128), // Number of frames in each DMA buffer. Frame size = number of channels * Bytes per sample _driveClock(true), _peek_buff(0), _peek_buff_valid(false), @@ -82,12 +82,12 @@ int I2SClass::_createCallbackTask(){ } xTaskCreate( - onDmaTransferComplete, // Function to implement the task + onDmaTransferComplete, // Function to implement the task "onDmaTransferComplete", // Name of the task - stack_size, // Stack size in words - NULL, // Task input parameter - 2, // Priority of the task - &_callbackTaskHandle // Task handle. + stack_size, // Stack size in words + NULL, // Task input parameter + 2, // Priority of the task + &_callbackTaskHandle // Task handle. ); if(_callbackTaskHandle == NULL){ log_e("Could not create callback task"); @@ -144,12 +144,11 @@ int I2SClass::_installDriver(){ .sample_rate = _sampleRate, .bits_per_sample = (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, .channel_format = esp_i2s::I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), // 0x01 // default + .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S), .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, .dma_buf_count = _I2S_DMA_BUFFER_COUNT, .dma_buf_len = _i2s_dma_buffer_size, .use_apll = false - //left_align = true // will it fix ? - no }; // Install and start i2s driver while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ @@ -161,13 +160,13 @@ int I2SClass::_installDriver(){ log_w("WARNING i2s driver install failed; Trying to decrease I2S DMA buffer size from %d to 1024\n", _i2s_dma_buffer_size); setBufferSize(1024); }else{ // install failed with max buffer size - log_e("ERROR i2s driver install failed"); - return 0; // ERR + log_e("ERROR i2s driver install failed"); + return 0; // ERR } } //try installing with increasing size if(_mode == I2S_RIGHT_JUSTIFIED_MODE || _mode == I2S_LEFT_JUSTIFIED_MODE || _mode == PDM_MONO_MODE){ // mono/single channel - // Set the clock for MONO. Stereo is not supported yet. + // Set the clock for MONO. Stereo is not supported yet. if(ESP_OK != esp_i2s::i2s_set_clk((esp_i2s::i2s_port_t) _deviceIndex, _sampleRate, (esp_i2s::i2s_bits_per_sample_t)_bitsPerSample, esp_i2s::I2S_CHANNEL_MONO)){ log_e("Setting the I2S Clock has failed!\n"); return 0; // ERR @@ -224,20 +223,20 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){ int ret = begin(mode, sampleRate, bitsPerSample, true); _give_if_top_call(); return ret; - } int I2SClass::begin(int mode, int bitsPerSample){ + _take_if_not_holding(); // slave mode (not driving clock and frame select pin - input) - log_d("begin in slave mode"); - return begin(mode, 0, bitsPerSample, false); + int ret = begin(mode, 0, bitsPerSample, false); + _give_if_top_call(); + return ret; } int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){ - log_d("starting "); _take_if_not_holding(); if(_initialized){ - log_e("ERROR I2SClass::begin() object already initialized! Call I2S.end() to deinitialize"); + log_e("ERROR: Object already initialized! Call I2S.end() to disable"); _give_if_top_call(); return 0; // ERR } @@ -247,19 +246,20 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock _bitsPerSample = bitsPerSample; if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { - log_e("Error: unexpected _state (%d)",_state); + log_e("Error: unexpected _state (%d)", _state); _give_if_top_call(); return 0; // ERR } - // TODO implement left / right justified modes switch (mode) { case I2S_PHILIPS_MODE: case I2S_RIGHT_JUSTIFIED_MODE: case I2S_LEFT_JUSTIFIED_MODE: -#if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - case ADC_DAC_MODE: -#endif + + #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) + case ADC_DAC_MODE: + #endif + case PDM_STEREO_MODE: case PDM_MONO_MODE: break; @@ -321,12 +321,12 @@ int I2SClass::_applyPinSetting(){ } } if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d\n", _sckPin, _fsPin, pin_config.data_in_num, pin_config.data_out_num); + log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d", _sckPin, _fsPin, pin_config.data_in_num, pin_config.data_out_num); return 0; // ERR }else{ return 1; // OK } - } // _driverInstalled ? + } // if(_driverInstalled) return 1; // OK } @@ -395,7 +395,6 @@ int I2SClass::setDataOutPin(int outSdPin){ return ret; } - int I2SClass::setAllPins(){ _take_if_not_holding(); int ret = setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); @@ -533,6 +532,7 @@ int I2SClass::read(){ sample.b32 = 0; if(_initialized){ read(&sample, _bitsPerSample / 8); + if (_bitsPerSample == 32) { _give_if_top_call(); return sample.b32; @@ -594,13 +594,6 @@ int I2SClass::read(void* buffer, size_t size){ return 0; // 0 Bytes read / ERR } -/* -size_t I2SClass::write(int sample) -{ - return write((int32_t)sample); -} -*/ - size_t I2SClass::write(uint8_t data){ _take_if_not_holding(); size_t ret = 0; @@ -643,6 +636,8 @@ size_t I2SClass::write(const void *buffer, size_t size){ } // blocking version of write +// This version of write will wait indefinitely to write requested samples +// into output buffer size_t I2SClass::write_blocking(const void *buffer, size_t size){ _take_if_not_holding(); if(_initialized){ @@ -672,6 +667,8 @@ size_t I2SClass::write_blocking(const void *buffer, size_t size){ } // non-blocking version of write +// In case there is not enough space in buffer to write requested size +// this function will try to flush the buffer and write requested data with 0 time-out size_t I2SClass::write_nonblocking(const void *buffer, size_t size){ _take_if_not_holding(); if(_initialized){ @@ -848,10 +845,10 @@ void I2SClass::_tx_done_routine(uint8_t* prev_item){ vRingbufferReturnItem(_output_ring_buffer, item); } // Check received item } // don't read from almost empty buffer - if(_onTransmit){ - _onTransmit(); - } // user callback } // fill up the I2S DMA buffer + if(_onTransmit){ + _onTransmit(); + } // user callback } void I2SClass::_rx_done_routine(){ @@ -951,8 +948,8 @@ void I2SClass::_post_read_data_fix(void *input, size_t *size){ // Prepares data and writes them to IDF i2s driver. // This counters possible bug in ESP IDF I2S driver -// output - bytes to be sent (input and output for this function) -// size - number of bytes in original buffer (this may change) +// output - bytes to be sent +// size - number of bytes in original buffer // bytes_written - number of bytes used from original buffer // actual_bytes_written - number of bytes written by i2s_write after fix void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written, size_t *actual_bytes_written){ From a73a1a65ae878eb2e09a2cb609d974c801d98713 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Thu, 6 Jan 2022 09:22:14 -0300 Subject: [PATCH 85/94] fixes buffer size - no noise now --- libraries/I2S/src/I2S.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index d92a3612b84..f41b64590d4 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -22,7 +22,7 @@ #include "freertos/semphr.h" #define _I2S_EVENT_QUEUE_LENGTH 16 -#define _I2S_DMA_BUFFER_COUNT 4 // BUFFER COUNT must be between 2 and 128 +#define _I2S_DMA_BUFFER_COUNT 2 // BUFFER COUNT must be between 2 and 128 #define I2S_INTERFACES_COUNT SOC_I2S_NUM #ifndef I2S_DEVICE @@ -813,7 +813,7 @@ int I2SClass::_enableReceiver(){ void I2SClass::_tx_done_routine(uint8_t* prev_item){ static bool prev_item_valid = false; - const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8)*2; // *2 for stereo - it has double number of samples for 2 channels static size_t item_size = 0; static size_t prev_item_size = 0; static void *item = NULL; From 29b05a3c02fc8a00b35cd6258afbff4065a45d0d Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Thu, 6 Jan 2022 14:58:07 +0100 Subject: [PATCH 86/94] Fix free bug for 24 and 32 bps; add warning to unsupported modes --- libraries/I2S/src/I2S.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index f41b64590d4..803f3b17ced 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -245,6 +245,17 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock _sampleRate = (uint32_t)sampleRate; _bitsPerSample = bitsPerSample; + // There is work in progress on this library. + if(_bitsPerSample == 16 && _sampleRate < 16000){ + log_w("This sample rate is not officially supported - audio might be noisy. Try using sample rate below or equal to 16000"); + } + if(_bitsPerSample != 16){ + log_w("This bit-per-sample is not officially supported - audio quality might suffer. Try using 16bps, with sample rate below equal 16000"); + } + if(_mode != I2S_PHILIPS_MODE){ + log_w("This mode is not officially supported - audio quality might suffer. At the moment the only supported mode is I2S_PHILIPS_MODE"); + } + if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { log_e("Error: unexpected _state (%d)", _state); _give_if_top_call(); @@ -983,8 +994,12 @@ void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written, ((uint16_t*)buff)[i+1] = ((uint16_t*)output)[i]; // [0] <- [1] } break; + case 24: + buff = (uint8_t*)output; + break; case 32: buff = (uint8_t*)output; + break; default: ; // Do nothing } // switch @@ -996,7 +1011,10 @@ void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written, if(ret == ESP_OK && buff_size != _bytes_written){ log_w("Warning: writing data to i2s - written %d B instead of requested %d B", _bytes_written, buff_size); } - free(buff); + // free if the buffer was actually allocated + if(_bitsPerSample == 8 || _bitsPerSample == 16){ + free(buff); + } if(bytes_written != NULL){ *bytes_written = _bitsPerSample == 8 ? _bytes_written/2 : _bytes_written; } From e1dc00e4498ff1483cc26d7a7d7bcf5051f58f37 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Tue, 18 Jan 2022 15:14:18 +0100 Subject: [PATCH 87/94] Initial documentation of I2S lib --- docs/source/api/i2s.rst | 339 ++++++++++++++++++++++++++++++++++++++ docs/source/libraries.rst | 3 +- 2 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 docs/source/api/i2s.rst diff --git a/docs/source/api/i2s.rst b/docs/source/api/i2s.rst new file mode 100644 index 00000000000..9ade2214151 --- /dev/null +++ b/docs/source/api/i2s.rst @@ -0,0 +1,339 @@ +### +I2S +### + +About +----- + +I2S - Inter-IC Sound, correctly written I²S pronounced "eye-squared-ess", alternative notation is IIS. I²S is an electrical serial bus interface standard used for connecting digital audio devices together. It is used to communicate PCM (Pulse-Code Modulation) audio data between integrated circuits in an electronic device. The I²S bus separates clock and serial data signals, resulting in simpler receivers than those required for asynchronous communications systems that need to recover the clock from the data stream. Despite the similar name, I²S is unrelated and incompatible with the bidirectional I²C (IIC) bus. + +The I²S bus consists of at least three lines: + +All lines can be attached to almost any pin and this change can occur even during operation. + +* **Bit clock line** + + * Officially "continuous serial clock (SCK)". Typically written "bit clock (BCLK)". + * In this library function parameter ``sckPin`` or constant ``PIN_I2S_SCK``. + +* **Word clock line** + + * Officially "word select (WS)". Typically called "left-right clock (LRCLK)" or "frame sync (FS)". + * 0 = Left channel, 1 = Right channel + * In this library function parameter ``fsPin`` or constant ``PIN_I2S_FS``. + +* **At least one multiplexed data line** + + * Officially "serial data (SD)", but can be called SDATA, SDIN, SDOUT, DACDAT, ADCDAT, etc. + * Unlike in original Arduino I2S library with single data pin swithing between input and output, in ESP version of this library we use separate data line for input and output. + * Input data are called ``inSdPin`` and ``sdPin`` for function parameter, or constant ``PIN_I2S_SD`` (for backward compatibility with original Arduino I2S library) + * Output data are called ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` + +I2S Modes +********* + +The I2C can be used in few different modes: + +.. note:: Officially supported mode is only ``I2S_PHILIPS_MODE``. Other modes are implemented, but we cannot guarantee flawless execution and behavior. + +Master / slave mode +------------------- + +In **Master mode** (default) the device is generating clock signal ``sckPin`` and word select signal on ``fsPin``. + +In **Slave mode** the device listens on attached pins for clock signal and word select - i.e. unless externally driven the pins will remain LOW. + +How to enter either mode is described in function section. + +Operation modes +--------------- + +* ``I2S_PHILIPS_MODE`` + Currently the only officially supported mode. + This mode is using original Philips specification for I2S bus. See their paper https://web.archive.org/web/20080706121949/http://www.nxp.com/acrobat_download/various/I2SBUS.pdf + + .. Note::Following modes currently not officially supported. Using any of the following modes will print warning, but continue to operate. However the quality is not guaranteed and the application may crash. + +* ``I2S_RIGHT_JUSTIFIED_MODE`` + In this mode, you only need to send one channel data but the data will be copied for another channel automatically, then both channels will transmit same data. + +* ``I2S_LEFT_JUSTIFIED_MODE`` + In this mode, you only need to send one channel data but the data will be copied for another channel automatically, then both channels will transmit same data. + +* ``ADC_DAC_MODE`` + Output will be analog signal on pins 25 (L or R?) and 26 (L or R?). + Input will be received on pin ``_inSdPin``. + The data are sampled on 12 bits and stored in 16 bits with 4 most significant bits set to zero. + +* ``PDM_STEREO_MODE`` + Pulse-density-modulation is similar to PWM, but instead the pulses have constant width. The signal is modulated with number of ones or zeroes in sequence. + +* ``PDM_MONO_MODE`` + Single-channel version of PDM mode described above. + + +Arduino-ESP32 I2S API +--------------------- + +The ESP32 I2S library is based on the Arduino I2S Library and implements a few more APIs, described in this documentation. +https://www.arduino.cc/en/Reference/I2S + +Initialization and deinitialization +*********************************** +Before usage choose which pins you want to use. In DAC mode you can use only pins 25 and 26 for output. + +int begin(int mode, int sampleRate, int bitsPerSample) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Performs initialization before use - creates buffers, task handling underlying driver messages, configuring and starting the driver operation. + +This version initializes I2S in MASTER mode (see next entry for SLAVE mode). + +parameters: + [in] ``mode`` one of above mentioned modes for example ``I2S_PHILIPS_MODE``. + + [in] ``sampleRate`` sampling rate in Hz. Currently officially supported value is only 16000 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. + + [in] ``bitsPerSample`` Number of bits in a channel sample. Currently officially supported value is only 16 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. + For ``ADC_DAC_MODE`` the only possible value will remain 16. + +returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. + +int begin(int mode, int bitsPerSample) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Performs initialization before use - creates buffers, task handling underlying driver messages, configuring and starting the driver operation. + +This version initializes I2S in SLAVE mode (see previous entry for MASTER mode). + +parameters: + [in] ``mode`` one of above mentioned modes for example ``I2S_PHILIPS_MODE``. + + [in] ``bitsPerSample`` Number of bits in a channel sample. Currently officially supported value is only 16 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. + For ``ADC_DAC_MODE`` the only possible value will remain 16. + +Returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. + +void end() +^^^^^^^^^^ +Performs safe deinitialization - free buffers, destroy task, end driver operation, etc. + +Pin setup +********* +Pins can changed in two ways- 1st constants, 2nd functions. + +..Note:: Shared data pin can be equal to any other data pin, but must not be equal to clock pin nor frame sync pin! Input and Output pins must not be equal, but one of them can be equal to shared data pin! + +sckPin != fsPin != outSdPin != inSdPin + +sckPin != fsPin != sdPin + +By default the pin numbers are defined in constants in the header file. You can redefine any of those constants before including ``I2S.h``. This way the driver will be use these new default values and you will not need to specify pins in your code. The constants and their default values are + +``PIN_I2S_SCK 14`` + +``PIN_I2S_FS 25`` + +``PIN_I2S_SD 26`` + +``PIN_I2S_SD_OUT 26`` + +``PIN_I2S_SD_IN 35`` + +Second option to change pins is using the following functions. These functions *MUST* be called on intialized object (after calling ``begin``) therefore they can change pin value during operation. + + +int setSckPin(int sckPin) +^^^^^^^^^^^^^^^^^^^^^^^^^ +Set and apply clock pin. + +Returns 1 on success, 0 on failure. + +int setFsPin(int fsPin) +^^^^^^^^^^^^^^^^^^^^^^^ +Set and apply frame sync pin. + +Returns 1 on success, 0 on failure. + +int setDataPin(int sdPin) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set and apply shared data pin used in simplex mode. + +Returns 1 on success, 0 on failure. + +int setDataInPin(int inSdPin) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set and apply data input pin. + +Returns 1 on success, 0 on failure. + +int setDataOutPin(int outSdPin) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set and apply data output pin. +Returns 1 on success, 0 on failure. + +int setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set all pins using given values in parameters. This simply a wrapper of four functions mentioned above. + + +int setAllPins() +^^^^^^^^^^^^^^^^ +Set all pins to default i.e. take values from constants mentioned above. This simply calls the the function ``setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SD_IN);`` + +int getSckPin() +^^^^^^^^^^^^^^^ +Get current value of clock pin. + +int getFsPin() +^^^^^^^^^^^^^^ +Get current value of frame sync pin. + +int getDataPin() +^^^^^^^^^^^^^^^^ +Get current value of shared data pin. + +int getDataInPin() +^^^^^^^^^^^^^^^^^^ +Get current value of data input pin. + +int getDataOutPin() +^^^^^^^^^^^^^^^^^^^ +Get current value of data output pin. + + +void onTransmit(void(*)(void)) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Register function which will be called on each successful i2s driver transmit event. + +void onReceive(void(*)(void)) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Register function which will be called on each successful i2s driver receive event. + +int setBufferSize(int bufferSize) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set new size of buffer. + +This function can be called both on initialized and uninitialized driver. +If called on initialized, it will change internal values for buffer size and re-initialize driver with new value. +If called on uninitialized, it will only change the internal values which will be used for next initialization. + +Parameter ``bufferSize`` must be in range <8; 1024>. the unit is sample words. Default value on object creation is 128. +Example: 16 bit sample, dual channel, buffer size 128 = 2B sample * 2 channels * 128 buffer size * buffer count (default 2) = 1024B for input buffer + 1024B for output buffer = total 2kB used. + +This function always assumes dual channel, keeping the same size even for MONO modes. + +Returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. + +int getBufferSize() +^^^^^^^^^^^^^^^^^^^ +Get current buffer sizes in sample words (see description for ``setBufferSize``). + +Duplex vs Simplex +***************** +Original Arduino I2S library supports only *simplex* mode (only transmit or only receive at a time). For compatibility we kept this behavior, but ESP natively supports *duplex* mode (receive and transmit simultaneously on separate pins). +By default this library is initialized in simplex mode as it would in Arduino, switching input and output on sdPin (constant PIN_I2S_SD) (default pin 26). + + +int setDuplex() +^^^^^^^^^^^^^^^ +Switch to duplex mode and use separate pins: +input: inSdPin (constant PIN_I2S_SD_IN, default 35) +output: outSdPin (constant PIN_I2S_SD, default 26) + +int setSimplex() +^^^^^^^^^^^^^^^^ +(Default mode) + +Switch to simplex mode using shared data pin sdPin (constant PIN_I2S_SD, default 26). + +int isDuplex() +^^^^^^^^^^^^^^ +Returns 1 if current mode is duplex, 0 if current mode is simplex (default). + +Data stream +*********** + +int available() +^^^^^^^^^^^^^^^ +Returns number of **BYTES** ready to read + +int read(void* buffer, size_t size) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Read ``size`` Bytes from internal buffer if possible. + +This function is non-blocking, i.e. if requested number of Bytes is not available it will return as much as possible without waiting. + +Hint: use ``available()`` before calling this function. + +Parameters: + +[out] ``void* buffer`` buffer into which will be copied data read from internal buffer. WARNING: this buffer must be allocated before use! + +[in] ``size_t size`` number of Bytes required to be read. +Returns number of successfully read Bytes. Returns 0 on error. + +int read() +^^^^^^^^^^ +Read one sample + +int peek() +^^^^^^^^^^ +Read 1 sample from internal buffer and return it. +Repeated peeks will return the same sample until read is called. + + +void flush() +^^^^^^^^^^^^ +Force write internal buffer to driver. + +size_t write(uint8_t) +^^^^^^^^^^^^^^^^^^^^^ +Write single Byte. + +Single-sample writes are blocking - waiting until there is free space in internal buffer to be written into. + +Returns number of successfully written Bytes, in this case 1. Returns 0 on error. + +size_t write(int32_t) +^^^^^^^^^^^^^^^^^^^^^ +Write sample. + +Single-sample writes are blocking - waiting until there is free space in internal buffer to be written into. + +Returns number of successfully written bytes. Returns 0 on error. + +Expected return number is ``bitsPerSample/8``. + +size_t write(const void *buffer, size_t size) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Write buffer of supplied size; + +Parameters: + +[in] ``const void *buffer`` buffer to be written + +[in] ``size_t size`` size of buffer in Bytes + +Returns number of successfully written bytes. Returns 0 on error. +Expected return number is equal to ``size``. + +size_t write(const uint8_t *buffer, size_t size) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This is a wrapper of previous function performing typecast from `uint8_t*`` to ``void*``. + +int availableForWrite() +^^^^^^^^^^^^^^^^^^^^^^^ +Returns number of **BYTES** available for write. + + +size_t write_blocking(const void *buffer, size_t size) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Core function implementing blocking write, i.e. waits until all requested data are written. +WARNING: If too many bytes are requested, this can cause WatchDog Trigger Reset! + +Returns number of successfully written bytes. Returns 0 on error. + +size_t write_nonblocking(const void *buffer, size_t size) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Core function implementing non-blocking write, i.e. writes as much as possible and exits. + +Returns number of successfully written bytes. Returns 0 on error. \ No newline at end of file diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 12dd8cfd37f..19e11ca5635 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -28,7 +28,7 @@ Currently, the Arduino ESP32 supports the following peripherals with Arduino API +---------------+---------------+---------------+---------------+-------------------------------+ | I2C | Yes | Yes | Yes | | +---------------+---------------+---------------+---------------+-------------------------------+ -| I2S | No | No | No | WIP | +| I2S | Yes | No | No | WIP | +---------------+---------------+---------------+---------------+-------------------------------+ | LEDC | Yes | Yes | Yes | | +---------------+---------------+---------------+---------------+-------------------------------+ @@ -84,6 +84,7 @@ The Arduino ESP32 offers some unique APIs, described in this section: ESPNOW GPIO I2C + I2S RainMaker Reset Reason USB From e88335820170b5c14b597c728ac6e97326fcdd79 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 19 Jan 2022 11:37:03 +0100 Subject: [PATCH 88/94] Modifications to I2S doc --- docs/source/api/i2s.rst | 85 +++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/docs/source/api/i2s.rst b/docs/source/api/i2s.rst index 9ade2214151..69d649a0906 100644 --- a/docs/source/api/i2s.rst +++ b/docs/source/api/i2s.rst @@ -22,22 +22,29 @@ All lines can be attached to almost any pin and this change can occur even durin * 0 = Left channel, 1 = Right channel * In this library function parameter ``fsPin`` or constant ``PIN_I2S_FS``. -* **At least one multiplexed data line** +* **Data line** * Officially "serial data (SD)", but can be called SDATA, SDIN, SDOUT, DACDAT, ADCDAT, etc. - * Unlike in original Arduino I2S library with single data pin swithing between input and output, in ESP version of this library we use separate data line for input and output. - * Input data are called ``inSdPin`` and ``sdPin`` for function parameter, or constant ``PIN_I2S_SD`` (for backward compatibility with original Arduino I2S library) - * Output data are called ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` + * Unlike Arduino I2S with single data pin switching between input and output, in ESP core driver use separate data line for input and output. + * For backward compatibility the shared data pin is ``sdPin`` or constant ``PIN_I2S_SD`` when using simplex mode. + * When using duplex mode there are two data lines: + * Output data line is called ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` + * Input data line is called ``inSdPin`` for function parameter, or constant ``PIN_I2S_SD_IN`` I2S Modes -********* +--------- + +The I2S can be set up in three groups of modes: -The I2C can be used in few different modes: + * Master (default) or Slave + * Simplex (default) or Duplex + * Operation modes (Philips standard, ADC/DAC, PDM) + * Most of them are dual channel, some can be single channel -.. note:: Officially supported mode is only ``I2S_PHILIPS_MODE``. Other modes are implemented, but we cannot guarantee flawless execution and behavior. +.. note:: Officially supported operation mode is only ``I2S_PHILIPS_MODE``. Other modes are implemented, but we cannot guarantee flawless execution and behavior. Master / slave mode -------------------- +******************* In **Master mode** (default) the device is generating clock signal ``sckPin`` and word select signal on ``fsPin``. @@ -46,7 +53,9 @@ In **Slave mode** the device listens on attached pins for clock signal and word How to enter either mode is described in function section. Operation modes ---------------- +*************** + +Setting the operation mode is done with function ``begin`` (see API section) * ``I2S_PHILIPS_MODE`` Currently the only officially supported mode. @@ -71,6 +80,19 @@ Operation modes * ``PDM_MONO_MODE`` Single-channel version of PDM mode described above. +Simplex / duplex mode +********************* + +The **Simplex** mode is default after driver initialization. Simplex mode is using shared data pin ``sdPin`` or constant ``PIN_I2S_SD`` for both output and input, but can only read or write. This is the same behavior as in original Arduino library. + +The **Duplex** mode uses two separate data pins: + + * Output pin ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` + * Input pin ``inSdPin`` for function parameter, or constant ``PIN_I2S_SD_IN`` +In this mode the driver is able to read and write simultaneously on each line and is suitable for applications like walkie-talkie or phone. + +Switching between these modes is performed simply by calling setDuplex() or setSimplex() (see APi section for details and more functions). + Arduino-ESP32 I2S API --------------------- @@ -89,14 +111,14 @@ Performs initialization before use - creates buffers, task handling underlying d This version initializes I2S in MASTER mode (see next entry for SLAVE mode). parameters: - [in] ``mode`` one of above mentioned modes for example ``I2S_PHILIPS_MODE``. + [in] ``mode`` one of above mentioned operation mode, for example ``I2S_PHILIPS_MODE``. [in] ``sampleRate`` sampling rate in Hz. Currently officially supported value is only 16000 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. [in] ``bitsPerSample`` Number of bits in a channel sample. Currently officially supported value is only 16 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. For ``ADC_DAC_MODE`` the only possible value will remain 16. -returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. +Returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. int begin(int mode, int bitsPerSample) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,19 +148,21 @@ sckPin != fsPin != outSdPin != inSdPin sckPin != fsPin != sdPin -By default the pin numbers are defined in constants in the header file. You can redefine any of those constants before including ``I2S.h``. This way the driver will be use these new default values and you will not need to specify pins in your code. The constants and their default values are +By default the pin numbers are defined in constants in the header file. You can redefine any of those constants before including ``I2S.h``. This way the driver will use these new default values and you will not need to specify pins in your code. The constants and their default values are: -``PIN_I2S_SCK 14`` +* ``PIN_I2S_SCK`` 14 -``PIN_I2S_FS 25`` +* ``PIN_I2S_FS`` 25 -``PIN_I2S_SD 26`` +* ``PIN_I2S_SD`` 26 -``PIN_I2S_SD_OUT 26`` +* ``PIN_I2S_SD_OUT`` 26 -``PIN_I2S_SD_IN 35`` +* ``PIN_I2S_SD_IN`` 35 -Second option to change pins is using the following functions. These functions *MUST* be called on intialized object (after calling ``begin``) therefore they can change pin value during operation. +Second option to change pins is using the following functions. These functions can be called on either on initialized or uninitialized object. +If called on initialized object (after calling ``begin``) the pins will change during operation. +If called on uninitialized object (before calling ``begin``, or after calling ``end``) the new pin setup will be used on next initialization. int setSckPin(int sckPin) @@ -172,8 +196,7 @@ Returns 1 on success, 0 on failure. int setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Set all pins using given values in parameters. This simply a wrapper of four functions mentioned above. - +Set all pins using given values in parameters. This is simply a wrapper of four functions mentioned above. int setAllPins() ^^^^^^^^^^^^^^^^ @@ -336,4 +359,24 @@ size_t write_nonblocking(const void *buffer, size_t size) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Core function implementing non-blocking write, i.e. writes as much as possible and exits. -Returns number of successfully written bytes. Returns 0 on error. \ No newline at end of file +Returns number of successfully written bytes. Returns 0 on error. + +Sample code +----------- +.. code-block:: c + + #include + const int buff_size = 128; + int available, read; + uint8_t buffer[buff_size]; + + I2S.begin(I2S_PHILIPS_MODE, 16000, 16); + I2S.read(); // Switch the driver in simplex mode to receive + available = I2S.available(); + if(available < buff_size){ + read = I2S.read(buffer, available); + }else{ + read = I2S.read(buffer, buff_size); + } + I2S.write(buffer, read); + I2S.end(); From d02f3923800c9a89ebfdd6a4cfc5084bee939ef4 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Wed, 19 Jan 2022 13:26:57 +0100 Subject: [PATCH 89/94] Updates and small fixes - slave mode, pins,... --- libraries/I2S/src/I2S.cpp | 112 +++++++++++++++++++++++--------------- libraries/I2S/src/I2S.h | 30 ++++++---- 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp index 803f3b17ced..44d3f7a2b0f 100644 --- a/libraries/I2S/src/I2S.cpp +++ b/libraries/I2S/src/I2S.cpp @@ -36,12 +36,8 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin) : _deviceIndex(deviceIndex), _sdPin(sdPin), // shared data pin - _inSdPin(sdPin), // input data pin -#ifdef PIN_I2S_SD_OUT - _outSdPin(PIN_I2S_SD_OUT), // output data pin -#else - _outSdPin(-1), // output data pin -#endif + _inSdPin(PIN_I2S_SD_IN), // input data pin + _outSdPin(PIN_I2S_SD), // output data pin _sckPin(sckPin), // clock pin _fsPin(fsPin), // frame (word) select pin @@ -59,7 +55,7 @@ I2SClass::I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, u _i2s_general_mutex(NULL), _input_ring_buffer(NULL), _output_ring_buffer(NULL), - _i2s_dma_buffer_size(128), // Number of frames in each DMA buffer. Frame size = number of channels * Bytes per sample + _i2s_dma_buffer_size(128), // Number of frames in each DMA buffer. Frame size = number of channels * Bytes per sample; Must be between 8 and 1024 _driveClock(true), _peek_buff(0), _peek_buff_valid(false), @@ -113,7 +109,7 @@ int I2SClass::_installDriver(){ if(_mode == ADC_DAC_MODE){ #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) if(_bitsPerSample != 16){ // ADC/DAC can only work in 16-bit sample mode - log_e("ERROR I2SClass::begin invalid bps for ADC/DAC. Allowed only 16, requested %d", _bitsPerSample); + log_e("ERROR invalid bps for ADC/DAC. Allowed only 16, requested %d", _bitsPerSample); return 0; // ERR } i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_DAC_BUILT_IN | esp_i2s::I2S_MODE_ADC_BUILT_IN); @@ -129,7 +125,7 @@ int I2SClass::_installDriver(){ return 0; // ERR } if(_bitsPerSample == 24){ - log_w("Original Arduino library does not support 24 bits per sample - keep that in mind if you should switch back"); + log_w("Original Arduino library does not support 24 bits per sample.\nKeep that in mind if you should switch back to Arduino"); } }else if(_mode == PDM_STEREO_MODE || _mode == PDM_MONO_MODE){ // end of Normal Philips mode; start of PDM mode #if (SOC_I2S_SUPPORTS_PDM_TX && SOC_I2S_SUPPORTS_PDM_RX) @@ -150,14 +146,20 @@ int I2SClass::_installDriver(){ .dma_buf_len = _i2s_dma_buffer_size, .use_apll = false }; + + if(_driveClock == false){ + i2s_config.use_apll = true; + i2s_config.fixed_mclk = 512*_sampleRate; + } + // Install and start i2s driver while(ESP_OK != esp_i2s::i2s_driver_install((esp_i2s::i2s_port_t) _deviceIndex, &i2s_config, _I2S_EVENT_QUEUE_LENGTH, &_i2sEventQueue)){ // increase buffer size if(2*_i2s_dma_buffer_size <= 1024){ - log_w("WARNING i2s driver install failed; Trying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); + log_w("WARNING i2s driver install failed.\nTrying to increase I2S DMA buffer size from %d to %d\n", _i2s_dma_buffer_size, 2*_i2s_dma_buffer_size); setBufferSize(2*_i2s_dma_buffer_size); }else if(_i2s_dma_buffer_size < 1024){ - log_w("WARNING i2s driver install failed; Trying to decrease I2S DMA buffer size from %d to 1024\n", _i2s_dma_buffer_size); + log_w("WARNING i2s driver install failed.\nTrying to decrease I2S DMA buffer size from %d to 1024\n", _i2s_dma_buffer_size); setBufferSize(1024); }else{ // install failed with max buffer size log_e("ERROR i2s driver install failed"); @@ -177,12 +179,12 @@ int I2SClass::_installDriver(){ if(_mode == ADC_DAC_MODE){ esp_i2s::i2s_set_dac_mode(esp_i2s::I2S_DAC_CHANNEL_BOTH_EN); esp_i2s::adc_unit_t adc_unit; - if(!gpioToAdcUnit((gpio_num_t)_inSdPin, &adc_unit)){ + if(!_gpioToAdcUnit((gpio_num_t)_inSdPin, &adc_unit)){ log_e("pin to adc unit conversion failed"); return 0; // ERR } esp_i2s::adc_channel_t adc_channel; - if(!gpioToAdcChannel((gpio_num_t)_inSdPin, &adc_channel)){ + if(!_gpioToAdcChannel((gpio_num_t)_inSdPin, &adc_channel)){ log_e("pin to adc channel conversion failed"); return 0; // ERR } @@ -217,6 +219,7 @@ int I2SClass::_installDriver(){ return 1; // OK } +// Init in MASTER mode: the SCK and FS pins are driven as outputs using the sample rate int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){ _take_if_not_holding(); // master mode (driving clock and frame select pins - output) @@ -225,14 +228,17 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample){ return ret; } +// Init in SLAVE mode: the SCK and FS pins are inputs, other side controls sample rate int I2SClass::begin(int mode, int bitsPerSample){ _take_if_not_holding(); // slave mode (not driving clock and frame select pin - input) - int ret = begin(mode, 0, bitsPerSample, false); + int ret = begin(mode, 96000, bitsPerSample, false); _give_if_top_call(); return ret; } + +// Core function int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock){ _take_if_not_holding(); if(_initialized){ @@ -246,14 +252,14 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock _bitsPerSample = bitsPerSample; // There is work in progress on this library. - if(_bitsPerSample == 16 && _sampleRate < 16000){ - log_w("This sample rate is not officially supported - audio might be noisy. Try using sample rate below or equal to 16000"); + if(_bitsPerSample == 16 && _sampleRate > 16000 && driveClock){ + log_w("This sample rate is not officially supported - audio might be noisy.\nTry using sample rate below or equal to 16000"); } if(_bitsPerSample != 16){ - log_w("This bit-per-sample is not officially supported - audio quality might suffer. Try using 16bps, with sample rate below equal 16000"); + log_w("This bit-per-sample is not officially supported - audio quality might suffer.\nTry using 16bps, with sample rate below or equal 16000"); } if(_mode != I2S_PHILIPS_MODE){ - log_w("This mode is not officially supported - audio quality might suffer. At the moment the only supported mode is I2S_PHILIPS_MODE"); + log_w("This mode is not officially supported - audio quality might suffer.\nAt the moment the only supported mode is I2S_PHILIPS_MODE"); } if (_state != I2S_STATE_IDLE && _state != I2S_STATE_DUPLEX) { @@ -288,7 +294,7 @@ int I2SClass::begin(int mode, int sampleRate, int bitsPerSample, bool driveClock return 0; // ERR } - _buffer_byte_size = _i2s_dma_buffer_size * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT; + _buffer_byte_size = _i2s_dma_buffer_size * (_bitsPerSample / 8) * _I2S_DMA_BUFFER_COUNT * 2; _input_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); _output_ring_buffer = xRingbufferCreate(_buffer_byte_size, RINGBUF_TYPE_BYTEBUF); if(_input_ring_buffer == NULL || _output_ring_buffer == NULL){ @@ -322,9 +328,9 @@ int I2SClass::_applyPinSetting(){ }else{ // simplex if(_state == I2S_STATE_RECEIVER){ pin_config.data_out_num = I2S_PIN_NO_CHANGE; - pin_config.data_in_num = _inSdPin>0 ? _inSdPin : _sdPin; + pin_config.data_in_num = _sdPin; }else if(_state == I2S_STATE_TRANSMITTER){ - pin_config.data_out_num = _outSdPin>0 ? _outSdPin : _sdPin; + pin_config.data_out_num = _sdPin; pin_config.data_in_num = I2S_PIN_NO_CHANGE; }else{ pin_config.data_out_num = I2S_PIN_NO_CHANGE; @@ -332,7 +338,7 @@ int I2SClass::_applyPinSetting(){ } } if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, &pin_config)){ - log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d", _sckPin, _fsPin, pin_config.data_in_num, pin_config.data_out_num); + log_e("i2s_set_pin failed; attempted settings: SCK=%d; FS=%d; DIN=%d; DOUT=%d", pin_config.bck_io_num, pin_config.ws_io_num, pin_config.data_in_num, pin_config.data_out_num); return 0; // ERR }else{ return 1; // OK @@ -342,11 +348,13 @@ int I2SClass::_applyPinSetting(){ } void I2SClass::_setSckPin(int sckPin){ + _take_if_not_holding(); if(sckPin >= 0){ _sckPin = sckPin; }else{ _sckPin = PIN_I2S_SCK; } + _give_if_top_call(); } int I2SClass::setSckPin(int sckPin){ @@ -374,11 +382,29 @@ int I2SClass::setFsPin(int fsPin){ return ret; } +// shared data pin for simplex +void I2SClass::_setDataPin(int sdPin){ + if(sdPin >= 0){ + _sdPin = sdPin; + }else{ + _sdPin = PIN_I2S_SD; + } +} + +// shared data pin for simplex +int I2SClass::setDataPin(int sdPin){ + _take_if_not_holding(); + _setDataPin(sdPin); + int ret = _applyPinSetting(); + _give_if_top_call(); + return ret; +} + void I2SClass::_setDataInPin(int inSdPin){ if(inSdPin >= 0){ _inSdPin = inSdPin; }else{ - _inSdPin = PIN_I2S_SD; + _inSdPin = PIN_I2S_SD_IN; } } @@ -394,7 +420,7 @@ void I2SClass::_setDataOutPin(int outSdPin){ if(outSdPin >= 0){ _outSdPin = outSdPin; }else{ - _outSdPin = PIN_I2S_SD_OUT; + _outSdPin = PIN_I2S_SD; } } @@ -408,19 +434,21 @@ int I2SClass::setDataOutPin(int outSdPin){ int I2SClass::setAllPins(){ _take_if_not_holding(); - int ret = setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT); + int ret = setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SD_IN); _give_if_top_call(); return ret; } -int I2SClass::setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin){ +int I2SClass::setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin){ _take_if_not_holding(); _setSckPin(sckPin); _setFsPin(fsPin); - _setDataInPin(inSdPin); + _setDataPin(sdPin); _setDataOutPin(outSdPin); + _setDataInPin(inSdPin); + int ret = _applyPinSetting(); _give_if_top_call(); - return 1; // OK + return ret; } int I2SClass::setDuplex(){ @@ -566,12 +594,10 @@ int I2SClass::read(void* buffer, size_t size){ _take_if_not_holding(); size_t requested_size = size; if(_initialized){ - if (_state != I2S_STATE_RECEIVER && _state != I2S_STATE_DUPLEX) { - if(!_enableReceiver()){ - _give_if_top_call(); - return 0; // There was an error switching to receiver - } // _enableReceiver succeeded ? - } // _state ? + if(!_enableReceiver()){ + _give_if_top_call(); + return 0; // There was an error switching to receiver + } // _enableReceiver succeeded ? size_t item_size = 0; void *tmp_buffer; @@ -652,12 +678,10 @@ size_t I2SClass::write(const void *buffer, size_t size){ size_t I2SClass::write_blocking(const void *buffer, size_t size){ _take_if_not_holding(); if(_initialized){ - if (_state != I2S_STATE_TRANSMITTER && _state != I2S_STATE_DUPLEX){ - if(!_enableTransmitter()){ - _give_if_top_call(); - return 0; // There was an error switching to transmitter - } // _enableTransmitter succeeded ? - } // _state ? + if(!_enableTransmitter()){ + _give_if_top_call(); + return 0; // There was an error switching to transmitter + } // _enableTransmitter succeeded ? if(_output_ring_buffer != NULL){ int ret = xRingbufferSend(_output_ring_buffer, buffer, size, portMAX_DELAY); @@ -736,7 +760,7 @@ int I2SClass::peek(){ void I2SClass::flush(){ _take_if_not_holding(); if(_initialized){ - const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8); + const size_t single_dma_buf = _i2s_dma_buffer_size*(_bitsPerSample/8)*2; size_t item_size = 0; void *item = NULL; if(_output_ring_buffer != NULL){ @@ -781,7 +805,7 @@ int I2SClass::setBufferSize(int bufferSize){ if(bufferSize >= 8 && bufferSize <= 1024){ _i2s_dma_buffer_size = bufferSize; }else{ - log_e("setBufferSize: wrong input! Buffer size must be between 8 and 1024. Requested %d\n ", bufferSize); + log_e("setBufferSize: wrong input! Buffer size must be between 8 and 1024. Requested %d", bufferSize); _give_if_top_call(); return 0; // ERR } // check requested buffer size @@ -1025,7 +1049,7 @@ void I2SClass::_fix_and_write(void *output, size_t size, size_t *bytes_written, #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) -int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ +int I2SClass::_gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ switch(gpio_num){ #if CONFIG_IDF_TARGET_ESP32 // ADC 1 @@ -1105,7 +1129,7 @@ int I2SClass::gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit){ } } -int I2SClass::gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel){ +int I2SClass::_gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel){ switch(gpio_num){ #if CONFIG_IDF_TARGET_ESP32 // ADC 1 diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h index 2727f875c0a..623fa8917b4 100644 --- a/libraries/I2S/src/I2S.h +++ b/libraries/I2S/src/I2S.h @@ -40,13 +40,17 @@ namespace esp_i2s { #endif #ifndef PIN_I2S_SD - #define PIN_I2S_SD 35 // Pin 35 is only input! + #define PIN_I2S_SD 26 #endif #ifndef PIN_I2S_SD_OUT #define PIN_I2S_SD_OUT 26 #endif +#ifndef PIN_I2S_SD_IN + #define PIN_I2S_SD_IN 35 // Pin 35 is only input! +#endif + typedef enum { I2S_PHILIPS_MODE, I2S_RIGHT_JUSTIFIED_MODE, @@ -59,28 +63,31 @@ typedef enum { class I2SClass : public Stream { public: - // the device index and pins must map to the "COM" pads in Table 6-1 of the datasheet + // The device index and pins must map to the "COM" pads in Table 6-1 of the datasheet I2SClass(uint8_t deviceIndex, uint8_t clockGenerator, uint8_t sdPin, uint8_t sckPin, uint8_t fsPin); - // the SCK and FS pins are driven as outputs using the sample rate + + // Init in MASTER mode: the SCK and FS pins are driven as outputs using the sample rate int begin(int mode, int sampleRate, int bitsPerSample); - // the SCK and FS pins are inputs, other side controls sample rate + + // Init in SLAVE mode: the SCK and FS pins are inputs, other side controls sample rate int begin(int mode, int bitsPerSample); // change pin setup and mode (default is Half Duplex) // Can be called only on initialized object (after begin) int setSckPin(int sckPin); int setFsPin(int fsPin); - int setDataInPin(int inSdPin); + int setDataPin(int sdPin); // shared data pin for simplex int setDataOutPin(int outSdPin); + int setDataInPin(int inSdPin); int setAllPins(); - int setAllPins(int sckPin, int fsPin, int inSdPin, int outSdPin); + int setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin); int getSckPin(); int getFsPin(); int getDataPin(); - int getDataInPin(); int getDataOutPin(); + int getDataInPin(); int setDuplex(); int setSimplex(); @@ -113,11 +120,11 @@ class I2SClass : public Stream int setBufferSize(int bufferSize); int getBufferSize(); +private: #if (SOC_I2S_SUPPORTS_ADC && SOC_I2S_SUPPORTS_DAC) - int gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit); - int gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel); + int _gpioToAdcUnit(gpio_num_t gpio_num, esp_i2s::adc_unit_t* adc_unit); + int _gpioToAdcChannel(gpio_num_t gpio_num, esp_i2s::adc_channel_t* adc_channel); #endif -private: int begin(int mode, int sampleRate, int bitsPerSample, bool driveClock); int _enableTransmitter(); @@ -131,8 +138,9 @@ class I2SClass : public Stream void _uninstallDriver(); void _setSckPin(int sckPin); void _setFsPin(int fsPin); - void _setDataInPin(int inSdPin); + void _setDataPin(int sdPin); void _setDataOutPin(int outSdPin); + void _setDataInPin(int inSdPin); int _applyPinSetting(); private: From 459ed79430a437efd8d03d3cf0b01a640b017c62 Mon Sep 17 00:00:00 2001 From: "pedro.minatel" Date: Tue, 1 Feb 2022 14:33:26 +0000 Subject: [PATCH 90/94] Structure review and syntax fixes --- docs/source/api/i2s.rst | 520 +++++++++++++++++++++++++++------------- 1 file changed, 352 insertions(+), 168 deletions(-) diff --git a/docs/source/api/i2s.rst b/docs/source/api/i2s.rst index 69d649a0906..02539fb3f2b 100644 --- a/docs/source/api/i2s.rst +++ b/docs/source/api/i2s.rst @@ -5,19 +5,23 @@ I2S About ----- -I2S - Inter-IC Sound, correctly written I²S pronounced "eye-squared-ess", alternative notation is IIS. I²S is an electrical serial bus interface standard used for connecting digital audio devices together. It is used to communicate PCM (Pulse-Code Modulation) audio data between integrated circuits in an electronic device. The I²S bus separates clock and serial data signals, resulting in simpler receivers than those required for asynchronous communications systems that need to recover the clock from the data stream. Despite the similar name, I²S is unrelated and incompatible with the bidirectional I²C (IIC) bus. +I2S - Inter-IC Sound, correctly written I²S pronounced "eye-squared-ess", alternative notation is IIS. I²S is an electrical serial bus interface standard used for connecting digital audio devices together. + +It is used to communicate PCM (Pulse-Code Modulation) audio data between integrated circuits in an electronic device. The I²S bus separates clock and serial data signals, resulting in simpler receivers than those required for asynchronous communications systems that need to recover the clock from the data stream. + +Despite the similar name, I²S is unrelated and incompatible with the bidirectional I²C (IIC) bus. The I²S bus consists of at least three lines: -All lines can be attached to almost any pin and this change can occur even during operation. +.. note:: All lines can be attached to almost any pin and this change can occur even during operation. * **Bit clock line** - + * Officially "continuous serial clock (SCK)". Typically written "bit clock (BCLK)". * In this library function parameter ``sckPin`` or constant ``PIN_I2S_SCK``. * **Word clock line** - + * Officially "word select (WS)". Typically called "left-right clock (LRCLK)" or "frame sync (FS)". * 0 = Left channel, 1 = Right channel * In this library function parameter ``fsPin`` or constant ``PIN_I2S_FS``. @@ -26,8 +30,10 @@ All lines can be attached to almost any pin and this change can occur even durin * Officially "serial data (SD)", but can be called SDATA, SDIN, SDOUT, DACDAT, ADCDAT, etc. * Unlike Arduino I2S with single data pin switching between input and output, in ESP core driver use separate data line for input and output. - * For backward compatibility the shared data pin is ``sdPin`` or constant ``PIN_I2S_SD`` when using simplex mode. - * When using duplex mode there are two data lines: + * For backward compatibility, the shared data pin is ``sdPin`` or constant ``PIN_I2S_SD`` when using simplex mode. + + * When using in duplex mode, there are two data lines: + * Output data line is called ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` * Input data line is called ``inSdPin`` for function parameter, or constant ``PIN_I2S_SD_IN`` @@ -36,254 +42,390 @@ I2S Modes The I2S can be set up in three groups of modes: - * Master (default) or Slave - * Simplex (default) or Duplex - * Operation modes (Philips standard, ADC/DAC, PDM) - * Most of them are dual channel, some can be single channel +* Master (default) or Slave. +* Simplex (default) or Duplex. +* Operation modes (Philips standard, ADC/DAC, PDM) + + * Most of them are dual-channel, some can be single channel .. note:: Officially supported operation mode is only ``I2S_PHILIPS_MODE``. Other modes are implemented, but we cannot guarantee flawless execution and behavior. -Master / slave mode +Master / Slave Mode ******************* In **Master mode** (default) the device is generating clock signal ``sckPin`` and word select signal on ``fsPin``. -In **Slave mode** the device listens on attached pins for clock signal and word select - i.e. unless externally driven the pins will remain LOW. +In **Slave mode** the device listens on attached pins for the clock signal and word select - i.e. unless externally driven the pins will remain LOW. -How to enter either mode is described in function section. +How to enter either mode is described in the function section. -Operation modes +Operation Modes *************** Setting the operation mode is done with function ``begin`` (see API section) * ``I2S_PHILIPS_MODE`` - Currently the only officially supported mode. - This mode is using original Philips specification for I2S bus. See their paper https://web.archive.org/web/20080706121949/http://www.nxp.com/acrobat_download/various/I2SBUS.pdf - - .. Note::Following modes currently not officially supported. Using any of the following modes will print warning, but continue to operate. However the quality is not guaranteed and the application may crash. - -* ``I2S_RIGHT_JUSTIFIED_MODE`` - In this mode, you only need to send one channel data but the data will be copied for another channel automatically, then both channels will transmit same data. - -* ``I2S_LEFT_JUSTIFIED_MODE`` - In this mode, you only need to send one channel data but the data will be copied for another channel automatically, then both channels will transmit same data. + * Currently the only official* ``PIN_I2S_SCK`` +* ``PIN_I2S_FS`` +* ``PIN_I2S_SD`` +* ``PIN_I2S_SD_OUT`` only need to send one channel data but the data will be copied for another channel automatically, then both channels will transmit same data. * ``ADC_DAC_MODE`` - Output will be analog signal on pins 25 (L or R?) and 26 (L or R?). + The output will be an analog signal on pins ``25`` (L or R?) and ``26`` (L or R?). Input will be received on pin ``_inSdPin``. - The data are sampled on 12 bits and stored in 16 bits with 4 most significant bits set to zero. + The data are sampled in 12 bits and stored in a 16 bits, with the 4 most significant bits set to zero. * ``PDM_STEREO_MODE`` - Pulse-density-modulation is similar to PWM, but instead the pulses have constant width. The signal is modulated with number of ones or zeroes in sequence. + Pulse-density-modulation is similar to PWM, but instead, the pulses have constant width. The signal is modulated with the number of ones or zeroes in sequence. * ``PDM_MONO_MODE`` Single-channel version of PDM mode described above. -Simplex / duplex mode +Simplex / Duplex Mode ********************* -The **Simplex** mode is default after driver initialization. Simplex mode is using shared data pin ``sdPin`` or constant ``PIN_I2S_SD`` for both output and input, but can only read or write. This is the same behavior as in original Arduino library. +The **Simplex** mode is the default after driver initialization. Simplex mode uses the shared data pin ``sdPin`` or constant ``PIN_I2S_SD`` for both output and input, but can only read or write. This is the same behavior as in original Arduino library. The **Duplex** mode uses two separate data pins: - * Output pin ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` - * Input pin ``inSdPin`` for function parameter, or constant ``PIN_I2S_SD_IN`` -In this mode the driver is able to read and write simultaneously on each line and is suitable for applications like walkie-talkie or phone. +* Output pin ``outSdPin`` for function parameter, or constant ``PIN_I2S_SD_OUT`` +* Input pin ``inSdPin`` for function parameter, or constant ``PIN_I2S_SD_IN`` -Switching between these modes is performed simply by calling setDuplex() or setSimplex() (see APi section for details and more functions). +In this mode, the driver is able to read and write simultaneously on each line and is suitable for applications like walkie-talkie or phone. +Switching between these modes is performed simply by calling setDuplex() or setSimplex() (see APi section for details and more functions). Arduino-ESP32 I2S API --------------------- -The ESP32 I2S library is based on the Arduino I2S Library and implements a few more APIs, described in this documentation. -https://www.arduino.cc/en/Reference/I2S +The ESP32 I2S library is based on the Arduino I2S Library and implements a few more APIs, described in this `documentation `_. Initialization and deinitialization *********************************** -Before usage choose which pins you want to use. In DAC mode you can use only pins 25 and 26 for output. -int begin(int mode, int sampleRate, int bitsPerSample) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Performs initialization before use - creates buffers, task handling underlying driver messages, configuring and starting the driver operation. +Before initialization, choose which pins you want to use. In DAC mode you can use only pins `25` and `26` for the output. -This version initializes I2S in MASTER mode (see next entry for SLAVE mode). +begin (Master Mode) +^^^^^^^^^^^^^^^^^^^ + +Before usage choose which pins you want to use. In DAC mode you can use only pins 25 and 26 as output. + +.. code-block:: arduino + + int begin(int mode, int sampleRate, int bitsPerSample) + +Parameters: + +* [in] ``mode`` one of above mentioned operation mode, for example ``I2S_PHILIPS_MODE``. + +* [in] ``sampleRate`` is the sampling rate in Hz. Currently officially supported value is only 16000 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. -parameters: - [in] ``mode`` one of above mentioned operation mode, for example ``I2S_PHILIPS_MODE``. +* [in] ``bitsPerSample`` is the number of bits in a channel sample. + +Currently, the supported value is only 16 - other than this value will print a warning, but continues to operate, however, the resulting audio quality may suffer and the application may crash. - [in] ``sampleRate`` sampling rate in Hz. Currently officially supported value is only 16000 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. +For ``ADC_DAC_MODE`` the only possible value will remain 16. - [in] ``bitsPerSample`` Number of bits in a channel sample. Currently officially supported value is only 16 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. - For ``ADC_DAC_MODE`` the only possible value will remain 16. +This function will return ``true`` on success or ``fail`` in case of failure. -Returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. +When failed, an error message will be printed if subscribed. + +begin (Slave Mode) +^^^^^^^^^^^^^^^^^^ -int begin(int mode, int bitsPerSample) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Performs initialization before use - creates buffers, task handling underlying driver messages, configuring and starting the driver operation. This version initializes I2S in SLAVE mode (see previous entry for MASTER mode). -parameters: - [in] ``mode`` one of above mentioned modes for example ``I2S_PHILIPS_MODE``. +.. code-block:: arduino - [in] ``bitsPerSample`` Number of bits in a channel sample. Currently officially supported value is only 16 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. - For ``ADC_DAC_MODE`` the only possible value will remain 16. + int begin(int mode, int bitsPerSample) -Returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. +Parameters: + +* [in] ``mode`` one of above mentioned modes for example ``I2S_PHILIPS_MODE``. + +* [in] ``bitsPerSample`` is the umber of bits in a channel sample. Currently, the only supported value is only 16 - other than this value will print warning, but continue to operate, however the resulting audio quality may suffer and the app may crash. + +For ``ADC_DAC_MODE`` the only possible value will remain 16. + +This function will return ``true`` on success or ``fail`` in case of failure. + +When failed, an error message will be printed if subscribed. + +end +^^^ -void end() -^^^^^^^^^^ Performs safe deinitialization - free buffers, destroy task, end driver operation, etc. +.. code-block:: arduino + + void end() + Pin setup ********* -Pins can changed in two ways- 1st constants, 2nd functions. -..Note:: Shared data pin can be equal to any other data pin, but must not be equal to clock pin nor frame sync pin! Input and Output pins must not be equal, but one of them can be equal to shared data pin! +Pins can be changed in two ways- 1st constants, 2nd functions. -sckPin != fsPin != outSdPin != inSdPin +.. note:: Shared data pin can be equal to any other data pin, but must not be equal to clock pin nor frame sync pin! Input and Output pins must not be equal, but one of them can be equal to shared data pin! -sckPin != fsPin != sdPin +.. code-block:: arduino -By default the pin numbers are defined in constants in the header file. You can redefine any of those constants before including ``I2S.h``. This way the driver will use these new default values and you will not need to specify pins in your code. The constants and their default values are: + sckPin != fsPin != outSdPin != inSdPin -* ``PIN_I2S_SCK`` 14 +.. code-block:: arduino -* ``PIN_I2S_FS`` 25 + sckPin != fsPin != sdPin -* ``PIN_I2S_SD`` 26 +By default, the pin numbers are defined in constants in the header file. You can redefine any of those constants before including ``I2S.h``. This way the driver will use these new default values and you will not need to specify pins in your code. The constants and their default values are: +* ``PIN_I2S_SCK`` 14 +* ``PIN_I2S_FS`` 25 +* ``PIN_I2S_SD`` 26 * ``PIN_I2S_SD_OUT`` 26 - * ``PIN_I2S_SD_IN`` 35 -Second option to change pins is using the following functions. These functions can be called on either on initialized or uninitialized object. -If called on initialized object (after calling ``begin``) the pins will change during operation. -If called on uninitialized object (before calling ``begin``, or after calling ``end``) the new pin setup will be used on next initialization. +The second option to change pins is using the following functions. These functions can be called on either on initialized or uninitialized object. + +If called on the initialized object (after calling ``begin``) the pins will change during operation. +If called on the uninitialized object (before calling ``begin``, or after calling ``end``) the new pin setup will be used on next initialization. +setSckPin +^^^^^^^^^ -int setSckPin(int sckPin) -^^^^^^^^^^^^^^^^^^^^^^^^^ Set and apply clock pin. -Returns 1 on success, 0 on failure. +.. code-block:: arduino + + int setSckPin(int sckPin) + +This function will return ``true`` on success or ``fail`` in case of failure. + +setFsPin +^^^^^^^^ -int setFsPin(int fsPin) -^^^^^^^^^^^^^^^^^^^^^^^ Set and apply frame sync pin. -Returns 1 on success, 0 on failure. +.. code-block:: arduino + + int setFsPin(int fsPin) + +This function will return ``true`` on success or ``fail`` in case of failure. + +setDataPin +^^^^^^^^^^ -int setDataPin(int sdPin) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set and apply shared data pin used in simplex mode. -Returns 1 on success, 0 on failure. +.. code-block:: arduino + + int setDataPin(int sdPin) + +This function will return ``true`` on success or ``fail`` in case of failure. + +setDataInPin +^^^^^^^^^^^^ -int setDataInPin(int inSdPin) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set and apply data input pin. -Returns 1 on success, 0 on failure. +.. code-block:: arduino + + int setDataInPin(int inSdPin) + +This function will return ``true`` on success or ``fail`` in case of failure. + +setDataOutPin +^^^^^^^^^^^^^ -int setDataOutPin(int outSdPin) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set and apply data output pin. -Returns 1 on success, 0 on failure. -int setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. code-block:: arduino + + int setDataOutPin(int outSdPin) + +This function will return ``true`` on success or ``fail`` in case of failure. + +setAllPins +^^^^^^^^^^ + Set all pins using given values in parameters. This is simply a wrapper of four functions mentioned above. -int setAllPins() -^^^^^^^^^^^^^^^^ -Set all pins to default i.e. take values from constants mentioned above. This simply calls the the function ``setAllPins(PIN_I2S_SCK, PIN_I2S_FS, PIN_I2S_SD, PIN_I2S_SD_OUT, PIN_I2S_SD_IN);`` +.. code-block:: arduino -int getSckPin() -^^^^^^^^^^^^^^^ -Get current value of clock pin. + int setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin) -int getFsPin() -^^^^^^^^^^^^^^ -Get current value of frame sync pin. +Set all pins to default i.e. take values from constants mentioned above. This simply calls the the function with the following constants. + +* ``PIN_I2S_SCK`` 14 +* ``PIN_I2S_FS`` 25 +* ``PIN_I2S_SD`` 26 +* ``PIN_I2S_SD_OUT`` 26 +* ``PIN_I2S_SD_IN`` 35 -int getDataPin() -^^^^^^^^^^^^^^^^ -Get current value of shared data pin. +.. code-block:: arduino -int getDataInPin() -^^^^^^^^^^^^^^^^^^ -Get current value of data input pin. + int setAllPins() -int getDataOutPin() -^^^^^^^^^^^^^^^^^^^ -Get current value of data output pin. +getSckPin +^^^^^^^^^ + +Get the current value of the clock pin. + +.. code-block:: arduino + + int getSckPin() + +getFsPin +^^^^^^^^ + +Get the current value of frame sync pin. + +.. code-block:: arduino + + int getFsPin() + +getDataPin +^^^^^^^^^^ + +Get the current value of shared data pin. + +.. code-block:: arduino + + int getDataPin() +getDataInPin +^^^^^^^^^^^^ + +Get the current value of data input pin. + +.. code-block:: arduino + + int getDataInPin() + +getDataOutPin +^^^^^^^^^^^^^ + +Get the current value of data output pin. + +.. code-block:: arduino + + int getDataOutPin() + +onTransmit +^^^^^^^^^^ -void onTransmit(void(*)(void)) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Register function which will be called on each successful i2s driver transmit event. +Register the function to be called on each successful transmit event. -void onReceive(void(*)(void)) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Register function which will be called on each successful i2s driver receive event. +.. code-block:: arduino -int setBufferSize(int bufferSize) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Set new size of buffer. + void onTransmit(void(*)(void)) + +onReceive +^^^^^^^^^ + +Register the function to be called on each successful receives event. + +.. code-block:: arduino + + void onReceive(void(*)(void)) + +setBufferSize +^^^^^^^^^^^^^ + +Set the size of buffer. + +.. code-block:: arduino + + int setBufferSize(int bufferSize) + +This function can be called on both the initialized or uninitialized driver. -This function can be called both on initialized and uninitialized driver. If called on initialized, it will change internal values for buffer size and re-initialize driver with new value. If called on uninitialized, it will only change the internal values which will be used for next initialization. -Parameter ``bufferSize`` must be in range <8; 1024>. the unit is sample words. Default value on object creation is 128. -Example: 16 bit sample, dual channel, buffer size 128 = 2B sample * 2 channels * 128 buffer size * buffer count (default 2) = 1024B for input buffer + 1024B for output buffer = total 2kB used. +Parameter ``bufferSize`` must be in range from 8 to 1024 and the unit is sample words. The default value is 128. -This function always assumes dual channel, keeping the same size even for MONO modes. +Example: 16 bit sample, dual channel, buffer size for input: -Returns 1 on success, 0 on failure. When failed an error message will be printed if subscribed. + ``128 = 2B sample * 2 channels * 128 buffer size * buffer count (default 2) = 1024B`` + +And more ```1024B`` for output buffer in total of ``2kB`` used. + +This function always assumes dual-channel, keeping the same size even for MONO modes. + +This function will return ``true`` on success or ``fail`` in case of failure. + +When failed, an error message will be printed. + +getBufferSize +^^^^^^^^^^^^^ -int getBufferSize() -^^^^^^^^^^^^^^^^^^^ Get current buffer sizes in sample words (see description for ``setBufferSize``). +.. code-block:: arduino + + int getBufferSize() + Duplex vs Simplex ***************** -Original Arduino I2S library supports only *simplex* mode (only transmit or only receive at a time). For compatibility we kept this behavior, but ESP natively supports *duplex* mode (receive and transmit simultaneously on separate pins). -By default this library is initialized in simplex mode as it would in Arduino, switching input and output on sdPin (constant PIN_I2S_SD) (default pin 26). +Original Arduino I2S library supports only *simplex* mode (only transmit or only receive at a time). For compatibility, we kept this behavior, but ESP natively supports *duplex* mode (receive and transmit simultaneously on separate pins). +By default this library is initialized in simplex mode as it would in Arduino, switching input and output on ``sdPin`` (constant ``PIN_I2S_SD`` default pin 26). + +setDuplex +^^^^^^^^^ -int setDuplex() -^^^^^^^^^^^^^^^ Switch to duplex mode and use separate pins: + +.. code-block:: arduino + + int setDuplex() + input: inSdPin (constant PIN_I2S_SD_IN, default 35) output: outSdPin (constant PIN_I2S_SD, default 26) -int setSimplex() -^^^^^^^^^^^^^^^^ +setSimplex +^^^^^^^^^^ + (Default mode) Switch to simplex mode using shared data pin sdPin (constant PIN_I2S_SD, default 26). -int isDuplex() -^^^^^^^^^^^^^^ +.. code-block:: arduino + + int setSimplex() + +isDuplex +^^^^^^^^ + Returns 1 if current mode is duplex, 0 if current mode is simplex (default). +.. code-block:: arduino + + int isDuplex() + Data stream *********** -int available() -^^^^^^^^^^^^^^^ -Returns number of **BYTES** ready to read +available +^^^^^^^^^ + +Returns number of **bytes** ready to read. -int read(void* buffer, size_t size) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Read ``size`` Bytes from internal buffer if possible. +.. code-block:: arduino -This function is non-blocking, i.e. if requested number of Bytes is not available it will return as much as possible without waiting. + int available() + +read +^^^^ + +Read ``size`` bytes from internal buffer if possible. + +.. code-block:: arduino + + int read(void* buffer, size_t size) + +This function is non-blocking, i.e. if the requested number of bytes is not available, it will return as much as possible without waiting. Hint: use ``available()`` before calling this function. @@ -291,78 +433,120 @@ Parameters: [out] ``void* buffer`` buffer into which will be copied data read from internal buffer. WARNING: this buffer must be allocated before use! -[in] ``size_t size`` number of Bytes required to be read. -Returns number of successfully read Bytes. Returns 0 on error. +[in] ``size_t size`` number of bytes required to be read. -int read() -^^^^^^^^^^ -Read one sample +Returns number of successfully bytes read. Returns ``false``` in case of reading error. -int peek() -^^^^^^^^^^ -Read 1 sample from internal buffer and return it. -Repeated peeks will return the same sample until read is called. +Read one sample. +.. code-block:: arduino + + int read() + +peek +^^^^ + +Read one sample from the internal buffer and returns it. + +.. code-block:: arduino + + int peek() + +Repeated peeks will be returned in the same sample until ``read`` is called. + +flush +^^^^^ -void flush() -^^^^^^^^^^^^ Force write internal buffer to driver. -size_t write(uint8_t) -^^^^^^^^^^^^^^^^^^^^^ -Write single Byte. +.. code-block:: arduino + + void flush() + +write +^^^^^ + +Write a single byte. + +.. code-block:: arduino -Single-sample writes are blocking - waiting until there is free space in internal buffer to be written into. + size_t write(uint8_t) -Returns number of successfully written Bytes, in this case 1. Returns 0 on error. +Single-sample writes are blocking - waiting until there is free space in the internal buffer to be written into. -size_t write(int32_t) -^^^^^^^^^^^^^^^^^^^^^ -Write sample. +Returns number of successfully written bytes, in this case, 1. Returns 0 on error. -Single-sample writes are blocking - waiting until there is free space in internal buffer to be written into. +Write single sample. + +.. code-block:: arduino + + size_t write(int32_t) + +Single-sample writes are blocking - waiting until there is free space in the internal buffer to be written into. Returns number of successfully written bytes. Returns 0 on error. Expected return number is ``bitsPerSample/8``. -size_t write(const void *buffer, size_t size) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Write buffer of supplied size; +.. code-block:: arduino + + size_t write(const void *buffer, size_t size) + Parameters: [in] ``const void *buffer`` buffer to be written +[in] ``size_t size`` size of buffer in bytes -[in] ``size_t size`` size of buffer in Bytes +Returns number of successfully written bytes. Returns 0 in case of error. +The expected return number is equal to ``size``. -Returns number of successfully written bytes. Returns 0 on error. -Expected return number is equal to ``size``. +write +^^^^^ + +This is a wrapper of the previous function performing typecast from `uint8_t*`` to ``void*``. + +.. code-block:: arduino + + size_t write(const uint8_t *buffer, size_t size) + +availableForWrite +^^^^^^^^^^^^^^^^^ -size_t write(const uint8_t *buffer, size_t size) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -This is a wrapper of previous function performing typecast from `uint8_t*`` to ``void*``. +Returns number of bytes available for write. -int availableForWrite() -^^^^^^^^^^^^^^^^^^^^^^^ -Returns number of **BYTES** available for write. +.. code-block:: arduino + int availableForWrite() + +write_blocking +^^^^^^^^^^^^^^ -size_t write_blocking(const void *buffer, size_t size) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Core function implementing blocking write, i.e. waits until all requested data are written. + +.. code-block:: arduino + + size_t write_blocking(const void *buffer, size_t size) + WARNING: If too many bytes are requested, this can cause WatchDog Trigger Reset! Returns number of successfully written bytes. Returns 0 on error. -size_t write_nonblocking(const void *buffer, size_t size) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +write_nonblocking +^^^^^^^^^^^^^^^^^ + Core function implementing non-blocking write, i.e. writes as much as possible and exits. +.. code-block:: arduino + + size_t write_nonblocking(const void *buffer, size_t size) + Returns number of successfully written bytes. Returns 0 on error. Sample code ----------- + .. code-block:: c #include From 6591f5bd4cf4d3ef7c9c783680d8b06678d3c449 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Thu, 3 Feb 2022 19:07:34 +0100 Subject: [PATCH 91/94] Fix replace() failing (#6224) --- cores/esp32/WString.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp index 4badf63d0fe..f69302a253f 100644 --- a/cores/esp32/WString.cpp +++ b/cores/esp32/WString.cpp @@ -774,9 +774,10 @@ void String::replace(const String& find, const String& replace) { } if(size == len()) return; - if(size > capacity() && !changeBuffer(size)) + if(size > capacity() && !changeBuffer(size)) { log_w("String.Replace() Insufficient space to replace string"); return; + } int index = len() - 1; while(index >= 0 && (index = lastIndexOf(find, index)) >= 0) { readFrom = wbuffer() + index + find.len(); From 1046f59f6bc3d60534171ae4e61bb4578247316a Mon Sep 17 00:00:00 2001 From: Dmitry Bondarenko Date: Thu, 3 Feb 2022 21:09:18 +0300 Subject: [PATCH 92/94] Upload to the component registry (#6203) Co-authored-by: Sergei Silnov --- .github/workflows/upload-idf-component.yml | 19 +++++++++++++++++++ idf_component.yml | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 .github/workflows/upload-idf-component.yml create mode 100644 idf_component.yml diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml new file mode 100644 index 00000000000..d9e4032df08 --- /dev/null +++ b/.github/workflows/upload-idf-component.yml @@ -0,0 +1,19 @@ +name: Push components to https://components.espressif.com +on: + push: + tags: + - v* +jobs: + upload_components: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: "recursive" + + - name: Upload components to the component registry + uses: espressif/github-actions/upload_components@master + with: + name: arduino-esp32 + namespace: espressif + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 00000000000..72e81d7b2a9 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,20 @@ +description: "Arduino core for ESP32, ESP32-S and ESP32-C series of SoCs" +url: "https://github.com/espressif/arduino-esp32" +targets: + - esp32 + - esp32s2 + - esp32c3 +tags: + - arduino +files: + include: + - "cores/**/*" + - "variants/esp32/**/*" + - "variants/esp32s2/**/*" + - "variants/esp32s3/**/*" + - "variants/esp32c3/**/*" + - "libraries/**/*" + - "CMakeLists.txt" + - "Kconfig.projbuild" + exclude: + - "**/*" \ No newline at end of file From bb4d9027dda9f1ac8a73217404294a3c06cb6607 Mon Sep 17 00:00:00 2001 From: "Limor \"Ladyada\" Fried" Date: Thu, 3 Feb 2022 13:10:54 -0500 Subject: [PATCH 93/94] add feather esp32 v2 and qtpy c3 board def (#6223) * add feather esp32 v2 and qtpy c3 board def update some pin names add variant.cpp's to auto-enable i2c, tft, neopixels on boot * add auto-enable for i2c! --- boards.txt | 278 ++++++++++++++++++ .../adafruit_feather_esp32_v2/pins_arduino.h | 73 +++++ .../adafruit_feather_esp32_v2/variant.cpp | 40 +++ .../adafruit_feather_esp32s2/pins_arduino.h | 1 + variants/adafruit_qtpy_esp32/pins_arduino.h | 8 +- variants/adafruit_qtpy_esp32/variant.cpp | 40 +++ variants/adafruit_qtpy_esp32c3/pins_arduino.h | 34 +++ variants/feather_esp32/pins_arduino.h | 3 +- 8 files changed, 471 insertions(+), 6 deletions(-) create mode 100644 variants/adafruit_feather_esp32_v2/pins_arduino.h create mode 100644 variants/adafruit_feather_esp32_v2/variant.cpp create mode 100644 variants/adafruit_qtpy_esp32/variant.cpp create mode 100644 variants/adafruit_qtpy_esp32c3/pins_arduino.h diff --git a/boards.txt b/boards.txt index 085c03e56d3..7517c647229 100755 --- a/boards.txt +++ b/boards.txt @@ -5088,6 +5088,284 @@ adafruit_qtpy_esp32s2.menu.DebugLevel.debug.build.code_debug=4 adafruit_qtpy_esp32s2.menu.DebugLevel.verbose=Verbose adafruit_qtpy_esp32s2.menu.DebugLevel.verbose.build.code_debug=5 +############################################################## + +adafruit_qtpy_esp32c3.name=Adafruit QT Py ESP32-C3 +adafruit_qtpy_esp32c3.vid.0=0x303a +adafruit_qtpy_esp32c3.pid.0=0x1001 + +adafruit_qtpy_esp32c3.upload.tool=esptool_py +adafruit_qtpy_esp32c3.upload.maximum_size=1310720 +adafruit_qtpy_esp32c3.upload.maximum_data_size=327680 +adafruit_qtpy_esp32c3.upload.flags= +adafruit_qtpy_esp32c3.upload.extra_flags= +adafruit_qtpy_esp32c3.upload.use_1200bps_touch=false +adafruit_qtpy_esp32c3.upload.wait_for_upload_port=false + +adafruit_qtpy_esp32c3.serial.disableDTR=false +adafruit_qtpy_esp32c3.serial.disableRTS=false + +adafruit_qtpy_esp32c3.build.tarch=riscv32 +adafruit_qtpy_esp32c3.build.target=esp +adafruit_qtpy_esp32c3.build.mcu=esp32c3 +adafruit_qtpy_esp32c3.build.core=esp32 +adafruit_qtpy_esp32c3.build.variant=adafruit_qtpy_esp32c3 +adafruit_qtpy_esp32c3.build.board=ADAFRUIT_QTPY_ESP32C3 +adafruit_qtpy_esp32c3.build.bootloader_addr=0x0 + +adafruit_qtpy_esp32c3.build.cdc_on_boot=0 +adafruit_qtpy_esp32c3.build.f_cpu=160000000L +adafruit_qtpy_esp32c3.build.flash_size=4MB +adafruit_qtpy_esp32c3.build.flash_freq=80m +adafruit_qtpy_esp32c3.build.flash_mode=dout +adafruit_qtpy_esp32c3.build.boot=dout +adafruit_qtpy_esp32c3.build.partitions=default +adafruit_qtpy_esp32c3.build.defines= + +adafruit_qtpy_esp32c3.menu.CDCOnBoot.cdc=Enabled +adafruit_qtpy_esp32c3.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +adafruit_qtpy_esp32c3.menu.CDCOnBoot.default=Disabled +adafruit_qtpy_esp32c3.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +adafruit_qtpy_esp32c3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.default.build.partitions=default +adafruit_qtpy_esp32c3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +adafruit_qtpy_esp32c3.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.minimal.build.partitions=minimal +adafruit_qtpy_esp32c3.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.no_ota.build.partitions=no_ota +adafruit_qtpy_esp32c3.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +adafruit_qtpy_esp32c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.huge_app.build.partitions=huge_app +adafruit_qtpy_esp32c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 + +adafruit_qtpy_esp32c3.menu.CPUFreq.160=160MHz (WiFi) +adafruit_qtpy_esp32c3.menu.CPUFreq.160.build.f_cpu=160000000L +adafruit_qtpy_esp32c3.menu.CPUFreq.80=80MHz (WiFi) +adafruit_qtpy_esp32c3.menu.CPUFreq.80.build.f_cpu=80000000L +adafruit_qtpy_esp32c3.menu.CPUFreq.40=40MHz +adafruit_qtpy_esp32c3.menu.CPUFreq.40.build.f_cpu=40000000L +adafruit_qtpy_esp32c3.menu.CPUFreq.20=20MHz +adafruit_qtpy_esp32c3.menu.CPUFreq.20.build.f_cpu=20000000L +adafruit_qtpy_esp32c3.menu.CPUFreq.10=10MHz +adafruit_qtpy_esp32c3.menu.CPUFreq.10.build.f_cpu=10000000L + +adafruit_qtpy_esp32c3.menu.FlashMode.dout=DOUT +adafruit_qtpy_esp32c3.menu.FlashMode.dout.build.flash_mode=dout +adafruit_qtpy_esp32c3.menu.FlashMode.dout.build.boot=dout +adafruit_qtpy_esp32c3.menu.FlashMode.qio=QIO +adafruit_qtpy_esp32c3.menu.FlashMode.qio.build.flash_mode=dio +adafruit_qtpy_esp32c3.menu.FlashMode.qio.build.boot=qio +adafruit_qtpy_esp32c3.menu.FlashMode.dio=DIO +adafruit_qtpy_esp32c3.menu.FlashMode.dio.build.flash_mode=dio +adafruit_qtpy_esp32c3.menu.FlashMode.dio.build.boot=dio +adafruit_qtpy_esp32c3.menu.FlashMode.qout=QOUT +adafruit_qtpy_esp32c3.menu.FlashMode.qout.build.flash_mode=dout +adafruit_qtpy_esp32c3.menu.FlashMode.qout.build.boot=qout + +adafruit_qtpy_esp32c3.menu.FlashFreq.80=80MHz +adafruit_qtpy_esp32c3.menu.FlashFreq.80.build.flash_freq=80m +adafruit_qtpy_esp32c3.menu.FlashFreq.40=40MHz +adafruit_qtpy_esp32c3.menu.FlashFreq.40.build.flash_freq=40m + +adafruit_qtpy_esp32c3.menu.FlashSize.4M=4MB (32Mb) +adafruit_qtpy_esp32c3.menu.FlashSize.4M.build.flash_size=4MB +adafruit_qtpy_esp32c3.menu.FlashSize.2M=2MB (16Mb) +adafruit_qtpy_esp32c3.menu.FlashSize.2M.build.flash_size=2MB +adafruit_qtpy_esp32c3.menu.FlashSize.2M.build.partitions=minimal + +adafruit_qtpy_esp32c3.menu.UploadSpeed.115200=115200 +adafruit_qtpy_esp32c3.menu.UploadSpeed.115200.upload.speed=115200 +adafruit_qtpy_esp32c3.menu.UploadSpeed.921600=921600 +adafruit_qtpy_esp32c3.menu.UploadSpeed.921600.upload.speed=921600 +adafruit_qtpy_esp32c3.menu.UploadSpeed.921600=921600 +adafruit_qtpy_esp32c3.menu.UploadSpeed.921600.upload.speed=921600 +adafruit_qtpy_esp32c3.menu.UploadSpeed.115200=115200 +adafruit_qtpy_esp32c3.menu.UploadSpeed.115200.upload.speed=115200 +adafruit_qtpy_esp32c3.menu.UploadSpeed.256000.windows=256000 +adafruit_qtpy_esp32c3.menu.UploadSpeed.256000.upload.speed=256000 +adafruit_qtpy_esp32c3.menu.UploadSpeed.230400.windows.upload.speed=256000 +adafruit_qtpy_esp32c3.menu.UploadSpeed.230400=230400 +adafruit_qtpy_esp32c3.menu.UploadSpeed.230400.upload.speed=230400 +adafruit_qtpy_esp32c3.menu.UploadSpeed.460800.linux=460800 +adafruit_qtpy_esp32c3.menu.UploadSpeed.460800.macosx=460800 +adafruit_qtpy_esp32c3.menu.UploadSpeed.460800.upload.speed=460800 +adafruit_qtpy_esp32c3.menu.UploadSpeed.512000.windows=512000 +adafruit_qtpy_esp32c3.menu.UploadSpeed.512000.upload.speed=512000 + +adafruit_qtpy_esp32c3.menu.DebugLevel.none=None +adafruit_qtpy_esp32c3.menu.DebugLevel.none.build.code_debug=0 +adafruit_qtpy_esp32c3.menu.DebugLevel.error=Error +adafruit_qtpy_esp32c3.menu.DebugLevel.error.build.code_debug=1 +adafruit_qtpy_esp32c3.menu.DebugLevel.warn=Warn +adafruit_qtpy_esp32c3.menu.DebugLevel.warn.build.code_debug=2 +adafruit_qtpy_esp32c3.menu.DebugLevel.info=Info +adafruit_qtpy_esp32c3.menu.DebugLevel.info.build.code_debug=3 +adafruit_qtpy_esp32c3.menu.DebugLevel.debug=Debug +adafruit_qtpy_esp32c3.menu.DebugLevel.debug.build.code_debug=4 +adafruit_qtpy_esp32c3.menu.DebugLevel.verbose=Verbose +adafruit_qtpy_esp32c3.menu.DebugLevel.verbose.build.code_debug=5 + + +adafruit_qtpy_esp32_pico.name=Adafruit QT Py ESP32 Pico + +adafruit_qtpy_esp32_pico.upload.tool=esptool_py +adafruit_qtpy_esp32_pico.upload.maximum_size=1310720 +adafruit_qtpy_esp32_pico.upload.maximum_data_size=327680 +adafruit_qtpy_esp32_pico.upload.flags= +adafruit_qtpy_esp32_pico.upload.extra_flags= + +adafruit_qtpy_esp32_pico.serial.disableDTR=true +adafruit_qtpy_esp32_pico.serial.disableRTS=true + +adafruit_qtpy_esp32_pico.build.tarch=xtensa +adafruit_qtpy_esp32_pico.build.bootloader_addr=0x1000 +adafruit_qtpy_esp32_pico.build.target=esp32 +adafruit_qtpy_esp32_pico.build.mcu=esp32 +adafruit_qtpy_esp32_pico.build.core=esp32 +adafruit_qtpy_esp32_pico.build.variant=adafruit_qtpy_esp32 +adafruit_qtpy_esp32_pico.build.board=ADAFRUIT_QTPY_ESP32_PICO + +adafruit_qtpy_esp32_pico.build.f_cpu=240000000L +adafruit_qtpy_esp32_pico.build.flash_size=8MB +adafruit_qtpy_esp32_pico.build.flash_freq=80m +adafruit_qtpy_esp32_pico.build.flash_mode=dio +adafruit_qtpy_esp32_pico.build.boot=dio +adafruit_qtpy_esp32_pico.build.partitions=default +adafruit_qtpy_esp32_pico.build.defines= + +adafruit_qtpy_esp32_pico.menu.PartitionScheme.default=Default +adafruit_qtpy_esp32_pico.menu.PartitionScheme.default.build.partitions=default +adafruit_qtpy_esp32_pico.menu.PartitionScheme.no_ota=No OTA (Large APP) +adafruit_qtpy_esp32_pico.menu.PartitionScheme.no_ota.build.partitions=no_ota +adafruit_qtpy_esp32_pico.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +adafruit_qtpy_esp32_pico.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (Large APPS with OTA) +adafruit_qtpy_esp32_pico.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +adafruit_qtpy_esp32_pico.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 + +adafruit_qtpy_esp32_pico.menu.UploadSpeed.921600=921600 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.921600.upload.speed=921600 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.115200=115200 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.115200.upload.speed=115200 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.256000.windows=256000 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.256000.upload.speed=256000 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.230400.windows.upload.speed=256000 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.230400=230400 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.230400.upload.speed=230400 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.460800.linux=460800 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.460800.macosx=460800 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.460800.upload.speed=460800 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.512000.windows=512000 +adafruit_qtpy_esp32_pico.menu.UploadSpeed.512000.upload.speed=512000 + +adafruit_qtpy_esp32_pico.menu.PSRAM.enabled=Enabled +adafruit_qtpy_esp32_pico.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw +adafruit_qtpy_esp32_pico.menu.PSRAM.enabled.build.extra_libs= +adafruit_qtpy_esp32_pico.menu.PSRAM.disabled=Disabled +adafruit_qtpy_esp32_pico.menu.PSRAM.disabled.build.defines= +adafruit_qtpy_esp32_pico.menu.PSRAM.disabled.build.extra_libs= + +adafruit_qtpy_esp32_pico.menu.DebugLevel.none=None +adafruit_qtpy_esp32_pico.menu.DebugLevel.none.build.code_debug=0 +adafruit_qtpy_esp32_pico.menu.DebugLevel.error=Error +adafruit_qtpy_esp32_pico.menu.DebugLevel.error.build.code_debug=1 +adafruit_qtpy_esp32_pico.menu.DebugLevel.warn=Warn +adafruit_qtpy_esp32_pico.menu.DebugLevel.warn.build.code_debug=2 +adafruit_qtpy_esp32_pico.menu.DebugLevel.info=Info +adafruit_qtpy_esp32_pico.menu.DebugLevel.info.build.code_debug=3 +adafruit_qtpy_esp32_pico.menu.DebugLevel.debug=Debug +adafruit_qtpy_esp32_pico.menu.DebugLevel.debug.build.code_debug=4 +adafruit_qtpy_esp32_pico.menu.DebugLevel.verbose=Verbose +adafruit_qtpy_esp32_pico.menu.DebugLevel.verbose.build.code_debug=5 + + + +adafruit_feather_esp32_v2.name=Adafruit Feather ESP32 V2 + +adafruit_feather_esp32_v2.upload.tool=esptool_py +adafruit_feather_esp32_v2.upload.maximum_size=1310720 +adafruit_feather_esp32_v2.upload.maximum_data_size=327680 +adafruit_feather_esp32_v2.upload.flags= +adafruit_feather_esp32_v2.upload.extra_flags= + +adafruit_feather_esp32_v2.serial.disableDTR=true +adafruit_feather_esp32_v2.serial.disableRTS=true + +adafruit_feather_esp32_v2.build.tarch=xtensa +adafruit_feather_esp32_v2.build.bootloader_addr=0x1000 +adafruit_feather_esp32_v2.build.target=esp32 +adafruit_feather_esp32_v2.build.mcu=esp32 +adafruit_feather_esp32_v2.build.core=esp32 +adafruit_feather_esp32_v2.build.variant=adafruit_feather_esp32_v2 +adafruit_feather_esp32_v2.build.board=ADAFRUIT_FEATHER_ESP32_V2 + +adafruit_feather_esp32_v2.build.f_cpu=240000000L +adafruit_feather_esp32_v2.build.flash_size=8MB +adafruit_feather_esp32_v2.build.flash_freq=80m +adafruit_feather_esp32_v2.build.flash_mode=dio +adafruit_feather_esp32_v2.build.boot=dio +adafruit_feather_esp32_v2.build.partitions=default +adafruit_feather_esp32_v2.build.defines= + +adafruit_feather_esp32_v2.menu.PartitionScheme.default=Default +adafruit_feather_esp32_v2.menu.PartitionScheme.default.build.partitions=default +adafruit_feather_esp32_v2.menu.PartitionScheme.no_ota=No OTA (Large APP) +adafruit_feather_esp32_v2.menu.PartitionScheme.no_ota.build.partitions=no_ota +adafruit_feather_esp32_v2.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +adafruit_feather_esp32_v2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (Large APPS with OTA) +adafruit_feather_esp32_v2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +adafruit_feather_esp32_v2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 + +adafruit_feather_esp32_v2.menu.UploadSpeed.921600=921600 +adafruit_feather_esp32_v2.menu.UploadSpeed.921600.upload.speed=921600 +adafruit_feather_esp32_v2.menu.UploadSpeed.115200=115200 +adafruit_feather_esp32_v2.menu.UploadSpeed.115200.upload.speed=115200 +adafruit_feather_esp32_v2.menu.UploadSpeed.256000.windows=256000 +adafruit_feather_esp32_v2.menu.UploadSpeed.256000.upload.speed=256000 +adafruit_feather_esp32_v2.menu.UploadSpeed.230400.windows.upload.speed=256000 +adafruit_feather_esp32_v2.menu.UploadSpeed.230400=230400 +adafruit_feather_esp32_v2.menu.UploadSpeed.230400.upload.speed=230400 +adafruit_feather_esp32_v2.menu.UploadSpeed.460800.linux=460800 +adafruit_feather_esp32_v2.menu.UploadSpeed.460800.macosx=460800 +adafruit_feather_esp32_v2.menu.UploadSpeed.460800.upload.speed=460800 +adafruit_feather_esp32_v2.menu.UploadSpeed.512000.windows=512000 +adafruit_feather_esp32_v2.menu.UploadSpeed.512000.upload.speed=512000 + +adafruit_feather_esp32_v2.menu.PSRAM.enabled=Enabled +adafruit_feather_esp32_v2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw +adafruit_feather_esp32_v2.menu.PSRAM.enabled.build.extra_libs= +adafruit_feather_esp32_v2.menu.PSRAM.disabled=Disabled +adafruit_feather_esp32_v2.menu.PSRAM.disabled.build.defines= +adafruit_feather_esp32_v2.menu.PSRAM.disabled.build.extra_libs= + +adafruit_feather_esp32_v2.menu.DebugLevel.none=None +adafruit_feather_esp32_v2.menu.DebugLevel.none.build.code_debug=0 +adafruit_feather_esp32_v2.menu.DebugLevel.error=Error +adafruit_feather_esp32_v2.menu.DebugLevel.error.build.code_debug=1 +adafruit_feather_esp32_v2.menu.DebugLevel.warn=Warn +adafruit_feather_esp32_v2.menu.DebugLevel.warn.build.code_debug=2 +adafruit_feather_esp32_v2.menu.DebugLevel.info=Info +adafruit_feather_esp32_v2.menu.DebugLevel.info.build.code_debug=3 +adafruit_feather_esp32_v2.menu.DebugLevel.debug=Debug +adafruit_feather_esp32_v2.menu.DebugLevel.debug.build.code_debug=4 +adafruit_feather_esp32_v2.menu.DebugLevel.verbose=Verbose +adafruit_feather_esp32_v2.menu.DebugLevel.verbose.build.code_debug=5 + + + ############################################################## nodemcu-32s.name=NodeMCU-32S diff --git a/variants/adafruit_feather_esp32_v2/pins_arduino.h b/variants/adafruit_feather_esp32_v2/pins_arduino.h new file mode 100644 index 00000000000..2bfaf63b7aa --- /dev/null +++ b/variants/adafruit_feather_esp32_v2/pins_arduino.h @@ -0,0 +1,73 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define EXTERNAL_NUM_INTERRUPTS 16 +#define NUM_DIGITAL_PINS 40 +#define NUM_ANALOG_INPUTS 16 + +#define analogInputToDigitalPin(p) (((p)<20)?(esp32_adc2gpio[(p)]):-1) +#define digitalPinToInterrupt(p) (((p)<40)?(p):-1) +#define digitalPinHasPWM(p) (p < 34) + +static const uint8_t LED_BUILTIN = 13; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN + +static const uint8_t TX = 7; +static const uint8_t RX = 8; +static const uint8_t TX1 = 7; +static const uint8_t RX1 = 8; + +static const uint8_t SDA = 22; +static const uint8_t SCL = 20; + +static const uint8_t SS = 33; +static const uint8_t MOSI = 19; +static const uint8_t MISO = 21; +static const uint8_t SCK = 5; + +// mapping to match other feathers and also in order +static const uint8_t A0 = 26; +static const uint8_t A1 = 25; +static const uint8_t A2 = 34; +static const uint8_t A3 = 39; +static const uint8_t A4 = 36; +static const uint8_t A5 = 4; +static const uint8_t A6 = 14; +static const uint8_t A7 = 32; +static const uint8_t A8 = 15; +static const uint8_t A9 = 33; +static const uint8_t A10 = 27; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; + +// vbat measure +static const uint8_t BATT_MONITOR = 35; +static const uint8_t A13 = 35; + +// internal switch +static const uint8_t BUTTON = 38; + +// Neopixel +static const uint8_t NEOPIXEL_PIN = 0; + +// Neopixel & I2C power +static const uint8_t NEOPIXEL_I2C_POWER = 2; + +static const uint8_t T0 = 4; +static const uint8_t T1 = 0; +static const uint8_t T2 = 2; +static const uint8_t T3 = 15; +static const uint8_t T4 = 13; +static const uint8_t T5 = 12; +static const uint8_t T6 = 14; +static const uint8_t T7 = 27; +static const uint8_t T8 = 33; +static const uint8_t T9 = 32; + +static const uint8_t DAC1 = 25; +static const uint8_t DAC2 = 26; + +#endif /* Pins_Arduino_h */ diff --git a/variants/adafruit_feather_esp32_v2/variant.cpp b/variants/adafruit_feather_esp32_v2/variant.cpp new file mode 100644 index 00000000000..9345f6058a6 --- /dev/null +++ b/variants/adafruit_feather_esp32_v2/variant.cpp @@ -0,0 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Ha Thach (tinyusb.org) for Adafruit Industries + * + * 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. + */ + + +#include "esp32-hal-gpio.h" +#include "pins_arduino.h" + +extern "C" { + +// Initialize variant/board, called before setup() +void initVariant(void) +{ + // This board has a power control pin, and we must set it to output and high + // in order to enable the NeoPixels & I2C + pinMode(NEOPIXEL_I2C_POWER, OUTPUT); + digitalWrite(NEOPIXEL_I2C_POWER, HIGH); +} + +} diff --git a/variants/adafruit_feather_esp32s2/pins_arduino.h b/variants/adafruit_feather_esp32s2/pins_arduino.h index 4e5e13de6e2..bde8d973d8f 100644 --- a/variants/adafruit_feather_esp32s2/pins_arduino.h +++ b/variants/adafruit_feather_esp32s2/pins_arduino.h @@ -26,6 +26,7 @@ #define NEOPIXEL_POWER 21 // power pin #define NEOPIXEL_POWER_ON HIGH // power pin state when on #define I2C_POWER 7 // I2C power pin +#define PIN_I2C_POWER 7 // I2C power pin static const uint8_t SDA = 3; static const uint8_t SCL = 4; diff --git a/variants/adafruit_qtpy_esp32/pins_arduino.h b/variants/adafruit_qtpy_esp32/pins_arduino.h index 72a5606c5d6..091554a08dd 100644 --- a/variants/adafruit_qtpy_esp32/pins_arduino.h +++ b/variants/adafruit_qtpy_esp32/pins_arduino.h @@ -11,14 +11,14 @@ #define digitalPinToInterrupt(p) (((p)<40)?(p):-1) #define digitalPinHasPWM(p) (p < 34) -static const uint8_t NEOPIXEL = 5; +static const uint8_t PIN_NEOPIXEL = 5; static const uint8_t NEOPIXEL_POWER = 8; static const uint8_t TX = 32; static const uint8_t RX = 7; -static const uint8_t TX1 = 32; -static const uint8_t RX1 = 7; +#define TX1 32 +#define RX1 7 static const uint8_t SDA = 25; static const uint8_t SCL = 33; @@ -43,7 +43,7 @@ static const uint8_t A8 = 14; static const uint8_t A9 = 12; static const uint8_t A10 = 13; -static const uint8_t SWITCH = 0; +static const uint8_t BUTTON = 0; static const uint8_t T0 = 4; static const uint8_t T3 = 15; diff --git a/variants/adafruit_qtpy_esp32/variant.cpp b/variants/adafruit_qtpy_esp32/variant.cpp new file mode 100644 index 00000000000..726ec8fa483 --- /dev/null +++ b/variants/adafruit_qtpy_esp32/variant.cpp @@ -0,0 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Ha Thach (tinyusb.org) for Adafruit Industries + * + * 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. + */ + + +#include "esp32-hal-gpio.h" +#include "pins_arduino.h" + +extern "C" { + +// Initialize variant/board, called before setup() +void initVariant(void) +{ + // This board has a power control pin, and we must set it to output and high + // in order to enable the NeoPixels. + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, HIGH); +} + +} diff --git a/variants/adafruit_qtpy_esp32c3/pins_arduino.h b/variants/adafruit_qtpy_esp32c3/pins_arduino.h new file mode 100644 index 00000000000..61d1f49492b --- /dev/null +++ b/variants/adafruit_qtpy_esp32c3/pins_arduino.h @@ -0,0 +1,34 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define EXTERNAL_NUM_INTERRUPTS 22 +#define NUM_DIGITAL_PINS 22 +#define NUM_ANALOG_INPUTS 6 + +#define analogInputToDigitalPin(p) (((p) Date: Thu, 3 Feb 2022 18:56:25 +0000 Subject: [PATCH 94/94] Added dual antenna for WiFi (based on the ESP32-WROOM-DA module) (#6226) * Added dual antenna for WiFi (based on the ESP32-WROOM-DA module) * Fixed build error * Fixed indentation and renamed function to setDualAntennaConfig * Added the RX and TX selection modes as configuration * Mode code optimization --- libraries/WiFi/src/WiFiGeneric.cpp | 88 ++++++++++++++++++++++++++++++ libraries/WiFi/src/WiFiGeneric.h | 14 +++++ 2 files changed, 102 insertions(+) diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp index 3bcaececc7a..5dfdacdc05d 100644 --- a/libraries/WiFi/src/WiFiGeneric.cpp +++ b/libraries/WiFi/src/WiFiGeneric.cpp @@ -1192,6 +1192,94 @@ bool WiFiGenericClass::initiateFTM(uint8_t frm_count, uint16_t burst_period, uin return true; } +/** + * Configure Dual antenna. + * @param gpio_ant1 Configure the GPIO number for the antenna 1 connected to the RF switch (default GPIO2 on ESP32-WROOM-DA) + * @param gpio_ant2 Configure the GPIO number for the antenna 2 connected to the RF switch (default GPIO25 on ESP32-WROOM-DA) + * @param rx_mode Set the RX antenna mode. See wifi_rx_ant_t for the options. + * @param tx_mode Set the TX antenna mode. See wifi_tx_ant_t for the options. + * @return true on success + */ +bool WiFiGenericClass::setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2, wifi_rx_ant_t rx_mode, wifi_tx_ant_t tx_mode) { + + wifi_ant_gpio_config_t wifi_ant_io; + + if (ESP_OK != esp_wifi_get_ant_gpio(&wifi_ant_io)) { + log_e("Failed to get antenna configuration"); + return false; + } + + wifi_ant_io.gpio_cfg[0].gpio_num = gpio_ant1; + wifi_ant_io.gpio_cfg[0].gpio_select = 1; + wifi_ant_io.gpio_cfg[1].gpio_num = gpio_ant2; + wifi_ant_io.gpio_cfg[1].gpio_select = 1; + + if (ESP_OK != esp_wifi_set_ant_gpio(&wifi_ant_io)) { + log_e("Failed to set antenna GPIO configuration"); + return false; + } + + // Set antenna default configuration + wifi_ant_config_t ant_config = { + .rx_ant_mode = WIFI_ANT_MODE_AUTO, + .tx_ant_mode = WIFI_ANT_MODE_AUTO, + .enabled_ant0 = 0, + .enabled_ant1 = 1, + }; + + switch (rx_mode) + { + case WIFI_RX_ANT0: + ant_config.rx_ant_mode = WIFI_ANT_MODE_ANT0; + break; + case WIFI_RX_ANT1: + ant_config.rx_ant_mode = WIFI_ANT_MODE_ANT1; + break; + case WIFI_RX_ANT_AUTO: + log_i("TX Antenna will be automatically selected"); + ant_config.rx_ant_default = WIFI_ANT_ANT0; + ant_config.rx_ant_mode = WIFI_ANT_MODE_AUTO; + // Force TX for AUTO if RX is AUTO + ant_config.tx_ant_mode = WIFI_ANT_MODE_AUTO; + goto set_ant; + break; + default: + log_e("Invalid default antenna! Falling back to AUTO"); + ant_config.rx_ant_mode = WIFI_ANT_MODE_AUTO; + break; + } + + switch (tx_mode) + { + case WIFI_TX_ANT0: + ant_config.tx_ant_mode = WIFI_ANT_MODE_ANT0; + break; + case WIFI_TX_ANT1: + ant_config.tx_ant_mode = WIFI_ANT_MODE_ANT1; + break; + case WIFI_TX_ANT_AUTO: + log_i("RX Antenna will be automatically selected"); + ant_config.rx_ant_default = WIFI_ANT_ANT0; + ant_config.tx_ant_mode = WIFI_ANT_MODE_AUTO; + // Force RX for AUTO if RX is AUTO + ant_config.rx_ant_mode = WIFI_ANT_MODE_AUTO; + break; + default: + log_e("Invalid default antenna! Falling back to AUTO"); + ant_config.rx_ant_default = WIFI_ANT_ANT0; + ant_config.tx_ant_mode = WIFI_ANT_MODE_AUTO; + break; + } + +set_ant: + if (ESP_OK != esp_wifi_set_ant(&ant_config)) { + log_e("Failed to set antenna configuration"); + return false; + } + + return true; +} + // ----------------------------------------------------------------------------------------------------------------------- // ------------------------------------------------ Generic Network function --------------------------------------------- // ----------------------------------------------------------------------------------------------------------------------- diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h index 7079c032993..f8a1bb77d37 100644 --- a/libraries/WiFi/src/WiFiGeneric.h +++ b/libraries/WiFi/src/WiFiGeneric.h @@ -139,6 +139,18 @@ static const int WIFI_SCAN_DONE_BIT= BIT12; static const int WIFI_DNS_IDLE_BIT = BIT13; static const int WIFI_DNS_DONE_BIT = BIT14; +typedef enum { + WIFI_RX_ANT0 = 0, + WIFI_RX_ANT1, + WIFI_RX_ANT_AUTO +} wifi_rx_ant_t; + +typedef enum { + WIFI_TX_ANT0 = 0, + WIFI_TX_ANT1, + WIFI_TX_ANT_AUTO +} wifi_tx_ant_t; + class WiFiGenericClass { public: @@ -174,6 +186,8 @@ class WiFiGenericClass bool initiateFTM(uint8_t frm_count=16, uint16_t burst_period=2, uint8_t channel=1, const uint8_t * mac=NULL); + bool setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2, wifi_rx_ant_t rx_mode, wifi_tx_ant_t tx_mode); + static const char * getHostname(); static bool setHostname(const char * hostname); static bool hostname(const String& aHostname) { return setHostname(aHostname.c_str()); }