diff --git a/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino b/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino index df328d1d6b1..e3b40d2c8d5 100644 --- a/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino +++ b/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino @@ -1,83 +1,133 @@ /* - * 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. + 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 -#define ADC_INPUT ADC1_CHANNEL_4 //pin 32 -#define OUTPUT_PIN 27 -#define OUTPUT_VALUE 3800 -#define READ_DELAY 9000 //microseconds +// I2S +#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 PWM_FREQUENCY ((I2S_SAMPLE_RATE)/4) +#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 -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 = 8, + .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); -} + }; + 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 != adc1_config_channel_atten(ADC_INPUT, ADC_ATTEN_DB_11)){ + Serial.printf("Error setting up ADC attenuation. Halt!"); + while(1); + } -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); - } + 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); +void loop(){ +// The 4 high bits are the channel, and the data is inverted + 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, 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 += buffer[i] & 0x0FFF; + ++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 }