I2S_read missing bytes from external ADC

goldenDragon
Posts: 3
Joined: Tue Jan 29, 2019 3:48 pm

I2S_read missing bytes from external ADC

Postby goldenDragon » Mon Feb 25, 2019 8:30 pm

Hi all,

I am trying to interface the ESP32 DevkitC to an ADC (ADS127L01) that is currently running on another custom PCB board. I have the required signals of SCK (clock), WS (word select), and SD (data) for the I2S interface jumped over from the ADC to the esp32. The esp32 is a slave and is only reading from the I2S interface.

I have tried different sample rates of 31.25Khz, 125Khz, and 500Khz and with each sample rate there are sections of data that the ESP32 is missing. It will work properly for some of the time and then miss blocks of data, the higher the data sample rate the more missed samples. I know this because I have a logic analyzer hooked up on the I2S signals coming from the ADC as well.

For example, in the attached file is an image of the discrepancy. On the left is the data as seen from the logic analyzer and on the right is what the ESP32 receives. The ADC is sampling at 31.25Khz and as you can see it misses 4 samples (of 32-bits each) and it repeats this pattern of missing 3-4 samples in between chunks of good data all the way until I2S_read is finished.

Question 1:
I know that when the ESP32 is operating in slave mode with the I2S interface the master has to use the ESP32's I2Sn MCLK, the problem is my ADC requires a 16 MHz clock for proper operation and with any of the data sample rates I pick the MCLK is never calculated to be 16Mhz. The ADC runs off a clock generated from a 16 Mhz crystal on a separate PCB. Could out of phase clocks be the cause of these errors?

Question 2:
Whatever the problem is, I would like to be able to know when the ESP32 detects these errors on the I2S signal lines because it seems like it's able to skip some bytes of data and recover to receive data properly for awhile. Is there somewhere in the I2S driver code that I can see if the ESP32 detects bit clock or framing errors? That information would be very helpful in debugging this issue. Thanks for reading, I look forward to any suggestions anyone might have.

The code is attached below:

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "esp_system.h"
#include <math.h>
#include "driver/gpio.h"
#include "driver/ledc.h"


#define I2S_NUM         (0)

#define ADC_SCLK        14
#define ADC_FSYNC       27
#define ADC_DOUT        13
#define ADC_START       25
#define DEBUG_PIN       22
#define I2S_MCLK        18

#define ADC_START_SEL   (1ULL<<ADC_START)
#define DEBUG_PIN_SEL   (1ULL<<DEBUG_PIN)

#define NUM_OF_SAMPLES      5000
#define BYTES_PER_SAMPLE    4
#define SAMPLE_RATE         31250


typedef struct
{
    int8_t data[NUM_OF_SAMPLES*BYTES_PER_SAMPLE];
    uint32_t index;
    uint8_t volatile rdy_flag;
    uint32_t volatile count;
} ADC;

ADC adc;


void print_adc_data_i2s_diff(void)
{
    for(uint32_t i = 0; i < (adc.index - BYTES_PER_SAMPLE); i = i+BYTES_PER_SAMPLE)
    {
        printf("%02x", (uint8_t)adc.data[i+1]);
        printf("%02x", (uint8_t)adc.data[i]);
        printf("%02x", (uint8_t)adc.data[i+7]);
        printf("%02x\n", (uint8_t)adc.data[i+6]); // status byte

        if( (i % 1000) == 0)  vTaskDelay(10);
    }
        printf("******** End of data ********\n");
        adc.index = 0;
        adc.rdy_flag = 0;
}

void app_main()
{
    uint32_t br;
    esp_err_t ret;

    i2s_config_t i2s_config = {
        .mode = I2S_MODE_SLAVE | I2S_MODE_RX,                                  
        .sample_rate = SAMPLE_RATE,
        .bits_per_sample = 16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                         
        .communication_format = I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_I2S_MSB,
        .dma_buf_count = 4, 
        .dma_buf_len = 1024,
        .use_apll = true,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1                                //Interrupt level 1
    };
    i2s_pin_config_t pin_config = {
        .bck_io_num = ADC_SCLK,
        .ws_io_num = ADC_FSYNC,
        .data_out_num = -1,
        .data_in_num = ADC_DOUT                                             
    };

    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
    i2s_stop(I2S_NUM);
    i2s_set_pin(I2S_NUM, &pin_config);

    //i2s_set_clk(I2S_NUM,SAMPLE_RATE, BYTES_PER_SAMPLE*8, I2S_CHANNEL_STEREO);
    gpio_config_t io_conf;
    //initialize GPIO output for ADC_START
    memset(&io_conf,0,sizeof(io_conf));
    //disable interrupt
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set
    io_conf.pin_bit_mask = ADC_START_SEL;
    //enable pull-down mode
    io_conf.pull_down_en = 1;
    //configure GPIO with the given settings
    gpio_config(&io_conf);
    gpio_set_level(ADC_START,0);

    //initialize GPIO output for DEBUG_PIN
    memset(&io_conf,0,sizeof(io_conf));
    //disable interrupt
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set
    io_conf.pin_bit_mask = DEBUG_PIN_SEL;
    //enable pull-down mode
    io_conf.pull_down_en = 1;
    //configure GPIO with the given settings
    gpio_config(&io_conf);
    gpio_set_level(DEBUG_PIN,0);
    
    while (1) {
        gpio_set_level(ADC_START, 1);
        vTaskDelay(pdMS_TO_TICKS(10));

        i2s_start(I2S_NUM);
        gpio_set_level(DEBUG_PIN,1);
        ret=i2s_read(I2S_NUM, adc.data, NUM_OF_SAMPLES*BYTES_PER_SAMPLE, &br, portMAX_DELAY );
        gpio_set_level(DEBUG_PIN,0);
        configASSERT(ret==ESP_OK);
    
        printf("bytes read: %d\n", br);
        gpio_set_level(ADC_START,0);
        i2s_stop(I2S_NUM);

        adc.index = br;
        print_adc_data_i2s_diff();
        return 0;
    }

}

Who is online

Users browsing this forum: artisdom, Sang_Huynh and 228 guests