Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adc i2s mode with multiple channel pattern (IDFGH-803) #1991

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

phonec
Copy link
Contributor

@phonec phonec commented May 23, 2018

This allows you to simultaneously scan multiple ADC1 channels in accordance with the pattern table that defines the measurement rule for each channel. Added function adc_set_mode_inv() which enable inversion in this mode for DIG CTRL, unlike adc_set_data_inv().

@phonec phonec force-pushed the master branch 3 times, most recently from b3a56e3 to ef2d34e Compare May 23, 2018 18:03
@CLAassistant
Copy link

CLAassistant commented May 23, 2018

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Andrew Dikarev seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@pimvanpelt
Copy link

@phonec I've bumped into your work, as I'm trying to read all 8 channels of ADC1 simultaneously using I2S. What's the status of your pull request? The PR here redefines the API slightly, but before I take a closer look and patch this in, I was wondering if you had success in your own applications using this patch. Could you let me know ?

@phonec
Copy link
Contributor Author

phonec commented Sep 21, 2018

@pimvanpelt, The status of this request is still active, I think that this is the required functionality and should be useful to many. I use it in an energy meter with detect current leak with two current and one voltage sensors, this patch allows me to solve this task with a high I2S sampling rate of 96KHz for all sensors.

In this context the declaration of the adc_i2s_pattern[] inside the function should be 'static'.
Copy link
Contributor Author

@phonec phonec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adc i2s mode with multiple channel pattern #1991

adc i2s mode with multiple channel pattern espressif#1991
@Patrik-Berglund
Copy link

Any updates when this can be merge to master?

@szmodz
Copy link

szmodz commented Mar 19, 2019

@phonec Can you post a multichannel initialisation example? I'm running into weird problems, like dropped samples.

@github-actions github-actions bot changed the title adc i2s mode with multiple channel pattern adc i2s mode with multiple channel pattern (IDFGH-803) Mar 19, 2019
@phonec
Copy link
Contributor Author

phonec commented Mar 19, 2019

@szmodz, use only one I2S channel with the option I2S_CHANNEL_FMT_ONLY_RIGHT, try the initialization example

i2s_config_t i2s_config = {
     .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
    .sample_rate = 48000,
    .bits_per_sample = 16,
    .use_apll = 1,
    .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB,
    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
    .intr_alloc_flags = 0,
    .dma_buf_count = 2,
    .dma_buf_len = 1024
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);

static const adc_i2s_pattern_t adc_i2s_pattern[] = {
        {.atten = ADC_ATTEN_DB_11, .bits = ADC_WIDTH_BIT_12, .channel = ADC1_CHANNEL_0},
        {.atten = ADC_ATTEN_DB_11, .bits = ADC_WIDTH_BIT_12, .channel = ADC1_CHANNEL_3}
};
i2s_set_adc_mode(ADC_UNIT_1, adc_i2s_pattern, sizeof(adc_i2s_pattern));

vTaskDelay(5000/portTICK_PERIOD_MS);
i2s_adc_enable(I2S_NUM_0);

@pimvanpelt
Copy link

pimvanpelt commented Mar 19, 2019 via email

@szmodz
Copy link

szmodz commented Mar 19, 2019

@phonec I'm using the same config with a different sample rate, buffer size and 4 channels instead of 2.

It keeps losing 2 samples after exactly 254 correct samples.

If I change ADC_MAX_MEAS_NUM_DEFAULT in rtc_module.c to 127, there are 2 lost samples after each 126 correct samples, so it seems this is related to the APB_SARADC_MAX_MEAS_NUM value (adc_set_measure_limit in rtc_module.c). Disabling the limit doesn't work.

This is only easily noticeable when the pattern table size is greater than 2.

With a pattern table length of 3, it goes insane.

Any idea what's going on? The TRM is not exactly helpful here.

BTW. the check in adc_i2s_mode_init and adc_set_i2s_data_len disallows a 16 entry pattern table.

@szmodz
Copy link

szmodz commented Mar 20, 2019

Setting ADC_MAX_MEAS_NUM_DEFAULT (rtc_module.c) to 2 seems to fix this. No idea why though.

Using I2S_CHANNEL_FMT_ONLY_LEFT orders the samples correctly, starting from the first pattern table entry.

@szmodz
Copy link

szmodz commented Mar 20, 2019

That's not the whole story though. There are some weird interactions between the config options, ADC_MAX_MEAS_NUM_DEFAULT, the pattern table size and sample order.

@phonec
Copy link
Contributor Author

phonec commented Mar 20, 2019

@szmodz this PR only adds the ability to configure multiple channels and does not solve possible future issues. Unfortunately, I can not reproduce this repeatedly. Maybe you have an example clearly demonstrating the loss of samples?

@szmodz
Copy link

szmodz commented Mar 20, 2019

@phonec not a complete example, but should be enough. Call testStart().

example4.c.zip

Should print:

ch: 0 2
ch: 254 2
ch: 254 2

2 1 0 3
2 1 0 3
2 1 0 3
...
2 1 0 3
2 1 0 3
2 1 2 1
0 3 2 1
0 3 2 1
...
0 3 2 1
0 3 2 1
2 1 0 3

Breaks in the repeating patterns can be seen. One would expect to see a repeating pattern equal in length to the pattern table length.

pattern table lengths of 4 and 8 seem to work with ADC_MAX_MEAS_NUM_DEFAULT=2
leghts 3 and 6 seem to work with ADC_MAX_MEAS_NUM_DEFAULT=3

I2S_CHANNEL_FMT_ONLY_LEFT / I2S_CHANNEL_FMT_ONLY_RIGHT seems to affect the order within the pattern only

didn't check if the samples are actually regularly spaced.

@szmodz
Copy link

szmodz commented Mar 20, 2019

@phonec it's much worse with a pattern table length of 3 or 6.

@phonec
Copy link
Contributor Author

phonec commented Mar 21, 2019

@szmodz Thanks for the example, really weird behavior with a loss in the sequence of samples.

@koobest
Copy link
Contributor

koobest commented Mar 25, 2019

@szmodz,
APB_SARADC_MAX_MEAS_NUM determines the max number of continuous sampling of the digital ACD.

e.g:
You set APB_SARADC_MAX_MEAS_NUM = 200. When you start the ADC, the digital ADC will start working according to your ADC mode table. After 200 samples, the hardware will restart the ADC and continue the sampling,but you will lose some samples during the restart. This is the hardware limitation.

thanks !!

karlp added a commit to karlp/esp-idf that referenced this pull request Mar 5, 2021
Allow configuring ADC DMA via the i2s peripheral for multiple channels,
instead of hard limiting it to a single channel.

See-also: espressif#1991

Signed-off-by: Karl Palsson <karlp@tweak.net.au>
@giPeteR
Copy link

giPeteR commented Mar 6, 2021

@szmodz, use only one I2S channel with the option I2S_CHANNEL_FMT_ONLY_RIGHT, try the initialization example

i2s_config_t i2s_config = {
     .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
    .sample_rate = 48000,

I try this example for esp-arduino and get " 'adc_i2s_pattern_t' does not name a type ".
i search for library that use and i cant find !
what library have 'adc_i2s_pattern_t' that i can use?
is this example working?
thanks

Sorry for this very late feedback. (I got so feed up with ESP32 for a while, even though I love it.)
I have never ever used the "Arduino" framework for ESP32. (For ESP8266 yes.) Somehow it feels better to use the framework provided by the manufacturer (Espressif /ESP-IDF). Especially since ESP-IDF features a Real-Time OS (give or take).
I use Visual Studio Code for the programming (fantastic) and the command prompt for programming, e.g. "idf.py menuconfig ..idf.py build ..idf.py flash monitor...
(I've even set up an RPi for OCD debugging, but that's a completely different story.)

And as @szmodz says, use only One channel, regardless 1-8 inputs.
I use I2S_CHANNEL_FMT_ONLY_LEFT because I don't want the 1st data to be dropped, but it doesn't really matter.

i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
.sample_rate = totRate, // Needs to be 'something'. It is set manually below.
.bits_per_sample = I2S_SAMPLE_BITS, // 16bit to get 2*dma_buf_len.
.communication_format = I2S_COMM_FORMAT_STAND_MSB, // I2S_COMM_FORMAT_I2S_MSB/LSB doesn't change ADC order. Old comment, have not tested lately.
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // Only 'one' channel when using pattern tables.
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL3|ESP_INTR_FLAG_IRAM, // INT priority: examples use 0 = lowest.
.dma_buf_count = I2S_DMA_BUFFERS, // Min 2, Max 128.
.dma_buf_len = I2S_DMA_BUF_LEN, // Min 8, Max 1024. Size * 2, in i2s.c
.use_apll = false, // Can't be used with internal ADC.
.fixed_mclk = 0 // fi2s_clk can be set manually, but i2s calculations will screw it anyway.
};

And the above of course uses other #define's for buffers and such but it's based on the examples/peripherals/i2s_adc_dac/main/app_main.c

karlp added a commit to karlp/esp-idf that referenced this pull request Mar 9, 2021
Add a new API for configuring ADC DMA via the i2s peripheral for
multiple channels, but leave the existing API for a single channel
in place.

Inspired by: espressif#1991

Signed-off-by: Karl Palsson <karlp@tweak.net.au>
@kletze
Copy link

kletze commented Apr 22, 2021

@szmodz - Use the Source, Luke!

// initialization
i2s_config_t i2s_config = {
	.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
	.sample_rate =  8000,
	.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
	.communication_format = I2S_COMM_FORMAT_I2S_MSB,
	.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
	.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
	.dma_buf_count = 2,
	.dma_buf_len = 1024,
	.use_apll = false,
};
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0));
ESP_ERROR_CHECK(i2s_adc_enable(I2S_NUM_0));

// workaround
vTaskDelay(10 / portTICK_PERIOD_MS);

// enable continuous adc sampling
SYSCON.saradc_ctrl2.meas_num_limit = 0;

// channel, attenation, bit width
SYSCON.saradc_sar1_patt_tab[0] = ((ADC1_CHANNEL_0 << 4) | (ADC_WIDTH_BIT_12 << 2) | ADC_ATTEN_DB_11) << 24;

}

Hello,
I just stumbled across this post while searching for some solutions to my problem. I want to sample an analog value with the maximum possible sample rate of the ESP32 (as I read this should be 2Msps), so I use the I2s functionality (esp-idf version 4.1).
I only need one analog input channel.

For initializing I2S I use the same code as @amedes.The problem is that the i2s_read function only writes zeros to the buffer if I use a sample rate higher than 10 ksps, for example '.sample_rate = 20000".
For debugging purposes, I measure the WS clock with an oscilloscope and can tell that the correct sample rate is being used.

Have any of you guys encountered a similar problem?

@giPeteR could you tell me what you had to change to achieve a sample rate of 2Msps?

@amedes
Copy link

amedes commented Apr 24, 2021

@kletze this is the code for 2Msps.

    i2s_config_t i2s_conf = {
    	.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
	    .sample_rate = 1000000,
	    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
	    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
	    .communication_format = I2S_COMM_FORMAT_I2S_MSB,
	    .intr_alloc_flags = 0,
	    .dma_buf_count = 2,
	    .dma_buf_len = 1024,
	    .use_apll = false
    };
    ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_conf, 0, NULL));
    ESP_ERROR_CHECK(i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0));
    ESP_ERROR_CHECK(i2s_adc_enable(I2S_NUM_0));

    // delay for I2S bug workaround
    vTaskDelay(10 / portTICK_PERIOD_MS);

    // ***IMPORTANT*** enable continuous adc sampling
    SYSCON.saradc_ctrl2.meas_num_limit = 0;

    // ADC setting
    SYSCON.saradc_sar1_patt_tab[0] = ((ADC1_CHANNEL_0 << 4) | (ADC_WIDTH_BIT_12 << 2) | ADC_ATTEN_DB_11) << 24;
    SYSCON.saradc_ctrl.sar1_patt_len = 0;

    // reduce sample time for 2Msps
    SYSCON.saradc_ctrl.sar_clk_div = 2;
    SYSCON.saradc_fsm.sample_cycle = 2;

    // sampling rate 2Msps setting
    I2S0.clkm_conf.clkm_div_num = 20;
    I2S0.clkm_conf.clkm_div_b = 0;
    I2S0.clkm_conf.clkm_div_a = 1;
    I2S0.sample_rate_conf.rx_bck_div_num = 2;

But the ADC samples have much noise like this (captured 10kHz sine wave).
image
I have no idea to reduce the noise.
Good luck!

@kletze
Copy link

kletze commented Apr 25, 2021

Thanks @amedes for sharing your code. But when reading from the DMA buffer with the library function "i2s_read(I2S_NUM_0, (char*)i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);" the function still returns all zeros.

In my application, everything works as expected as long as my sample rate is less than 10 ksps. But as soon as I increase the sample rate to more than 10 ksps, the i2s_read function returns only zeros.

Is there anything specific I need to consider when reading from the DMA buffer?

@amedes
Copy link

amedes commented Apr 26, 2021

@kletze here is my test code. I compiled the code using ESP-IDF v4.2.
You need to connect GPIO36(ADC1 CH0) and GPIO25(DAC1) to capture the sine wave.
May the Force be with you.

// I2S ADC 2Msps example
// You need to connect GPIO36(ADC1_CHANNEL_0) and GPIO25(DAC_1)
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"

#include "driver/i2s.h"
#include "driver/adc.h"
#include "driver/dac.h"
#include "soc/syscon_periph.h"
#include "soc/i2s_periph.h"
#include "soc/sens_periph.h"

#define SAMPLE_RATE (2000 * 1000)
#define BUF_LEN 1024
#define PRINT_SIZE 256

void i2s_init(void)
{
    i2s_config_t i2s_conf = {
    	.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
	    .sample_rate = 1000000,
	    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
	    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
	    .communication_format = I2S_COMM_FORMAT_I2S_MSB,
	    .intr_alloc_flags = 0,
	    .dma_buf_count = 2,
	    .dma_buf_len = BUF_LEN,
	    .use_apll = false
    };
    ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_conf, 0, NULL));
    ESP_ERROR_CHECK(i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0));
    ESP_ERROR_CHECK(i2s_adc_enable(I2S_NUM_0));

    // delay for I2S bug workaround
    vTaskDelay(10 / portTICK_PERIOD_MS);

    // ***IMPORTANT*** enable continuous adc sampling
    SYSCON.saradc_ctrl2.meas_num_limit = 0;

    // ADC setting
    SYSCON.saradc_sar1_patt_tab[0] = ((ADC1_CHANNEL_0 << 4) | (ADC_WIDTH_BIT_12 << 2) | ADC_ATTEN_DB_11) << 24;
    SYSCON.saradc_ctrl.sar1_patt_len = 0;

    // reduce sample time for 2Msps
    SYSCON.saradc_ctrl.sar_clk_div = 2;
    SYSCON.saradc_fsm.sample_cycle = 2;

    // sampling rate 2Msps setting
    I2S0.clkm_conf.clkm_div_num = 20;
    I2S0.clkm_conf.clkm_div_b = 0;
    I2S0.clkm_conf.clkm_div_a = 1;
    I2S0.sample_rate_conf.rx_bck_div_num = 2;
}

