Page 1 of 1

ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Mon Dec 14, 2020 7:47 am
by fgecekusu
As you may know, esp32s2 doesn't use i2s for adc with dma and in the github page, example uses i2s for dma example. There is a library (ADC Digital Controller)for esp32s2. I have read the documentations but i couldn't figure it out yet. Is there anybody have example application for esp32s2.

Re: ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Sun Jan 31, 2021 11:27 am
by messier31
Hi,

with help of the technical manual, the driver source code, and some guess work I was able to get it running. See function below (I use it together with Arduino framework).
I was not able to use funtion "spi_slave_hd_get_trans_res" to wait for the sampling to finish (always got a timeout), instead there is a crude "delay" call at the end that does this job. Other than that it seems to work fine.

Code: Select all

 
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
 
 
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "dig_adc.h"
#include "driver/spi_slave_hd.h"
#include "driver/spi_slave.h"
#include <Arduino.h>
 
//#define USE_ADC_CHANNEL     ADC1_CHANNEL_2 /*!< ADC1 channel 0 is GPIO36 (ESP32), GPIO1 (ESP32-S2) */

#define RCV_HOST    SPI3_HOST
#define DMA_CHAN    RCV_HOST

// some unused pin had to be defined for CS
#define GPIO_CS 0



//  sampling frequency = 40 MHz / (div_num + 1) / sample_interval.
//  use following attenuation values from the enum:
//    ADC_ATTEN_DB_0   = 0,  /*!<No input attenumation, ADC can measure up to approx. 800 mV. */
//    ADC_ATTEN_DB_2_5 = 1,  /*!<The input voltage of ADC will be attenuated, extending the range of measurement to up to approx. 1100 mV. */
//    ADC_ATTEN_DB_6   = 2,  /*!<The input voltage of ADC will be attenuated, extending the range of measurement to up to  approx. 1350 mV. */
//    ADC_ATTEN_DB_11 
//  use following ADC channels from the adc_channel_t enum:
//    ADC_CHANNEL_0    // GPIO1
//    ADC_CHANNEL_1    // GPIO2
//    ADC_CHANNEL_2    // GPIO3
//    ADC_CHANNEL_3    // GPIO4
//    ADC_CHANNEL_4    // GPIO5
//    ADC_CHANNEL_5    // GPIO6
//    ADC_CHANNEL_6    // GPIO7
//    ADC_CHANNEL_7    // GPIO8
//    ADC_CHANNEL_8    // GPIO9
 //   ADC_CHANNEL_9    // GPIO10
