kopia lustrzana https://github.com/Aircoookie/WLED
Adding analog mic support to AR for C3, S2, S3
still needs some cleanup and testing but basically works.pull/4761/head
rodzic
e6716fe834
commit
220f2565e2
|
@ -267,7 +267,6 @@ void FFTcode(void * parameter)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
xLastWakeTime = xTaskGetTickCount(); // update "last unblocked time" for vTaskDelay
|
xLastWakeTime = xTaskGetTickCount(); // update "last unblocked time" for vTaskDelay
|
||||||
|
|
||||||
// band pass filter - can reduce noise floor by a factor of 50
|
// band pass filter - can reduce noise floor by a factor of 50
|
||||||
// downside: frequencies below 100Hz will be ignored
|
// downside: frequencies below 100Hz will be ignored
|
||||||
if (useBandPassFilter) runMicFilter(samplesFFT, vReal);
|
if (useBandPassFilter) runMicFilter(samplesFFT, vReal);
|
||||||
|
@ -393,9 +392,8 @@ void FFTcode(void * parameter)
|
||||||
// run peak detection
|
// run peak detection
|
||||||
autoResetPeak();
|
autoResetPeak();
|
||||||
detectSamplePeak();
|
detectSamplePeak();
|
||||||
|
#if !defined(I2S_GRAB_ADC1_COMPLETELY) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
#if !defined(I2S_GRAB_ADC1_COMPLETELY)
|
if ((audioSource == nullptr) || (audioSource->getType() != AudioSource::Type_Adc)) // the "delay trick" does not help for analog ADC
|
||||||
if ((audioSource == nullptr) || (audioSource->getType() != AudioSource::Type_I2SAdc)) // the "delay trick" does not help for analog ADC
|
|
||||||
#endif
|
#endif
|
||||||
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers
|
||||||
|
|
||||||
|
@ -672,9 +670,7 @@ class AudioReactive : public Usermod {
|
||||||
static const char _dynamics[];
|
static const char _dynamics[];
|
||||||
static const char _frequency[];
|
static const char _frequency[];
|
||||||
static const char _inputLvl[];
|
static const char _inputLvl[];
|
||||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
static const char _analogmic[];
|
static const char _analogmic[];
|
||||||
#endif
|
|
||||||
static const char _digitalmic[];
|
static const char _digitalmic[];
|
||||||
static const char _addPalettes[];
|
static const char _addPalettes[];
|
||||||
static const char UDP_SYNC_HEADER[];
|
static const char UDP_SYNC_HEADER[];
|
||||||
|
@ -1177,13 +1173,10 @@ class AudioReactive : public Usermod {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (dmType) {
|
switch (dmType) {
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
// stub cases for not-yet-supported I2S modes on other ESP32 chips
|
|
||||||
case 0: //ADC analog
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
|
// stub case for not-yet-supported I2S mode
|
||||||
case 5: //PDM Microphone
|
case 5: //PDM Microphone
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
case 1:
|
case 1:
|
||||||
DEBUGSR_PRINT(F("AR: Generic I2S Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
DEBUGSR_PRINT(F("AR: Generic I2S Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT));
|
||||||
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
|
@ -1223,17 +1216,24 @@ class AudioReactive : public Usermod {
|
||||||
delay(100);
|
delay(100);
|
||||||
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
// ADC over I2S is only possible on "classic" ESP32
|
|
||||||
case 0:
|
case 0:
|
||||||
default:
|
default:
|
||||||
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
// ADC over I2S is only possible on "classic" ESP32
|
||||||
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
|
useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
|
||||||
if (audioSource) audioSource->initialize(audioPin);
|
if (audioSource) audioSource->initialize(audioPin);
|
||||||
break;
|
break;
|
||||||
|
#else
|
||||||
|
// use ADC DMA on ESP32S2, ESP32C3, ESP32S3
|
||||||
|
DEBUGSR_PRINTLN(F("AR: Analog Microphone"));
|
||||||
|
audioSource = new DMAadcSource(SAMPLE_RATE, samplesFFT);
|
||||||
|
delay(10); // might help with proper initialization
|
||||||
|
useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
|
||||||
|
if (audioSource) audioSource->initialize(audioPin);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
delay(250); // give microphone enough time to initialise
|
delay(250); // give microphone enough time to initialise
|
||||||
|
@ -1611,7 +1611,7 @@ class AudioReactive : public Usermod {
|
||||||
// Analog or I2S digital input
|
// Analog or I2S digital input
|
||||||
if (audioSource && (audioSource->isInitialized())) {
|
if (audioSource && (audioSource->isInitialized())) {
|
||||||
// audio source successfully configured
|
// audio source successfully configured
|
||||||
if (audioSource->getType() == AudioSource::Type_I2SAdc) {
|
if (audioSource->getType() == AudioSource::Type_Adc) {
|
||||||
infoArr.add(F("ADC analog"));
|
infoArr.add(F("ADC analog"));
|
||||||
} else {
|
} else {
|
||||||
infoArr.add(F("I2S digital"));
|
infoArr.add(F("I2S digital"));
|
||||||
|
@ -1787,10 +1787,8 @@ class AudioReactive : public Usermod {
|
||||||
top[FPSTR(_addPalettes)] = addPalettes;
|
top[FPSTR(_addPalettes)] = addPalettes;
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
||||||
amic["pin"] = audioPin;
|
amic["pin"] = audioPin;
|
||||||
#endif
|
|
||||||
|
|
||||||
JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic));
|
JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic));
|
||||||
dmic["type"] = dmType;
|
dmic["type"] = dmType;
|
||||||
|
@ -1846,19 +1844,11 @@ class AudioReactive : public Usermod {
|
||||||
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
|
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
|
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
|
||||||
#else
|
|
||||||
audioPin = -1; // MCU does not support analog mic
|
|
||||||
#endif
|
|
||||||
|
|
||||||
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["type"], dmType);
|
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["type"], dmType);
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
if (dmType == 0) dmType = SR_DMTYPE; // MCU does not support analog
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||||
if (dmType == 5) dmType = SR_DMTYPE; // MCU does not support PDM
|
if (dmType == 5) dmType = SR_DMTYPE; // MCU does not support PDM
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][0], i2ssdPin);
|
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][0], i2ssdPin);
|
||||||
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][1], i2swsPin);
|
configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][1], i2swsPin);
|
||||||
|
@ -1893,9 +1883,7 @@ class AudioReactive : public Usermod {
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
uiScript.print(F("uxp=ux+':digitalmic:pin[]';")); // uxp = shortcut for AudioReactive:digitalmic:pin[]
|
uiScript.print(F("uxp=ux+':digitalmic:pin[]';")); // uxp = shortcut for AudioReactive:digitalmic:pin[]
|
||||||
uiScript.print(F("dd=addDropdown(ux,'digitalmic:type');"));
|
uiScript.print(F("dd=addDropdown(ux,'digitalmic:type');"));
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
uiScript.print(F("addOption(dd,'Generic Analog',0);"));
|
uiScript.print(F("addOption(dd,'Generic Analog',0);"));
|
||||||
#endif
|
|
||||||
uiScript.print(F("addOption(dd,'Generic I2S',1);"));
|
uiScript.print(F("addOption(dd,'Generic I2S',1);"));
|
||||||
uiScript.print(F("addOption(dd,'ES7243',2);"));
|
uiScript.print(F("addOption(dd,'ES7243',2);"));
|
||||||
uiScript.print(F("addOption(dd,'SPH0654',3);"));
|
uiScript.print(F("addOption(dd,'SPH0654',3);"));
|
||||||
|
@ -2059,9 +2047,7 @@ const char AudioReactive::_config[] PROGMEM = "config";
|
||||||
const char AudioReactive::_dynamics[] PROGMEM = "dynamics";
|
const char AudioReactive::_dynamics[] PROGMEM = "dynamics";
|
||||||
const char AudioReactive::_frequency[] PROGMEM = "frequency";
|
const char AudioReactive::_frequency[] PROGMEM = "frequency";
|
||||||
const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
|
const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
|
||||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
|
const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
|
||||||
#endif
|
|
||||||
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
|
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
|
||||||
const char AudioReactive::_addPalettes[] PROGMEM = "add-palettes";
|
const char AudioReactive::_addPalettes[] PROGMEM = "add-palettes";
|
||||||
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
|
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
|
||||||
|
|
|
@ -140,7 +140,7 @@ class AudioSource {
|
||||||
virtual bool isInitialized(void) {return(_initialized);}
|
virtual bool isInitialized(void) {return(_initialized);}
|
||||||
|
|
||||||
/* identify Audiosource type - I2S-ADC or I2S-digital */
|
/* identify Audiosource type - I2S-ADC or I2S-digital */
|
||||||
typedef enum{Type_unknown=0, Type_I2SAdc=1, Type_I2SDigital=2} AudioSourceType;
|
typedef enum{Type_unknown=0, Type_Adc=1, Type_I2SDigital=2} AudioSourceType;
|
||||||
virtual AudioSourceType getType(void) {return(Type_I2SDigital);} // default is "I2S digital source" - ADC type overrides this method
|
virtual AudioSourceType getType(void) {return(Type_I2SDigital);} // default is "I2S digital source" - ADC type overrides this method
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -546,11 +546,11 @@ class ES8388Source : public I2SSource {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
//#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||||
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
|
//#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
|
||||||
#warning this MCU does not support analog sound input
|
// #warning this MCU does not support analog sound input
|
||||||
#endif
|
//#endif
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
// ADC over I2S is only availeable in "classic" ESP32
|
// ADC over I2S is only availeable in "classic" ESP32
|
||||||
|
@ -583,8 +583,8 @@ class I2SAdcSource : public I2SSource {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* identify Audiosource type - I2S-ADC*/
|
/* identify Audiosource type - ADC*/
|
||||||
AudioSourceType getType(void) {return(Type_I2SAdc);}
|
AudioSourceType getType(void) {return(Type_Adc);}
|
||||||
|
|
||||||
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||||
DEBUGSR_PRINTLN(F("I2SAdcSource:: initialize()."));
|
DEBUGSR_PRINTLN(F("I2SAdcSource:: initialize()."));
|
||||||
|
@ -743,6 +743,180 @@ class I2SAdcSource : public I2SSource {
|
||||||
int8_t _audioPin;
|
int8_t _audioPin;
|
||||||
int8_t _myADCchannel = 0x0F; // current ADC channel for analog input. 0x0F means "undefined"
|
int8_t _myADCchannel = 0x0F; // current ADC channel for analog input. 0x0F means "undefined"
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* ADC sampling with DMA
|
||||||
|
This microphone is an ADC pin sampled via ADC1 in continuous mode
|
||||||
|
This allows to sample in the background with high sample rates and minimal CPU load
|
||||||
|
note: only ADC1 channels can be used (ADC2 is used for WiFi)
|
||||||
|
ESP32 is not implemented as it supports I2S for ADC sampling (see above)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "driver/adc.h"
|
||||||
|
#include "hal/adc_types.h"
|
||||||
|
#define ADC_TIMEOUT 30 // Timout for one full frame of samples in ms (TODO: use (FFT_MIN_CYCLE + 5) but need to move the ifdefs before the include in the cpp file)
|
||||||
|
#define ADC_RESULT_BYTE SOC_ADC_DIGI_RESULT_BYTES //for C3 this is 4 (32bit, first 12bits is ADC result, see adc_digi_output_data_t)
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
#define MAX_ADC1_CHANNEL 4 // C3 has 5 channels (0-4)
|
||||||
|
#else
|
||||||
|
#define MAX_ADC1_CHANNEL 9 // ESP32, S2, S3 have 10 channels (0-9)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class DMAadcSource : public AudioSource {
|
||||||
|
public:
|
||||||
|
DMAadcSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) :
|
||||||
|
AudioSource(sampleRate, blockSize, sampleScale) {
|
||||||
|
// ADC continuous mode configuration
|
||||||
|
adc_dma_config = {
|
||||||
|
.max_store_buf_size = (unsigned)blockSize * ADC_RESULT_BYTE, // internal storage of DMA driver (in bytes, one sample is 4 bytes on C3&S3, 2bytes on S2 note: using 2x buffer size would reduce overflows but can add latency
|
||||||
|
.conv_num_each_intr = (unsigned)blockSize * ADC_RESULT_BYTE, // number of bytes per interrupt (or per frame, one sample contains 12bit of sample data)
|
||||||
|
.adc1_chan_mask = 0, // ADC1 channel mask (set to correct channel in initialize())
|
||||||
|
.adc2_chan_mask = 0, // dont use adc2 (used for wifi)
|
||||||
|
};
|
||||||
|
|
||||||
|
adcpattern = {
|
||||||
|
.atten = ADC_ATTEN_DB_11, // approx. 0-2.5V input range
|
||||||
|
.channel = 0, // channel mask (set to correct channel in initialize())
|
||||||
|
.unit = 0, // use ADC1
|
||||||
|
.bit_width = SOC_ADC_DIGI_MAX_BITWIDTH, // set to 12bit
|
||||||
|
};
|
||||||
|
|
||||||
|
dig_cfg = {
|
||||||
|
.conv_limit_en = 0, // disable limit (does not work right if enabled)
|
||||||
|
.conv_limit_num = 255, // set to max just in case
|
||||||
|
.pattern_num = 1, // single channel sampling
|
||||||
|
.adc_pattern = &adcpattern, // Pattern configuration
|
||||||
|
.sample_freq_hz = sampleRate, // sample frequency in Hz
|
||||||
|
.conv_mode = ADC_CONV_SINGLE_UNIT_1, // use ADC1 only
|
||||||
|
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2, // C3, S2 and S3 use this type, ESP32 uses TYPE1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* identify Audiosource type - ADC*/
|
||||||
|
AudioSourceType getType(void) {return(Type_Adc);}
|
||||||
|
|
||||||
|
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||||
|
DEBUGSR_PRINTLN(F("DMAadcSource:: initialize()."));
|
||||||
|
_myADCchannel = 0x0F;
|
||||||
|
if(!PinManager::allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
|
||||||
|
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_audioPin = audioPin;
|
||||||
|
|
||||||
|
// Determine Analog channel. Only Channels on ADC1 are supported
|
||||||
|
int8_t channel = digitalPinToAnalogChannel(_audioPin);
|
||||||
|
if (channel > MAX_ADC1_CHANNEL) {
|
||||||
|
DEBUGSR_PRINTF("Incompatible GPIO used for analog audio input: %d\n", _audioPin);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_myADCchannel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc_dma_config.adc1_chan_mask = (1 << channel); // update channel mask in DMA config
|
||||||
|
adcpattern.channel = channel; // update channel in pattern config
|
||||||
|
|
||||||
|
if (init_adc_continuous() != ESP_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
adc_digi_start(); //start sampling
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getSamples(float *buffer, uint16_t num_samples) {
|
||||||
|
|
||||||
|
int32_t framesize = num_samples * ADC_RESULT_BYTE; // size of one sample frame in bytes
|
||||||
|
uint8_t result[framesize]; // create a read buffer
|
||||||
|
uint32_t ret_num;
|
||||||
|
uint32_t totalbytes = 0;
|
||||||
|
uint32_t j = 0;
|
||||||
|
esp_err_t err;
|
||||||
|
if (_initialized) {
|
||||||
|
do {
|
||||||
|
err = adc_digi_read_bytes(result, framesize, &ret_num, ADC_TIMEOUT); // read samples
|
||||||
|
if ((err == ESP_OK || err == ESP_ERR_INVALID_STATE) && ret_num > 0) { // in invalid sate (internal buffer overrun), still read the last valid sample, then reset the ADC DMA afterwards (better than not having samples at all)
|
||||||
|
totalbytes += ret_num; // After an error, DMA buffer can be misaligned, returning partial frames. Found no other solution to re-align or flush the buffers, seems to be yet another IDF4 bug
|
||||||
|
|
||||||
|
// TODO: anything different if all channels of ADC1 are initialized in DMA init? currently not setting extra channels to zero like in the example.
|
||||||
|
|
||||||
|
if (totalbytes > framesize) { // got too many bytes to fit sample buffer
|
||||||
|
ret_num -= totalbytes - framesize; // discard extra samples
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE) {
|
||||||
|
adc_digi_output_data_t *p = reinterpret_cast<adc_digi_output_data_t*>(&result[i]);
|
||||||
|
buffer[j++] = float(((p->val & 0x0FFF))); // get the 12bit sample data and convert to float note: works on all format types
|
||||||
|
// for integer math: when scaling up to 16bit: compared to I2S mic the scaling seems about the same when not shifting at all, so need to divide by 16 after FFT if scaling up to 16bit
|
||||||
|
//Serial.print(buffer[j-1]); Serial.print(",");
|
||||||
|
}
|
||||||
|
//Serial.println("*");
|
||||||
|
//if (j < NUMSAMPLES) Serial.println("***SPLIT SAMPLE***"); // Even with split samples, the data is consistend (no discontinuities in a sine input signal input)
|
||||||
|
} else { // other read error, usually ESP_ERR_TIMEOUT (if DMA has stopped for some reason)
|
||||||
|
reset_DMA_ADC();
|
||||||
|
DEBUGSR_PRINTF("!!!!!!!! ADC ERROR !!!!!!!!!!\n");
|
||||||
|
return; // something went very wrong, just exit
|
||||||
|
}
|
||||||
|
} while (totalbytes < framesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove DC TODO: should really do this in int on C3... -> can use integer math if defined, see other PR
|
||||||
|
int32_t sum = 0;
|
||||||
|
for (int i = 0; i < num_samples; i++) sum += buffer[i];
|
||||||
|
int32_t mean = sum / num_samples;
|
||||||
|
for (int i = 0; i < num_samples; i++) buffer[i] -= mean; //uses static mean, as it should not change too much over time, deducted above
|
||||||
|
|
||||||
|
if (err == ESP_ERR_INVALID_STATE) { // error reading data, errors are: ESP_ERR_INVALID_STATE (=buffer overrun) or ESP_ERR_TIMEOUT, in both cases its best to reset the DMA ADC
|
||||||
|
DEBUGSR_PRINTF("ADC BFR OVERFLOW, RESETTING ADC\n");
|
||||||
|
Serial.println("BFR OVERFLOW");
|
||||||
|
reset_DMA_ADC();
|
||||||
|
}
|
||||||
|
//Serial.print("bytes:");
|
||||||
|
//Serial.println(totalbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinitialize() {
|
||||||
|
PinManager::deallocatePin(_audioPin, PinOwner::UM_Audioreactive);
|
||||||
|
_initialized = false;
|
||||||
|
_myADCchannel = 0x0F;
|
||||||
|
esp_err_t err;
|
||||||
|
adc_digi_stop();
|
||||||
|
delay(50); // just in case, give it some time
|
||||||
|
err = adc_digi_deinitialize();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
DEBUGSR_PRINTF("Failed to uninstall adc driver: %d\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
adc_digi_init_config_t adc_dma_config;
|
||||||
|
adc_digi_pattern_config_t adcpattern;
|
||||||
|
adc_digi_configuration_t dig_cfg;
|
||||||
|
int8_t _audioPin;
|
||||||
|
int8_t _myADCchannel = 0x0F; // current ADC channel for analog input. 0x0F means "undefined"
|
||||||
|
|
||||||
|
// Initialize ADC continuous mode with stored settings
|
||||||
|
esp_err_t init_adc_continuous() {
|
||||||
|
esp_err_t err = adc_digi_initialize(&adc_dma_config);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
DEBUGSR_PRINTF("Failed to init ADC DMA: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = adc_digi_controller_configure(&dig_cfg);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
DEBUGSR_PRINTF("Failed to init ADC sampling: %d\n", err);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_DMA_ADC(void) {
|
||||||
|
adc_digi_stop();
|
||||||
|
adc_digi_deinitialize();
|
||||||
|
//delay(1); // TODO: need any delay? seems to work fine without it and this code can be invoked at any time, so do not waste time here
|
||||||
|
init_adc_continuous();
|
||||||
|
adc_digi_start(); //start sampling
|
||||||
|
}
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SPH0645 Microphone
|
/* SPH0645 Microphone
|
||||||
|
@ -771,3 +945,4 @@ class SPH0654 : public I2SSource {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue