Faster ADC Reading

megomes
Posts: 1
Joined: Tue Jul 04, 2017 5:22 pm

Faster ADC Reading

Postby megomes » Tue Jul 04, 2017 5:33 pm

I'm using a ESP32 with ESP-IDF and the fastest ADC reading that I can get is 17.20 us. That means 58KHz.

It doesn't matter the sample size, the resolution, the clock subdivision, I can't get faster reading.

I've read on the Technical Reference Manual that the ADC can be used as a DIG SAR Mode which is meant to be High performance "the clock is much faster, therefore, the sample rate is highly increased" but I just can't make it work.

http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
[ page 413: 22.3.5 DIG SAR ADC Controllers ]

There is no need for high precision or 12 bits. I just need speed in the sample rate.

Does anyone knows how do I do this?

Thank you guys in advance!

ESP_Angus
Posts: 1235
Joined: Sun May 08, 2016 4:11 am

Re: Faster ADC Reading

Postby ESP_Angus » Thu Jul 06, 2017 11:02 am

One of the features still on our roadmap is to allow the I2S peripheral to read from a built-in ADC (in the same way you can already output to the built-in DAC). This is probably going to be the recommended approach for high frequency ADC input.

Unfortunately I don't have an ETA for you, sorry.

bluesceada
Posts: 3
Joined: Fri Sep 28, 2018 6:55 am

Re: Faster ADC Reading

Postby bluesceada » Fri Sep 28, 2018 8:57 am

Any news on the ADC DIG SAR Mode?

In my experiments I could sample the ADC faster (at about 104kHz for ADC1 and 98kHz for ADC2) using the ULP rather than main core(s), with this loop:

Code: Select all

// ...
   move r0, adc_data
measure:
   adc r2, 0, adc_channel + 1
   st r2,r0,0
   add r0, r0, 1
   jumpr measure, adc_data+ADC_WORDS,lt

(using jumpr with r0 is faster than using stage_inc and jumps)
Then in main you need to realign the data, since it is 16bit data aligned on 32bit:

Code: Select all

// ...
uint32_t* p_ulp_adc_data = &ulp_adc_data;
uint16_t adc_data[ADC_WORDS];
for (int i=0;i<ADC_WORDS;i++)
{
   uint16_t w = (*p_ulp_adc_data) & 0xffff;
   p_ulp_adc_data++;
   adc_data[i] = w;
}


To sample from main, I used a task with IRAM_ATTR and the code below. With the CPU @ 80MHz (for other reasons..) and normal debugging it then achieves about 18.5 kHz with ADC1 and 21.0 kHz with ADC2 (both ADCs at 12bit).
Disabling the assertion/debug level and set the compiler optimizations it improves to 20.4 kHz on ADC1 and 23.3 kHz on ADC2. (sdkconfig: CONFIG_OPTIMIZATION_LEVEL_RELEASE=y and CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED=y ).

Code: Select all

void IRAM_ATTR sampleAdcTask(void * pvParameters)
{
   for (int i=0;i<ADC_WORDS;i++)
   {
#ifdef USE_ADC1
      adc_data[i] = adc1_get_raw(ADC_CHAN_SEL); // for ADC1
#else // ADC2
      adc2_get_raw(ADC2_CHAN_SEL, ADC_WIDTH_12Bit, &adc_data[i]); // for ADC2
#endif
   }
}


I have checked the esp-idf code for adc2_get_raw and adc1_get_raw. They both seem to unnecessarily set a lot of options each time again for a single ADC sample, however when I tried to remove some of these (just a quick try), I didn't get valid ADC samples anymore (0x0000).

ESP_Sprite
Posts: 1941
Joined: Thu Nov 26, 2015 4:08 am

Re: Faster ADC Reading

Postby ESP_Sprite » Fri Sep 28, 2018 9:07 am

FWIW, the I2S driver supports ADC input now; there's an example in the ESP-IDF master tree. I've used it for up to 320Ksamps/second, and although I have no idea wrt the analog specs at that rate, it seemed to behave well.

bluesceada
Posts: 3
Joined: Fri Sep 28, 2018 6:55 am

Re: Faster ADC Reading

Postby bluesceada » Mon Oct 01, 2018 12:57 pm

Okay, so you probably mean examples/peripherals/i2s_adc_dac ? Then main/app_main.c , function example_i2s_adc_dac ?
Is that right? I can't seem to get it to work in my isolated case, maybe you can clarify where I should look?

For my understanding, the following code should sample as many samples as half of i2s_read_len (because .bits_per_sample is set to 16):

Code: Select all