esp_err_t adc_sample(uint8_t clkdiv, uint16_t sample_interval, adc_channel_t adc_channel, 
                adc_atten_t att, uint16_t *sample_buf, uint16_t numsamples)
{

    //  controller_clk = (APLL or APB) / (div_num + div_a / div_b + 1).
    adc_digi_clk_t adc_clk { .use_apll = false,     //!<true: use APLL clock; false: use APB clock. 
                             .div_num = clkdiv,          //!<Division factor. Range: 0 ~ 255.
                                                    // Note: When a higher frequency clock is used (the division factor is less than 9),
                                                    // the ADC reading value will be slightly offset. 
                             .div_b = 1,            //!<Division factor. Range: 1 ~ 63. 
                             .div_a = 0};           //!<Division factor. Range: 0 ~ 63.

    adc_digi_pattern_table_t adc1_pattern;
                adc1_pattern.atten=att;   /*!< ADC sampling voltage attenuation configuration. Modification of attenuation affects the range of measurements.
                                         0: measurement range 0 - 800mV,
                                         1: measurement range 0 - 1100mV,
                                         2: measurement range 0 - 1350mV,
                                         3: measurement range 0 - 2600mV. */
                adc1_pattern.channel=adc_channel;   /*!< ADC channel index. */
    
    adc_digi_config_t adc_config = {.conv_limit_en = false,  /*!<Enable the function of limiting ADC conversion times.
                                                            If the number of ADC conversion trigger count is equal to the `limit_num`, the conversion is stopped. */
                                    .conv_limit_num = 0,     /*!<Set the upper limit of the number of ADC conversion triggers. Range: 1 ~ 255. */
                                    .adc1_pattern_len = 1,  /*!<Pattern table length for digital controller. Range: 0 ~ 16 (0: Don't change the pattern table setting).
                                                            The pattern table that defines the conversion rules for each SAR ADC. Each table has 16 items, in which channel selection,
                                                            resolution and attenuation are stored. When the conversion is started, the controller reads conversion rules from the
                                                            pattern table one by one. For each controller the scan sequence has at most 16 different rules before repeating itself. */
                                    .adc2_pattern_len = 0,  /*!<Refer to ``adc1_pattern_len`` */
                                    .adc1_pattern = &adc1_pattern,      /*!<Pointer to pattern table for digital controller. The table size defined by `adc1_pattern_len`. */
                                    .adc2_pattern = 0,      /*!<Refer to `adc1_pattern` */
                                    .conv_mode = ADC_CONV_SINGLE_UNIT_1, 
                                    .format = ADC_DIGI_FORMAT_12BIT,  
                                    .interval = sample_interval,  //!<The number of interval clock cycles for the digital controller to trigger the measurement.
                                                            // The unit is the divided clock. Range: 40 ~ 4095.
                                                            // Expression: `trigger_meas_freq` = `controller_clk` / 2 / interval. Refer to ``adc_digi_clk_t``.
                                                            // Note: The sampling rate of each channel is also related to the conversion mode (See ``adc_digi_convert_mode_t``) and pattern table settings. */
                                    .dig_clk = adc_clk,     //!<ADC digital controller clock divider settings. Refer to ``adc_digi_clk_t``.
                                                            // Note: The clocks of the DAC digital controller use the ADC digital controller clock divider. 
                                    .dma_eof_num = numsamples}; //!<DMA eof num of adc digital controller.
                                                                //If the number of measurements reaches `dma_eof_num`, then `dma_in_suc_eof` signal is generated in DMA.
                                                                //Note: The converted d}
 

    spi_bus_config_t spi_bus_cfg = { .mosi_io_num = -1,                ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.
                                     .miso_io_num = -1,                ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.
                                     .sclk_io_num = -1,                ///< GPIO pin for Spi CLocK signal, or -1 if not used.
                                     .quadwp_io_num = -1,              ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used.
                                     .quadhd_io_num = -1,
                                     .max_transfer_sz = numsamples*2};

    spi_slave_hd_callback_config_t cb_config;
    spi_slave_hd_slot_config_t spi_slot_cfg = { .spics_io_num=GPIO_CS,
                                                .flags=0,
                                                .mode=0,                                                
                                                .queue_size=1,
                                                .dma_chan =  DMA_CHAN,
                                                .cb_config=cb_config};


    esp_err_t err = spi_slave_hd_init(RCV_HOST, &spi_bus_cfg, &spi_slot_cfg);
    if(err) return err;

    err = adc_digi_init();
    if(err) return err;

    err = adc_digi_controller_config(&adc_config);
    if(err) return err;

    //err = adc_digi_start();
    //if(err) return err;

    Serial.println("Init Done"); 

    spi_slave_hd_data_t rx_trans {
        .data = (uint8_t*)sample_buf,       ///< Buffer to send, must be DMA capable
        .len = (size_t)(numsamples<<1),     ///< Len of data to send/receive. For receiving the buffer length should be multiples of 4 bytes, otherwise the extra part will be truncated.
        .trans_len = numsamples,            ///< Data actually received
        .arg = 0};                          ///< Extra argument indiciating this data


    err = spi_slave_hd_queue_trans(RCV_HOST, SPI_SLAVE_CHAN_RX, &rx_trans, 1000);
    if(err) return err;

    err = adc_digi_start();
    if(err) return err;

    else {
        // wait for data to be complete
        delay(((clkdiv+1)*sample_interval * numsamples) / 40000 + 1);

        Serial.println("Received Data:");
        for(int i=0; i < numsamples; ++i) {
            Serial.println(sample_buf[i]);
        }
    }

    spi_slave_hd_deinit(RCV_HOST);
    return err;

}



Re: ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Sun Jan 31, 2021 5:33 pm
by messier31
Maybe some early observation:
1) The LSB of the sampled data is never toggeling. It seems like 11bit and 12 bit conversion mode are essentially the same, while the 12 bit mode only adds a 0 at the end. Could someone from espressif maybe comment on that?
2) In a quick trial it seems like even 40 MHz sampling rate is still providing valid output. I did not see any invalid data, just the signal bandwidth seems to be limited to ~1 MHz. But still higher sampling rates seem usable although the datasheet defines the max at 2 MHz

Re: ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Fri Oct 15, 2021 9:52 pm
by Samit Hasan
Cant find dig_adc.h header file. Could you kindly provide the file here ?

Re: ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Mon Oct 18, 2021 10:31 am
by Konny_Germany
Hallo,
i also could not find the file "dig_adc.h".
So, can somebody give me any info where i can find this file?

Thanks from old Germany
Konny

Re: ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Tue May 17, 2022 8:28 pm
by TinkerPHU
Thank you for this perl of a code! That would have taken me ages to get that arranged.
I can confirm that it works well and very precisely with 100kHz sampling frequency! I did not measure the analog accuracy yet, I am testing a 1kHz 0/3.3V 10% duty cycle PWM signal and got the following very exact result:

Code: Select all

Received Data:
0 0 0 0 4095 4095 4095 4095 4095 4095 4095 4095 4095 4095 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4095 4095 4095 4095 4095 4095 
4095 4095 4095 4095 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I had to add one little but crucial change:

Initialize cb_config with 0 (c++). Without that, I got an immediate crash.

Code: Select all

spi_slave_hd_callback_config_t cb_config = {0};

As for the values: Mask them with 0x0fff. The upper bits are not 0 when read (haven't searched for the explanation yet):

Code: Select all

Serial.print(sample_buf[i]&0x0fff);
Will post my solution when I am satisfied with the code (I would like to add some more features).

btw: to answer the questions above: dig_adc.h contains just the prototype of the function.

Chears and once again, thanks @messier31

Re: ESP32S2 ADC with DMA(Adc Digital Controller)

Posted: Sat Sep 21, 2024 6:21 am
by Nibbelodean
The first bits code for the channel the data is coming from.