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;
}
}