void dac_init(void)
{
    dac_cw_config_t cw = {
        .en_ch = DAC_CHANNEL_1,
        .scale = DAC_CW_SCALE_2,
        .phase = DAC_CW_PHASE_0,
        .freq = 10500, // about 10Khz
        .offset = 1,
    };

    ESP_ERROR_CHECK(dac_output_enable(DAC_CHANNEL_1));
    ESP_ERROR_CHECK(dac_cw_generator_config(&cw));
    ESP_ERROR_CHECK(dac_cw_generator_enable());
}

#define TAG "I2S"

void i2s_read_samples(void)
{
    static uint16_t buf[BUF_LEN];
    int samples = 0;
    size_t bytes_read;
    int i;

    while (1) {
        if (i2s_read(I2S_NUM_0, buf, BUF_LEN*sizeof(uint16_t), &bytes_read, portMAX_DELAY) != ESP_OK) {
            ESP_LOGW(TAG, "i2s_read() fail");
            continue;
        }

        samples += BUF_LEN;

        // output samples every 5sec
        if (samples >= SAMPLE_RATE * 5) {
            samples -= SAMPLE_RATE * 5;
            
            // output only 256 samples
    	    for (i = 0; i < PRINT_SIZE; i++) {
                printf("%d\n", buf[i ^ 1] & 0x0fff);
	        }
            printf("----------------\n");
        }
    }
}

void app_main(void)
{
    dac_init();
    i2s_init();
    i2s_read_samples();
}

@kletze
Copy link

kletze commented Apr 26, 2021

Hi @amedes,

I updated from esp-idf v4.1 to v4.2. Now the code is working as it should.

Thank you very much for you patience.

@giPeteR
Copy link

giPeteR commented Jun 12, 2021

@amedes

But the ADC samples have much noise like this (captured 10kHz sine wave).
I have no idea to reduce the noise.
Good luck!

Possible problems:

  • DAC's are normally followed by an analogue low pass filter set to fs/2.
  • You may need a buffer to feed the ADC (with low pass filter fs/2). ADC's can draw a lot of pulsed current at the input.
  • High sample rates creates more noise.
  • Noise from digital signals close to the ADC & DAC pins.

Picture below is a signal from my cars flywheel sensor, about 7kHz. It is 1 of 4 channels sampling at a total of 600kSps.
As you can see it is quite smooth even though my setup has long cables, poor filtering... but still good.
image

@0xFEEDC0DE64
Copy link
Contributor

what is the status of this? I tried rebasing it but without luck, too many conflicts which I cannot resolve.

@mattitrux
Copy link

@amedes

But the ADC samples have much noise like this (captured 10kHz sine wave).
I have no idea to reduce the noise.
Good luck!

Possible problems:

  • DAC's are normally followed by an analogue low pass filter set to fs/2.
  • You may need a buffer to feed the ADC (with low pass filter fs/2). ADC's can draw a lot of pulsed current at the input.
  • High sample rates creates more noise.
  • Noise from digital signals close to the ADC & DAC pins.

Picture below is a signal from my cars flywheel sensor, about 7kHz. It is 1 of 4 channels sampling at a total of 600kSps. As you can see it is quite smooth even though my setup has long cables, poor filtering... but still good. image

Hi giPeter, thanks for the tips you threw in this topic.
I am trying to achieve 2Msps too, but i can't overcome 234Ksps with APLL and 83Ksps without it.
I tried to integrate your suggestions in my code but it still not working.
Would you mind posting yours please? You could save me weeks. Thank you in advance.
Mattia

@hasankarainfo
Copy link

This is my application

h library

#pragma once
#include "driver/adc.h"
#include "stdint.h"
//\\\_\__\___\____\_____\______
//\\\			TYPEDEFS
//\\\_\__\___\____\_____\______

typedef struct
{
	float x[2];
	float y[2];

} lin_interp_ts;
///\\\-\---\----\------\-------\--------\----------\-----------\-------------
extern float lin_interp_calc(lin_interp_ts *li, float val);
extern float lin_interp_raw(float x0, float y0, float x1, float y1, float val);
///\\\-\---\----\------\-------\--------\----------\-----------\-------------
extern long adc_result[ADC1_CHANNEL_MAX];

extern void app_adc_start(void);

c library

/*
 *  ADC1 SCAN MODE CONVERSION
*/

//\\\_\__\___\____\_____\______
//\\\			INCLUDES
//\\\_\__\___\____\_____\______

#include "AnalogRead.h"

#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_event_loop.h"

#include "soc/syscon_periph.h"
#include "driver/i2s.h"
#include <driver/adc.h>
#include <driver/dac.h>

//\\\_\__\___\____\_____\______
//\\\			DEFINES
//\\\_\__\___\____\_____\______

#define SAMPLE_RATE (100000)
#define I2S_NUM (0)
#define filt_w 100

#define TEST_SGN_EN 1
#define TEST_SGN_ADC_CHAN ADC1_CHANNEL_4
#define TEST_SGN_DAC_CHAN DAC_CHANNEL_1

