Files
arduino-esp32/libraries/I2S/src/I2S.cpp
Me No Dev 8ee5f0a11e Esp32 s3 support (#6341)
Co-authored-by: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Co-authored-by: Unexpected Maker <seon@unexpectedmaker.com>
Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Co-authored-by: Tomáš Pilný <34927466+PilnyTomas@users.noreply.github.com>
Co-authored-by: Pedro Minatel <pedro.minatel@espressif.com>
Co-authored-by: Ivan Grokhotkov <ivan@espressif.com>
Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>
Co-authored-by: Limor "Ladyada" Fried <limor@ladyada.net>
2022-03-28 12:09:41 +03:00

1210 lines
38 KiB
C++

/*
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 <Arduino.h>
#include <wiring_private.h>
#include "I2S.h"
#include "freertos/semphr.h"
#define _I2S_EVENT_QUEUE_LENGTH 16
#define _I2S_DMA_BUFFER_COUNT 2 // BUFFER COUNT must be between 2 and 128
#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
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(PIN_I2S_SD_IN), // input data pin
_outSdPin(PIN_I2S_SD), // output data pin
_sckPin(sckPin), // clock pin
_fsPin(fsPin), // frame (word) select pin
_state(I2S_STATE_IDLE),
_bitsPerSample(0),
_sampleRate(0),
_mode(I2S_PHILIPS_MODE),
_buffer_byte_size(0),
_driverInstalled(false),
_initialized(false),
_callbackTaskHandle(NULL),
_i2sEventQueue(NULL),
_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; Must be between 8 and 1024
_driveClock(true),
_peek_buff(0),
_peek_buff_valid(false),
_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 = 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
}
return 1; // OK
}
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){
i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_MASTER);
}else{
i2s_mode = (esp_i2s::i2s_mode_t)(i2s_mode | esp_i2s::I2S_MODE_SLAVE);
}
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 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);
#else
log_e("This chip does not support ADC / DAC mode");
return 0; // ERR
#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 Philips 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
}
if(_bitsPerSample == 24){
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)
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),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
.dma_buf_count = _I2S_DMA_BUFFER_COUNT,
.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.\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.\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");
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.
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 == 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)){
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");
return 0; // ERR
}
if(ESP_OK != esp_i2s::i2s_set_pin((esp_i2s::i2s_port_t) _deviceIndex, NULL)){
log_e("i2s_set_pin failed");
return 0; // ERR
}
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);
_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
_driverInstalled = true; // IDF I2S driver must be installed before calling _applyPinSetting
if(!_applyPinSetting()){
log_e("could not apply pin setting during driver install");
_uninstallDriver();
return 0; // ERR
}
} // if I2S _mode
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)
int ret = begin(mode, sampleRate, bitsPerSample, true);
_give_if_top_call();
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, 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){
log_e("ERROR: Object already initialized! Call I2S.end() to disable");
_give_if_top_call();
return 0; // ERR
}
_driveClock = driveClock;
_mode = mode;
_sampleRate = (uint32_t)sampleRate;
_bitsPerSample = bitsPerSample;
// There is work in progress on this library.
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.\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.\nAt 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();
return 0; // ERR
}
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
case PDM_STEREO_MODE:
case PDM_MONO_MODE:
break;
default: // invalid mode
log_e("ERROR: unknown mode");
_give_if_top_call();
return 0; // ERR
}
if(!_installDriver()){
log_e("ERROR: failed to install driver");
end();
_give_if_top_call();
return 0; // ERR
}
_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){
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
}
_initialized = true;
_give_if_top_call();
return 1; // OK
}
int I2SClass::_applyPinSetting(){
if(_driverInstalled){
esp_i2s::i2s_pin_config_t pin_config = {
.bck_io_num = _sckPin,
.ws_io_num = _fsPin,
.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 = _sdPin;
}else if(_state == I2S_STATE_TRANSMITTER){
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;
pin_config.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; 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
}
} // if(_driverInstalled)
return 1; // OK
}
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){
_take_if_not_holding();
_setSckPin(sckPin);
int ret = _applyPinSetting();
_applyPinSetting();
_give_if_top_call();
return ret;
}
void I2SClass::_setFsPin(int fsPin){
if(fsPin >= 0){
_fsPin = fsPin;
}else{
_fsPin = PIN_I2S_FS;
}
}
int I2SClass::setFsPin(int fsPin){
_take_if_not_holding();
_setFsPin(fsPin);
int ret = _applyPinSetting();
_give_if_top_call();
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_IN;
}
}
int I2SClass::setDataInPin(int inSdPin){
_take_if_not_holding();
_setDataInPin(inSdPin);
int ret = _applyPinSetting();
_give_if_top_call();
return ret;
}
void I2SClass::_setDataOutPin(int outSdPin){
if(outSdPin >= 0){
_outSdPin = outSdPin;
}else{
_outSdPin = PIN_I2S_SD;
}
}
int I2SClass::setDataOutPin(int outSdPin){
_take_if_not_holding();
_setDataOutPin(outSdPin);
int ret = _applyPinSetting();
_give_if_top_call();
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, PIN_I2S_SD_IN);
_give_if_top_call();
return ret;
}
int I2SClass::setAllPins(int sckPin, int fsPin, int sdPin, int outSdPin, int inSdPin){
_take_if_not_holding();
_setSckPin(sckPin);
_setFsPin(fsPin);
_setDataPin(sdPin);
_setDataOutPin(outSdPin);
_setDataInPin(inSdPin);
int ret = _applyPinSetting();
_give_if_top_call();
return ret;
}
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(){
_take_if_not_holding();
int ret = (int)(_state == I2S_STATE_DUPLEX);
_give_if_top_call();
return ret;
}
int I2SClass::getSckPin(){
_take_if_not_holding();
int ret = _sckPin;
_give_if_top_call();
return ret;
}
int I2SClass::getFsPin(){
_take_if_not_holding();
int ret = _fsPin;
_give_if_top_call();
return ret;
}
int I2SClass::getDataPin(){
_take_if_not_holding();
int ret = _sdPin;
_give_if_top_call();
return ret;
}
int I2SClass::getDataInPin(){
_take_if_not_holding();
int ret = _inSdPin;
_give_if_top_call();
return ret;
}
int I2SClass::getDataOutPin(){
_take_if_not_holding();
int ret = _outSdPin;
_give_if_top_call();
return ret;
}
void I2SClass::_uninstallDriver(){
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;
}
_driverInstalled = false;
} // if(_driverInstalled)
}
void I2SClass::end(){
_take_if_not_holding();
if(xTaskGetCurrentTaskHandle() != _callbackTaskHandle){
if(_callbackTaskHandle){
vTaskDelete(_callbackTaskHandle);
_callbackTaskHandle = NULL; // prevent secondary termination to non-existing task
}
_uninstallDriver();
_onTransmit = NULL;
_onReceive = NULL;
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;
}
_initialized = false;
}else{
log_w("WARNING: ending I2SClass from callback task not permitted, but attempted!");
}
_give_if_top_call();
}
// Bytes available to read
int I2SClass::available(){
_take_if_not_holding();
int ret = 0;
if(_input_ring_buffer != NULL){
ret = _buffer_byte_size - (int)xRingbufferGetCurFreeSize(_input_ring_buffer);
}
_give_if_top_call();
return ret;
}
union i2s_sample_t {
uint8_t b8;
int16_t b16;
int32_t b32;
};
int I2SClass::read(){
_take_if_not_holding();
i2s_sample_t sample;
sample.b32 = 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; // sample value
}
} // if(_initialized)
_give_if_top_call();
return 0; // sample value
}
int I2SClass::read(void* buffer, size_t size){
_take_if_not_holding();
size_t requested_size = size;
if(_initialized){
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;
if(_input_ring_buffer != NULL){
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)
if(_mode == ADC_DAC_MODE){
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{
log_w("input buffer is empty - timed out");
_give_if_top_call();
return 0; // 0 Bytes read / ERR
} // tmp buffer not NULL ?
} // ring buffer not NULL ?
} // if(_initialized)
_give_if_top_call();
return 0; // 0 Bytes read / ERR
}
size_t I2SClass::write(uint8_t data){
_take_if_not_holding();
size_t ret = 0;
if(_initialized){
ret = write_blocking((int32_t*)&data, 1);
}
_give_if_top_call();
return ret;
}
size_t I2SClass::write(int32_t sample){
_take_if_not_holding();
size_t ret = 0;
if(_initialized){
ret = write_blocking(&sample, _bitsPerSample/8);
}
_give_if_top_call();
return ret;
}
size_t I2SClass::write(const uint8_t *buffer, size_t size){
_take_if_not_holding();
size_t ret = 0;
if(_initialized){
ret = write((const void*)buffer, size);
}
_give_if_top_call();
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
// 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){
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);
if(pdTRUE == ret){
_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 ?
} // ring buffer not NULL ?
} // if(_initialized)
return 0;
log_w("I2S not initialized");
_give_if_top_call();
return 0;
}
// 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){
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(_output_ring_buffer != NULL){
if(pdTRUE == xRingbufferSend(_output_ring_buffer, buffer, size, 0)){
_give_if_top_call();
return size;
}else{
log_w("I2S could not write all data into ring buffer!");
_give_if_top_call();
return 0;
}
}
} // if(_initialized)
return 0;
_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 && _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;
}
void I2SClass::flush(){
_take_if_not_holding();
if(_initialized){
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){
item = xRingbufferReceiveUpTo(_output_ring_buffer, &item_size, 0, single_dma_buf);
if (item != NULL){
_fix_and_write(item, item_size);
vRingbufferReturnItem(_output_ring_buffer, item);
}
}
} // if(_initialized)
_give_if_top_call();
}
// Bytes available to write
int I2SClass::availableForWrite(){
_take_if_not_holding();
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)){
_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();
int ret = 0;
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", bufferSize);
_give_if_top_call();
return 0; // ERR
} // check requested buffer size
if(_initialized){
_uninstallDriver();
ret = _installDriver();
_give_if_top_call();
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
}
int I2SClass::getBufferSize(){
_take_if_not_holding();
int ret = _i2s_dma_buffer_size;
_give_if_top_call();
return ret;
}
int I2SClass::_enableTransmitter(){
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_DUPLEX && _state != I2S_STATE_RECEIVER){
_state = I2S_STATE_RECEIVER;
return _applyPinSetting();
}
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)*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;
static int prev_item_offset = 0;
static size_t bytes_written = 0;
if(prev_item_valid){ // use item from previous round
_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(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
} // fill up the I2S DMA buffer
if(_onTransmit){
_onTransmit();
} // user callback
}
void I2SClass::_rx_done_routine(){
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);
if(avail > 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
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
}
}
void I2SClass::_onTransferComplete(){
uint8_t prev_item[_i2s_dma_buffer_size*4];
esp_i2s::i2s_event_t i2s_event;
while(true){
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
}
void I2SClass::onDmaTransferComplete(void*){
I2S._onTransferComplete();
}
void I2SClass::_take_if_not_holding(){
TaskHandle_t mutex_holder = xSemaphoreGetMutexHolder(_i2s_general_mutex);
if(mutex_holder != NULL && mutex_holder == xTaskGetCurrentTaskHandle()){
++_nesting_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 I2SClass::_give_if_top_call(){
if(_nesting_counter){
--_nesting_counter;
}else{
if(xSemaphoreGive(_i2s_general_mutex) != pdTRUE){
log_e("I2S internal mutex give error");
}
}
}
// 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
// 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){
long start = millis();
ulong src_ptr = 0;
uint8_t* buff = NULL;
size_t buff_size = size;
switch(_bitsPerSample){
case 8:
buff_size = size *2;
buff = (uint8_t*)calloc(buff_size, sizeof(uint8_t));
if(buff == NULL){
log_e("callock error");
if(bytes_written != NULL){ *bytes_written = 0; }
return;
}
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(buff_size);
if(buff == NULL){
log_e("malloc error");
if(bytes_written != NULL){ *bytes_written = 0; }
return;
}
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 24:
buff = (uint8_t*)output;
break;
case 32:
buff = (uint8_t*)output;
break;
default: ; // Do nothing
} // switch
size_t _bytes_written;
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_e("Error: writing data to i2s - function returned with err code %d", ret);
}
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 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;
}
if(actual_bytes_written != NULL){
*actual_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){
#if 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
#if (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
#if 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
#if (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){
#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
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
#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
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
#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
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
#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
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
}
}
#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
#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