char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
i2s_adc_enable(EXAMPLE_I2S_NUM);
i2s_read(EXAMPLE_I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
i2s_adc_disable(EXAMPLE_I2S_NUM);


But somehow it takes way too short to execute. I also don't understand why you set it like (16 * 1024) for the i2s_read_len. It definitely can't mean bits, since calloc even allocates per byte due to sizeof(char). With i2s_read_len=1024 and then subsequently calloc(1024, sizeof(uint16_t)); it would make sense to me.

If it is just bytes, it would mean the ADC is set to 8-bit mode, but EXAMPLE_I2S_SAMPLE_BITS is set to 16. What does that mean?

Can you clarify?

In the end, it is also completely unclear to me in which data format the i2s_read_buff would get filled, it seems to be only 8bit samples, which doesn't make sense if the ADC is set to 12bit (aligned to 16?). How is it supposed to work / how is the data aligned in the i2s_read_buff?

Thanks for clarifying. Maybe you can just write the few lines of code needed or guide me to another example...

ESP_Sprite
Posts: 1941
Joined: Thu Nov 26, 2015 4:08 am

Re: Faster ADC Reading

Postby ESP_Sprite » Thu Oct 04, 2018 4:52 am

bluesceada wrote:For my understanding, the following code should sample as many samples as half of i2s_read_len (because .bits_per_sample is set to 16):
<snip>
But somehow it takes way too short to execute.

I'm not that intimately familiar with the I2S driver, but this may be because the setup of the I2S driver already starts it up as well; the i2s_adc_enable call re-starts it, but there may still be buffers in the queue that your read call reads.

I also don't understand why you set it like (16 * 1024) for the i2s_read_len. It definitely can't mean bits, since calloc even allocates per byte due to sizeof(char). With i2s_read_len=1024 and then subsequently calloc(1024, sizeof(uint16_t)); it would make sense to me.

If it is just bytes, it would mean the ADC is set to 8-bit mode, but EXAMPLE_I2S_SAMPLE_BITS is set to 16. What does that mean?

My guess is that the dev just wanted a buffer of 16K, thinking more about memory usage than sample size, and used 16*1024 as a more readable way to write it than 16384.

In the end, it is also completely unclear to me in which data format the i2s_read_buff would get filled, it seems to be only 8bit samples, which doesn't make sense if the ADC is set to 12bit (aligned to 16?). How is it supposed to work / how is the data aligned in the i2s_read_buff?


The buffer will be filled with 16-bit samples (consisting of the 12 ADC bits with the top 4 bits filled with zeroes). Note that the pairs of 16-bit words are flipped around in the buffer, so if I take your example lines, you would do something like this to print out the actual ADC values:

Code: Select all

uint16_t *i2s_read_samples=(uint16_t*)i2s_read_buff;
for (int i=0; i<bytes_read/sizeof(uint16_t); i++) printf(i2s_read_samples[i^1]);

bluesceada
Posts: 3
Joined: Fri Sep 28, 2018 6:55 am

Re: Faster ADC Reading

Postby bluesceada » Thu Oct 04, 2018 10:04 am

Thanks for some of the clarifications, but other things are still open, maybe someone else can help who knows more of the details?

ESP_Sprite wrote:
bluesceada wrote:For my understanding, the following code should sample as many samples as half of i2s_read_len (because .bits_per_sample is set to 16):
<snip>
But somehow it takes way too short to execute.

I'm not that intimately familiar with the I2S driver, but this may be because the setup of the I2S driver already starts it up as well; the i2s_adc_enable call re-starts it, but there may still be buffers in the queue that your read call reads.


Yeah I thought something similar, but being sure on it would really help. So, to rephrase the question: How can I synchronize at which point in time the ADC data starts filling up a buffer from buffer index 0?
Like, I would like to have something like a "start_adc_sampling();" call and "wait_for_adc_to_fill_buffer();". I guess there should be an interrupt that I can use as a buffer-full signal? And how can I start it? To my understanding it essentially starts already when it is setup with i2s_driver_install(..) ? Or at least after i2s_adc_enable(..)? Then I can control it with i2s_start(..) and i2s_stop(..)? However, after the first i2s_stop, it locks up for me...

My guess is that the dev just wanted a buffer of 16K, thinking more about memory usage than sample size, and used 16*1024 as a more readable way to write it than 16384.

That was my guess, too. So he allocates 16K, but actually just uses the first 2K bytes (1K samples)? Is that what you mean? Again, maybe someone else can clarify.

Thanks if anyone can help further clarify these points!

Who is online

Users browsing this forum: No registered users and 3 guests