//\\\_\__\___\____\_____\______
//\\\			VARIABLES
//\\\_\__\___\____\_____\______
long adc_result[ADC1_CHANNEL_MAX];
static uint32_t adc_tot[ADC1_CHANNEL_MAX];
static uint32_t adc_head[ADC1_CHANNEL_MAX];
static int32_t adc_summ[filt_w][ADC1_CHANNEL_MAX];

static QueueHandle_t s_i2s_event_queue;

static uint16_t i2s_read_buff[filt_w * ADC1_CHANNEL_MAX ];
///\\\-\---\----\------\-------\--------\----------\-----------\-------------

static const int adc_x[] = {
	0,
	100,
	200,
	300,
	400,
	500,
	600,
	700,
	800,
	900,
	1000,
	1100,
	1200,
	1300,
	1400,
	1500,
	1600,
	1700,
	1800,
	1900,
	2000,
	2100,
	2200,
	2300,
	2400,
	2500,
	2600,
	2700,
	2800,
	2900,
	3000,
	3100,
	3200,
	3300,
	3400,
	3500,
	3600,
	3700,
	3800,
	3900,
	4000,
	4100,
};
static const int adc_y[] = {
	129,
	209,
	289,
	371,
	453,
	534,
	610,
	690,
	773,
	854,
	932,
	1009,
	1089,
	1167,
	1246,
	1327,
	1404,
	1483,
	1563,
	1644,
	1723,
	1800,
	1883,
	1962,
	2044,
	2127,
	2203,
	2282,
	2362,
	2438,
	2514,
	2590,
	2666,
	2742,
	2818,
	2894,
	2970,
	3046,
	3122,
	3198,
	3274,
	3350};
///\\\-\---\----\------\-------\--------\----------\-----------\-------------

float lin_interp_calc(lin_interp_ts *li, float val)
{
	float ca = (li->y[1] - li->y[0]) / (li->x[1] - li->x[0]);
	float cb = li->y[0] - li->x[0]* ca;
	return (ca * val + cb);
}

float lin_interp_raw(float x0, float y0, float x1, float y1, float val)
{
	lin_interp_ts li = { .x[0] = x0, .y[0] = y0, .x[1] = x1, .y[1] = y1 };
	return lin_interp_calc(&li, val);
}
///\\\-\---\----\------\-------\--------\----------\-----------\-------------
int adc_raw2volt(int raw)
{
	int ind = raw / 100;
	int max = (sizeof(adc_x) / sizeof(int)) - 2;
	if (ind > max)
	{
		ind = max;
	}
	int result = (int)lin_interp_raw(adc_x[ind], adc_y[ind], adc_x[ind + 1], adc_y[ind + 1], raw);

	return result;
}

///\\\-\---\----\------\-------\--------\----------\-----------\-------------

size_t bytes_read = 0;
static void _adc_scanner(void *arg)
{
#if (TEST_SGN_EN)
	dac_output_enable(TEST_SGN_DAC_CHAN);
#endif

	vTaskDelay(10 / portTICK_PERIOD_MS);
	i2s_adc_enable(I2S_NUM);
	while (1)
	{
		int i2s_read_len = filt_w * ADC1_CHANNEL_MAX * 2 ;

		system_event_t evt;
		if (xQueueReceive(s_i2s_event_queue, &evt, portMAX_DELAY) != pdPASS)
		{
			ESP_LOGI("i2s", "No event to consume - consider reducing i2s_scanner frequency");
			continue;
		}

		if (evt.event_id != 2)
		{ // Event is not I2S_EVENT_RX_DONE
			ESP_LOGI("i2s", "Event %d received, skipping", evt.event_id);
			continue;
		}

		i2s_read(I2S_NUM, (char *)i2s_read_buff, i2s_read_len, &bytes_read, 0);

		for (int i = 0; i < bytes_read / 2; i++)
		{
			uint8_t chan = (i2s_read_buff[i] >> 12) & 0x07;
			uint16_t adc_value = i2s_read_buff[i] & 0xfff;

			/*en eski deger cikarilir*/
			adc_tot[chan] -= adc_summ[adc_head[chan]][chan];
			adc_summ[adc_head[chan]][chan] = adc_value;
			/*en yeni deger eklenir*/
			adc_tot[chan] += adc_summ[adc_head[chan]][chan];

			adc_head[chan]++;
			if (adc_head[chan] >= filt_w)
				adc_head[chan] = 0;
		}

		for (int i = 0; i < ADC1_CHANNEL_MAX; i++)
		{
			adc_result[i] = adc_raw2volt(adc_tot[i] / filt_w);
		}

#if (TEST_SGN_EN)
		dac_output_voltage(TEST_SGN_DAC_CHAN, adc_result[TEST_SGN_ADC_CHAN] * 256 / 3350);
#endif
	}
}

