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

How to use SPI of esp32 to read ADC samples periodically with fixed sample rate? (IDFGH-101) #1148

Open
liubenyuan opened this issue Oct 21, 2017 · 1 comment

Comments

@liubenyuan
Copy link

liubenyuan commented Oct 21, 2017

Hi, I want to use two ADC (ADS8864) and esp32 to sample analog waveform and send them to PC using WIFI. The data are sampled in a fixed sample rate, for example 100ksps for ADS8864, and I want to use SPI to read them into a buffer. When the data are ready, for example, 256 samples have been collected. I memcpy them out and send them to PC.

I config a timer of 200kHz (5us), and at the interrupt of the timer, I toggle a GPIO (which is connected to the CONV port of ADC), and at every two timer interrupt, I send a semaphore to notice a task to read the ADC using the SPI peripheral of esp32.

spi_adc

However, 1. we found that the CONV signal is not stable, which is not precisely 100khz with sharp edges. the duty cycle is approximately 0.4 and the period is changing. 2. How could I read the samples out using a SPI DMA? so that SPI can quietly sample the data and put them in a list or a cyclic buffer. 3. Can I run SPI sample in another core and notify current core when samples are ready?

Is it right in this way (for esp32) to use SPI as a ADC sampler reader and periodically (100KHz) sample an analog waveform? If not, what is the right way to do so?

The corresponding codes are

#define TIMER_INTR_SEL TIMER_INTR_LEVEL  /*!< Timer level interrupt */
#define TIMER_GROUP    TIMER_GROUP_0     /*!< Test on timer group 0 */
#define TIMER_DIVIDER   16               /*!< Hardware timer clock divider, 80 to get 1MHz clock to timer */
#define TIMER_SCALE    (TIMER_BASE_CLK / TIMER_DIVIDER)  /*!< used to calculate counter value */
#define TIMER_FINE_ADJ   (1.4*(TIMER_BASE_CLK / TIMER_DIVIDER)/1000000) /*!< used to compensate alarm value */
#define TIMER_INTERVAL0_SEC   (0.000005)   /*!< test interval for timer 0 */

xSemaphoreHandle semaphore;
volatile int cnt = 0;
static spi_device_handle_t spi1,spi2;

void IRAM_ATTR timer_group0_isr(void *para)
{
    int timer_idx = (int) para;
    int led_val;
    uint32_t intr_status = TIMERG0.int_st_timers.val;
    if((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) {
        TIMERG0.hw_timer[timer_idx].update = 1;
        TIMERG0.int_clr_timers.t0 = 1;

        led_val = (cnt+1)%2;
        gpio_set_level(GPIO_CONV, led_val);
        cnt++;

        if (led_val==0){
            // if CONVST falling down, tell SPI to read!
            xSemaphoreGiveFromISR(semaphore, NULL);
        }

        TIMERG0.hw_timer[timer_idx].config.alarm_en = 1;
    }

}

// config and start timer
static void tg0_timer0_init()
{
    int timer_group = TIMER_GROUP_0;
    int timer_idx = TIMER_0;
    timer_config_t config;
    config.alarm_en = 1;
    config.auto_reload = 1;
    config.counter_dir = TIMER_COUNT_UP;
    config.divider = TIMER_DIVIDER;
    config.intr_type = TIMER_INTR_SEL;
    config.counter_en = TIMER_PAUSE;
    /*Configure timer*/
    timer_init(timer_group, timer_idx, &config);
    /*Stop timer counter*/
    timer_pause(timer_group, timer_idx);
    /*Load counter value */
    timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL);
    /*Set alarm value*/
    timer_set_alarm_value(timer_group, timer_idx, (TIMER_INTERVAL0_SEC * TIMER_SCALE) - TIMER_FINE_ADJ);
    /*Enable timer interrupt*/
    timer_enable_intr(timer_group, timer_idx);
    /*Set ISR handler*/
    timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL);
    /*Start timer counter*/
    timer_start(timer_group, timer_idx);
}

void timer0_task()
{
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length= 16;
    t.flags = SPI_TRANS_USE_RXDATA;

    while(1){
        xSemaphoreTake(semaphore, portMAX_DELAY);

        spi_device_transmit(spi1, &t);
        // sample ADC1

        spi_device_transmit(spi2, &t);
        // sample ADC2
    }
    //taskdelete
}

in main_task

    esp_err_t ret;
    spi_bus_config_t buscfg1={
        .miso_io_num=PIN_NUM_MISO1,
        .mosi_io_num=PIN_NUM_MOSI1,
        .sclk_io_num=PIN_NUM_CLK1,
        .quadwp_io_num=-1,
        .quadhd_io_num=-1
    };
    spi_device_interface_config_t devcfg1={
        .clock_speed_hz=16*1000*1000,   //Clock out at 16MHz
        .mode=3,                        //SPI mode 3
        .queue_size=1,                  //We want to be able to queue 1 transactions at a time
    };

    spi_bus_config_t buscfg2={
        .miso_io_num=PIN_NUM_MISO2,
        .mosi_io_num=PIN_NUM_MOSI2,
        .sclk_io_num=PIN_NUM_CLK2,
        .quadwp_io_num=-1,
        .quadhd_io_num=-1
    };
    spi_device_interface_config_t devcfg2={
        .clock_speed_hz=16*1000*1000,   //Clock out at 16MHz
        .mode=3,                        //SPI mode 3
        .queue_size=1,                  //We want to be able to queue 1 transactions at a time
    };

    ret=spi_bus_initialize(HSPI_HOST, &buscfg1, 1);
    ret=spi_bus_initialize(VSPI_HOST, &buscfg2, 2);
    //assert(ret==ESP_OK);

    ret=spi_bus_add_device(HSPI_HOST, &devcfg1, &spi1);
    ret=spi_bus_add_device(VSPI_HOST, &devcfg2, &spi2);
    //assert(ret==ESP_OK);
@FayeY FayeY changed the title How to use SPI of esp32 to read ADC samples periodically with fixed sample rate? [TW#16070] How to use SPI of esp32 to read ADC samples periodically with fixed sample rate? Oct 28, 2017
@projectgus projectgus changed the title [TW#16070] How to use SPI of esp32 to read ADC samples periodically with fixed sample rate? How to use SPI of esp32 to read ADC samples periodically with fixed sample rate? (IDFGH-101) Mar 12, 2019
@X-Ryl669
Copy link
Contributor

X-Ryl669 commented Sep 5, 2019

For what it's worth, you have a RMT or LEDC for precise GPIO control. You should not use a timer that requires work from the RTOS if you don't want any jitter.

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

No branches or pull requests

2 participants