From 5646c2eada3e0b64655acc5a604a193e8dca5e26 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Fri, 24 Jun 2022 14:20:46 +0200 Subject: [PATCH 1/2] Initial version of reworked example --- .../examples/I2S/HiFreq_ADC/HiFreq_ADC.ino | 161 ++++++++++++------ 1 file changed, 105 insertions(+), 56 deletions(-) diff --git a/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino b/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino index df328d1d6b1..0ecfa346cb6 100644 --- a/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino +++ b/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino @@ -1,83 +1,132 @@ /* - * This is an example to read analog data at high frequency using the I2S peripheral - * Run a wire between pins 27 & 32 - * The readings from the device will be 12bit (0-4096) - */ + This example demonstrates I2S ADC capability to sample high frequency analog signals (up to 5MHz sampling - 2.5MHz input signal) + The PWM signal generated with ledc is only for ease of use when first trying out. + To sample the generated signal connect default pins 27(PWM) and 32(Sampling) together. + If you do not wish to generate PWM simply comment out the definition of constant GENERATE_PWM + Try to change the PWM_DUTY_PERCENT and see how to averaged value changes. + + The maximum for I2S ADC sampling frequency is 5MHz (value 5000000), however there will be many values repeated because the real + sampling frequency is much lower - + + By default this example will print values compatible with Arduino plotter + 1. signal - all values + 2. signal - averaged value + + You can change the number of sample over which is the signal averaged by changing value of AVERAGE_EVERY_N_SAMPLES + If you comment the definition altogether the averaging will not be performed nor printed. + + If you do not wish to print every value, simply comment definition of constant PRINT_ALL_VALUES + + Note: ESP prints messages at startup which will pollute Arduino IDE Serial plotter legend. + To avoid this pollution, start the plotter after startup (op restart) +*/ #include -#define I2S_SAMPLE_RATE 78125 +// I2S +//#define I2S_SAMPLE_RATE 5000000 // Max sampling frequency is 5MHz (value 5000000) +#define I2S_SAMPLE_RATE 277777 // Real max sampling frequency = 277777 Hz #define ADC_INPUT ADC1_CHANNEL_4 //pin 32 +#define I2S_DMA_BUF_LEN 1024 + +// PWM +#define GENERATE_PWM #define OUTPUT_PIN 27 -#define OUTPUT_VALUE 3800 -#define READ_DELAY 9000 //microseconds +#define PWM_FREQUENCY ((I2S_SAMPLE_RATE)/4) +#define PWM_DUTY_PERCENT 50 +#define PWM_RESOLUTION_BITS 6 // Lower bit resolution enables higher frequency +#define PWM_DUTY_VALUE ((((1<<(PWM_RESOLUTION_BITS)))*(PWM_DUTY_PERCENT))/100) // Duty value used for setup function based on resolution -uint16_t adc_reading; +// Sample post processing +#define PRINT_ALL_VALUES +#define AVERAGE_EVERY_N_SAMPLES 100 -void i2sInit() -{ - i2s_config_t i2s_config = { +void i2sInit(){ + i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN), .sample_rate = I2S_SAMPLE_RATE, // The format of the signal using ADC_BUILT_IN .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_I2S_MSB, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 4, - .dma_buf_len = 8, + .dma_buf_count = 2, + .dma_buf_len = I2S_DMA_BUF_LEN, .use_apll = false, .tx_desc_auto_clear = false, .fixed_mclk = 0 - }; - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT); - i2s_adc_enable(I2S_NUM_0); -} - -void reader(void *pvParameters) { - uint32_t read_counter = 0; - uint64_t read_sum = 0; -// The 4 high bits are the channel, and the data is inverted - uint16_t offset = (int)ADC_INPUT * 0x1000 + 0xFFF; - size_t bytes_read; - while(1){ - uint16_t buffer[2] = {0}; - i2s_read(I2S_NUM_0, &buffer, sizeof(buffer), &bytes_read, 15); - //Serial.printf("%d %d\n", offset - buffer[0], offset - buffer[1]); - if (bytes_read == sizeof(buffer)) { - read_sum += offset - buffer[0]; - read_sum += offset - buffer[1]; - read_counter++; - } else { - Serial.println("buffer empty"); - } - if (read_counter == I2S_SAMPLE_RATE) { - adc_reading = read_sum / I2S_SAMPLE_RATE / 2; - //Serial.printf("avg: %d millis: ", adc_reading); - //Serial.println(millis()); - read_counter = 0; - read_sum = 0; - i2s_adc_disable(I2S_NUM_0); - delay(READ_DELAY); - i2s_adc_enable(I2S_NUM_0); - } + }; + Serial.printf("Attempting to setup I2S ADC with sampling frequency %d Hz\n", I2S_SAMPLE_RATE); + if(ESP_OK != i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)){ + Serial.printf("Error installing I2S. Halt!"); + while(1); } + if(ESP_OK != i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT)){ + Serial.printf("Error setting up ADC. Halt!"); + while(1); + } + if(ESP_OK != i2s_adc_enable(I2S_NUM_0)){ + Serial.printf("Error enabling ADC. Halt!"); + while(1); + } + Serial.printf("I2S ADC setup ok\n"); } void setup() { Serial.begin(115200); - // Put a signal out on pin - uint32_t freq = ledcSetup(0, I2S_SAMPLE_RATE, 10); - Serial.printf("Output frequency: %d\n", freq); - ledcWrite(0, OUTPUT_VALUE/4); + +#ifdef GENERATE_PWM + // PWM setup + Serial.printf("Setting up PWM: frequency = %d; resolution bits %d; Duty cycle = %d; duty value = %d, Output pin = %d\n", PWM_FREQUENCY, PWM_RESOLUTION_BITS, PWM_DUTY_PERCENT, PWM_DUTY_VALUE, OUTPUT_PIN); + uint32_t freq = ledcSetup(0, PWM_FREQUENCY, PWM_RESOLUTION_BITS); + if(freq != PWM_FREQUENCY){ + Serial.printf("Error setting up PWM. Halt!"); + while(1); + } ledcAttachPin(OUTPUT_PIN, 0); + ledcWrite(0, PWM_DUTY_VALUE); + Serial.printf("PWM setup ok\n"); +#endif + // Initialize the I2S peripheral i2sInit(); - // Create a task that will read the data - xTaskCreatePinnedToCore(reader, "ADC_reader", 2048, NULL, 1, NULL, 1); } void loop() { - delay(1020); - Serial.printf("ADC reading: %d\n", adc_reading); - delay(READ_DELAY); +// The 4 high bits are the channel, and the data is inverted +#if defined(PRINT_ALL_VALUES) || defined(AVERAGE_EVERY_N_SAMPLES) + uint16_t offset = (int)ADC_INPUT * 0x1000 + 0xFFF; +#endif + size_t bytes_read; + uint16_t buffer[I2S_DMA_BUF_LEN] = {0}; + +#ifdef AVERAGE_EVERY_N_SAMPLES + uint32_t read_counter = 0; + uint32_t averaged_reading = 0; + uint64_t read_sum = 0; +#endif + + while(1){ + i2s_read(I2S_NUM_0, &buffer, sizeof(buffer), &bytes_read, 15); + //Serial.printf("read %d Bytes\n", bytes_read); + + for(int i = 0; i < bytes_read/2; ++i){ +#ifdef PRINT_ALL_VALUES + //Serial.printf("[%d] = %d\n", i, offset - buffer[i]); // Print with indexes + Serial.printf("Signal:%d ", offset - buffer[i]); // Print compatible with Arduino Plotter +#endif +#ifdef AVERAGE_EVERY_N_SAMPLES + read_sum += offset - buffer[i]; + ++read_counter; + if(read_counter == AVERAGE_EVERY_N_SAMPLES){ + averaged_reading = read_sum / AVERAGE_EVERY_N_SAMPLES; + //Serial.printf("averaged_reading = %d over %d samples\n", averaged_reading, read_counter); // Print with additional info + Serial.printf("Averaged_signal:%d", averaged_reading); // Print compatible with Arduino Plotter + read_counter = 0; + read_sum = 0; + } +#endif +#if defined(PRINT_ALL_VALUES) || defined (AVERAGE_EVERY_N_SAMPLES) + Serial.printf("\n"); +#endif + } // for + } // while } From 819b218c78ead989a935db92924d06645332f659 Mon Sep 17 00:00:00 2001 From: Tomas Pilny Date: Mon, 27 Jun 2022 16:46:00 +0200 Subject: [PATCH 2/2] Minor updates --- .../examples/I2S/HiFreq_ADC/HiFreq_ADC.ino | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino b/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino index 0ecfa346cb6..e3b40d2c8d5 100644 --- a/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino +++ b/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino @@ -1,5 +1,5 @@ /* - This example demonstrates I2S ADC capability to sample high frequency analog signals (up to 5MHz sampling - 2.5MHz input signal) + This example demonstrates I2S ADC capability to sample high frequency analog signals. The PWM signal generated with ledc is only for ease of use when first trying out. To sample the generated signal connect default pins 27(PWM) and 32(Sampling) together. If you do not wish to generate PWM simply comment out the definition of constant GENERATE_PWM @@ -23,22 +23,21 @@ #include // I2S -//#define I2S_SAMPLE_RATE 5000000 // Max sampling frequency is 5MHz (value 5000000) -#define I2S_SAMPLE_RATE 277777 // Real max sampling frequency = 277777 Hz -#define ADC_INPUT ADC1_CHANNEL_4 //pin 32 -#define I2S_DMA_BUF_LEN 1024 +#define I2S_SAMPLE_RATE (277777) // Max sampling frequency = 277.777 kHz +#define ADC_INPUT (ADC1_CHANNEL_4) //pin 32 +#define I2S_DMA_BUF_LEN (1024) // PWM #define GENERATE_PWM -#define OUTPUT_PIN 27 +#define OUTPUT_PIN (27) #define PWM_FREQUENCY ((I2S_SAMPLE_RATE)/4) -#define PWM_DUTY_PERCENT 50 -#define PWM_RESOLUTION_BITS 6 // Lower bit resolution enables higher frequency +#define PWM_DUTY_PERCENT (50) +#define PWM_RESOLUTION_BITS (2) // Lower bit resolution enables higher frequency #define PWM_DUTY_VALUE ((((1<<(PWM_RESOLUTION_BITS)))*(PWM_DUTY_PERCENT))/100) // Duty value used for setup function based on resolution // Sample post processing #define PRINT_ALL_VALUES -#define AVERAGE_EVERY_N_SAMPLES 100 +#define AVERAGE_EVERY_N_SAMPLES (100) void i2sInit(){ i2s_config_t i2s_config = { @@ -48,7 +47,7 @@ void i2sInit(){ .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 2, + .dma_buf_count = 8, .dma_buf_len = I2S_DMA_BUF_LEN, .use_apll = false, .tx_desc_auto_clear = false, @@ -63,6 +62,11 @@ void i2sInit(){ Serial.printf("Error setting up ADC. Halt!"); while(1); } + if(ESP_OK != adc1_config_channel_atten(ADC_INPUT, ADC_ATTEN_DB_11)){ + Serial.printf("Error setting up ADC attenuation. Halt!"); + while(1); + } + if(ESP_OK != i2s_adc_enable(I2S_NUM_0)){ Serial.printf("Error enabling ADC. Halt!"); while(1); @@ -90,11 +94,8 @@ void setup() { i2sInit(); } -void loop() { +void loop(){ // The 4 high bits are the channel, and the data is inverted -#if defined(PRINT_ALL_VALUES) || defined(AVERAGE_EVERY_N_SAMPLES) - uint16_t offset = (int)ADC_INPUT * 0x1000 + 0xFFF; -#endif size_t bytes_read; uint16_t buffer[I2S_DMA_BUF_LEN] = {0}; @@ -110,11 +111,11 @@ void loop() { for(int i = 0; i < bytes_read/2; ++i){ #ifdef PRINT_ALL_VALUES - //Serial.printf("[%d] = %d\n", i, offset - buffer[i]); // Print with indexes - Serial.printf("Signal:%d ", offset - buffer[i]); // Print compatible with Arduino Plotter + //Serial.printf("[%d] = %d\n", i, buffer[i] & 0x0FFF); // Print with indexes + Serial.printf("Signal:%d ", buffer[i] & 0x0FFF); // Print compatible with Arduino Plotter #endif #ifdef AVERAGE_EVERY_N_SAMPLES - read_sum += offset - buffer[i]; + read_sum += buffer[i] & 0x0FFF; ++read_counter; if(read_counter == AVERAGE_EVERY_N_SAMPLES){ averaged_reading = read_sum / AVERAGE_EVERY_N_SAMPLES;