void app_adc_start(void )
{

	i2s_config_t i2s_config = {
		.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
		.sample_rate = SAMPLE_RATE,
		.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
		.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
		.communication_format = I2S_COMM_FORMAT_STAND_I2S,
		.dma_buf_count = 4,
		.dma_buf_len = 128,
		.use_apll = true,
		.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1};

	i2s_driver_install(I2S_NUM, &i2s_config, 2, &s_i2s_event_queue);
	i2s_set_adc_mode(I2S_NUM, ADC1_CHANNEL_0);

	// This 32 bit register has 4 bytes for the first set of channels to scan.
	// Each byte consists of:
	// [7:4] Channel
	// [3:2] Bit Width; 3=12bit, 2=11bit, 1=10bit, 0=9bit
	// [1:0] Attenuation; 3=11dB, 2=6dB, 1=2.5dB, 0=0dB
	WRITE_PERI_REG(SYSCON_SARADC_SAR1_PATT_TAB1_REG, 0x3F2f1f0F);
	WRITE_PERI_REG(SYSCON_SARADC_SAR1_PATT_TAB2_REG, 0x7F6F5F4F); 
	WRITE_PERI_REG(SYSCON_SARADC_SAR1_PATT_TAB3_REG, 0);
	WRITE_PERI_REG(SYSCON_SARADC_SAR1_PATT_TAB4_REG, 0);

	// Scan multiple channels.
	SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR1_PATT_LEN, 0x7, SYSCON_SARADC_SAR1_PATT_LEN_S);

	// The raw ADC data is written to DMA in inverted form. Invert back.
	SET_PERI_REG_MASK(SYSCON_SARADC_CTRL2_REG, SYSCON_SARADC_SAR1_INV);

	xTaskCreate(_adc_scanner, "adc_scanner", 1024 * 2, NULL, 100, NULL);
}

@mattitrux
Copy link

mattitrux commented Mar 24, 2022 via email

@ICENacl
Copy link

ICENacl commented Jun 16, 2022

@kletze here is my test code. I compiled the code using ESP-IDF v4.2. You need to connect GPIO36(ADC1 CH0) and GPIO25(DAC1) to capture the sine wave. May the Force be with you.

// I2S ADC 2Msps example
// You need to connect GPIO36(ADC1_CHANNEL_0) and GPIO25(DAC_1)
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"

#include "driver/i2s.h"
#include "driver/adc.h"
#include "driver/dac.h"
#include "soc/syscon_periph.h"
#include "soc/i2s_periph.h"
#include "soc/sens_periph.h"

#define SAMPLE_RATE (2000 * 1000)
#define BUF_LEN 1024
#define PRINT_SIZE 256

void i2s_init(void)
{
    i2s_config_t i2s_conf = {
    	.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
	    .sample_rate = 1000000,
	    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
	    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
	    .communication_format = I2S_COMM_FORMAT_I2S_MSB,
	    .intr_alloc_flags = 0,
	    .dma_buf_count = 2,
	    .dma_buf_len = BUF_LEN,
	    .use_apll = false
    };
    ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_conf, 0, NULL));
    ESP_ERROR_CHECK(i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0));
    ESP_ERROR_CHECK(i2s_adc_enable(I2S_NUM_0));

    // delay for I2S bug workaround
    vTaskDelay(10 / portTICK_PERIOD_MS);

    // ***IMPORTANT*** enable continuous adc sampling
    SYSCON.saradc_ctrl2.meas_num_limit = 0;

    // ADC setting
    SYSCON.saradc_sar1_patt_tab[0] = ((ADC1_CHANNEL_0 << 4) | (ADC_WIDTH_BIT_12 << 2) | ADC_ATTEN_DB_11) << 24;
    SYSCON.saradc_ctrl.sar1_patt_len = 0;

    // reduce sample time for 2Msps
    SYSCON.saradc_ctrl.sar_clk_div = 2;
    SYSCON.saradc_fsm.sample_cycle = 2;

    // sampling rate 2Msps setting
    I2S0.clkm_conf.clkm_div_num = 20;
    I2S0.clkm_conf.clkm_div_b = 0;
    I2S0.clkm_conf.clkm_div_a = 1;
    I2S0.sample_rate_conf.rx_bck_div_num = 2;
}

void dac_init(void)
{
    dac_cw_config_t cw = {
        .en_ch = DAC_CHANNEL_1,
        .scale = DAC_CW_SCALE_2,
        .phase = DAC_CW_PHASE_0,
        .freq = 10500, // about 10Khz
        .offset = 1,
    };

    ESP_ERROR_CHECK(dac_output_enable(DAC_CHANNEL_1));
    ESP_ERROR_CHECK(dac_cw_generator_config(&cw));
    ESP_ERROR_CHECK(dac_cw_generator_enable());
}

#define TAG "I2S"

void i2s_read_samples(void)
{
    static uint16_t buf[BUF_LEN];
    int samples = 0;
    size_t bytes_read;
    int i;

    while (1) {
        if (i2s_read(I2S_NUM_0, buf, BUF_LEN*sizeof(uint16_t), &bytes_read, portMAX_DELAY) != ESP_OK) {
            ESP_LOGW(TAG, "i2s_read() fail");
            continue;
        }

        samples += BUF_LEN;

        // output samples every 5sec
        if (samples >= SAMPLE_RATE * 5) {
            samples -= SAMPLE_RATE * 5;
            
            // output only 256 samples
    	    for (i = 0; i < PRINT_SIZE; i++) {
                printf("%d\n", buf[i ^ 1] & 0x0fff);
	        }
            printf("----------------\n");
        }
    }
}

void app_main(void)
{
    dac_init();
    i2s_init();
    i2s_read_samples();
}

this code help me a lot, thanks!!!!!!!!!!!!!!!!

@ICENacl
Copy link

ICENacl commented Jul 25, 2022

@kletze here is my test code. I compiled the code using ESP-IDF v4.2. You need to connect GPIO36(ADC1 CH0) and GPIO25(DAC1) to capture the sine wave. May the Force be with you.

// I2S ADC 2Msps example
// You need to connect GPIO36(ADC1_CHANNEL_0) and GPIO25(DAC_1)
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"

#include "driver/i2s.h"
#include "driver/adc.h"
#include "driver/dac.h"
#include "soc/syscon_periph.h"
#include "soc/i2s_periph.h"
#include "soc/sens_periph.h"

#define SAMPLE_RATE (2000 * 1000)
#define BUF_LEN 1024
#define PRINT_SIZE 256

void i2s_init(void)
{
    i2s_config_t i2s_conf = {
    	.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
	    .sample_rate = 1000000,
	    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
	    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
	    .communication_format = I2S_COMM_FORMAT_I2S_MSB,
	    .intr_alloc_flags = 0,
	    .dma_buf_count = 2,
	    .dma_buf_len = BUF_LEN,
	    .use_apll = false
    };
    ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2s_conf, 0, NULL));
    ESP_ERROR_CHECK(i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0));
    ESP_ERROR_CHECK(i2s_adc_enable(I2S_NUM_0));

    // delay for I2S bug workaround
    vTaskDelay(10 / portTICK_PERIOD_MS);

    // ***IMPORTANT*** enable continuous adc sampling
    SYSCON.saradc_ctrl2.meas_num_limit = 0;

    // ADC setting
    SYSCON.saradc_sar1_patt_tab[0] = ((ADC1_CHANNEL_0 << 4) | (ADC_WIDTH_BIT_12 << 2) | ADC_ATTEN_DB_11) << 24;
    SYSCON.saradc_ctrl.sar1_patt_len = 0;

    // reduce sample time for 2Msps
    SYSCON.saradc_ctrl.sar_clk_div = 2;
    SYSCON.saradc_fsm.sample_cycle = 2;

    // sampling rate 2Msps setting
    I2S0.clkm_conf.clkm_div_num = 20;
    I2S0.clkm_conf.clkm_div_b = 0;
    I2S0.clkm_conf.clkm_div_a = 1;
    I2S0.sample_rate_conf.rx_bck_div_num = 2;
}

void dac_init(void)
{
    dac_cw_config_t cw = {
        .en_ch = DAC_CHANNEL_1,
        .scale = DAC_CW_SCALE_2,
        .phase = DAC_CW_PHASE_0,
        .freq = 10500, // about 10Khz
        .offset = 1,
    };

    ESP_ERROR_CHECK(dac_output_enable(DAC_CHANNEL_1));
    ESP_ERROR_CHECK(dac_cw_generator_config(&cw));
    ESP_ERROR_CHECK(dac_cw_generator_enable());
}

#define TAG "I2S"

void i2s_read_samples(void)
{
    static uint16_t buf[BUF_LEN];
    int samples = 0;
    size_t bytes_read;
    int i;

    while (1) {
        if (i2s_read(I2S_NUM_0, buf, BUF_LEN*sizeof(uint16_t), &bytes_read, portMAX_DELAY) != ESP_OK) {
            ESP_LOGW(TAG, "i2s_read() fail");
            continue;
        }

        samples += BUF_LEN;

        // output samples every 5sec
        if (samples >= SAMPLE_RATE * 5) {
            samples -= SAMPLE_RATE * 5;
            
            // output only 256 samples
    	    for (i = 0; i < PRINT_SIZE; i++) {
                printf("%d\n", buf[i ^ 1] & 0x0fff);
	        }
            printf("----------------\n");
        }
    }
}

void app_main(void)
{
    dac_init();
    i2s_init();
    i2s_read_samples();
}

hello, i wonder why it need to XOR when output the data printf("%d\n", buf[i ^ 1] & 0x0fff);,is there anything i miss in the ESP-IDF programming guide? I would appreciate it if you could reply me.

@amedes
Copy link

amedes commented Jul 25, 2022

@ICENacl

hello, i wonder why it need to XOR when output the data printf("%d\n", buf[i ^ 1] & 0x0fff);,is there anything i miss in the ESP-IDF programming guide? I would appreciate it if you could reply me.

Please see Figure70 mode1 in TRM ver.4.6 p309.
RX_FIFO holds two ADC data and the storing order is upper 16bit first.
ESP32 is little endian machine. So the ADC data will be reversed in memory.

Figure 70: Modes of Writing Received Data into FIFO, ESP32 TRM (Version 4.6) p309
https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf

@ICENacl
Copy link

ICENacl commented Jul 26, 2022

Please see Figure70 mode1 in TRM ver.4.6 p309. RX_FIFO holds two ADC data and the storing order is upper 16bit first. ESP32 is little endian machine. So the ADC data will be reversed in memory.

Figure 70: Modes of Writing Received Data into FIFO, ESP32 TRM (Version 4.6) p309 https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf

i already understand it, thanks a lot for your reply, wish you have a good day 👍

@a442509097
Copy link

size_t bytesRead = 0;
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_6);
i2s_read(I2S_NUM_0, (void*)samples, sizeof(samples), &bytesRead, portMAX_DELAY); 
FFT01 handle

-------------------------------------------------------

bytesRead = 0;
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_7);
i2s_read(I2S_NUM_0, (void*)samples02, sizeof(samples02), &bytesRead, portMAX_DELAY);
FFT02 handle

@summerfind
Copy link

Hi all.
I still got compiling error for ESP32S3 in Arduino IDE: 'I2S_MODE_ADC_BUILT_IN' was not declared in this scope?
any solution?
Thanks

@AntonioFromBrazil
Copy link

Folks, I need to ask a very basic and stupid question: If the i2s configuration was made to capture two ADC signals with DMA such as channel 0 and channel 6, for instance, how to inform the buffers should be used for each channel ? Sorry for this question but every examples shows only the configutarion... I could not find any complete example that shows what to do next to finally get access to ADC values........

@SerhiiZhilin
Copy link

SerhiiZhilin commented Aug 2, 2023

i2s_driver_install
@giPeteR
Hello. Could you please share the code of 8 ch sampling?

@bigbrassbed
Copy link

bigbrassbed commented Aug 2, 2023 via email

@bigbrassbed
Copy link

bigbrassbed commented Aug 2, 2023 via email

@bigbrassbed
Copy link

bigbrassbed commented Aug 2, 2023 via email

@bigbrassbed
Copy link

bigbrassbed commented Aug 2, 2023 via email

@AntonioFromBrazil
Copy link

AntonioFromBrazil commented Aug 3, 2023

Thanks everyone for advice. I tryied to use a couple of examples but I did not succeded. I think my problem is much easier than the examples showned. What I want is just transform the code bellow to read two channels simultaneoulsly. As it is bellow it is working well for one channel only... Thanks again for any help.

//------------------------------------------------------------------------------------
#include <driver/i2s.h>

#define I2S_SAMPLE_RATE (1000000)
#define I2S_DMA_BUF_LEN (50)

size_t bytes_read;
uint16_t buffer[I2S_DMA_BUF_LEN] = {0};

void i2sInit() {
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN | I2S_CHANNEL_MONO),
    .sample_rate = I2S_SAMPLE_RATE,      
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
    .communication_format = I2S_COMM_FORMAT_I2S_MSB,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 2,
    .dma_buf_len = I2S_DMA_BUF_LEN,
    .use_apll = true,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0
  };
  
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
  adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_11);
  i2s_set_adc_mode(ADC_UNIT_1, (ADC1_CHANNEL_3));
  i2s_adc_enable(I2S_NUM_0);
}

void setup()
{
  Serial.begin(115200);
  i2sInit();
}

void loop()
{
  i2s_read(I2S_NUM_0, &buffer, sizeof(buffer), &bytes_read, 0);
  for (int i=0; i<50; i++){
    Serial.print("0x");Serial.print(buffer[i],HEX);Serial.print(",");
  }
  Serial.println("");  
  delay(1000);
}  
//------------------------------------------------------------------------------------

@AntonioFromBrazil
Copy link

So I will be forever grateful if someone would help me in this question. I am trying hard to underestand the i2s Espressif docs but sincerelly it is too much confuse and look like it is for professionals only. Unfortunatelly it is not my case :(

@AntonioFromBrazil
Copy link

AntonioFromBrazil commented Sep 2, 2023

On @phonec post dated from March,19,2023, about how to read two ADC channels with i2s/DMA (copied bellow), the code uses adc_i2s_pattern_t function that is NOT present in none of available IDF versions. Would you please tell me what version did you use to compile the code or where can I find the adc_i2s_pattern_t function ? Thanks

@szmodz, use only one I2S channel with the option I2S_CHANNEL_FMT_ONLY_RIGHT, try the initialization example

i2s_config_t i2s_config = {
     .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
    .sample_rate = 48000,
    .bits_per_sample = 16,
    .use_apll = 1,
    .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB,
    .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
    .intr_alloc_flags = 0,
    .dma_buf_count = 2,
    .dma_buf_len = 1024
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);

static const adc_i2s_pattern_t adc_i2s_pattern[] = {
        {.atten = ADC_ATTEN_DB_11, .bits = ADC_WIDTH_BIT_12, .channel = ADC1_CHANNEL_0},
        {.atten = ADC_ATTEN_DB_11, .bits = ADC_WIDTH_BIT_12, .channel = ADC1_CHANNEL_3}
};
i2s_set_adc_mode(ADC_UNIT_1, adc_i2s_pattern, sizeof(adc_i2s_pattern));

vTaskDelay(5000/portTICK_PERIOD_MS);
i2s_adc_enable(I2S_NUM_0);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet