espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.76k stars 7.44k forks source link

ESP32-S3 simultanous use of I2S and I2C results in racing condition #7857

Open jpm1712 opened 1 year ago

jpm1712 commented 1 year ago

Board

ESP32-S3 DEV KIT or SUNTON 7" ESP32-S3 LCD (Makerfab)

Device Description

Problem is reproducable on both boards: ESP32-S3 DEV KIT + SUNTON 7" ESP32-S3 LCD (Makerfab). Both with external DAC MAX98573 I2S audio chip.

Hardware Configuration

No, both buses I2S (MAX98573) and I2C (GT911 LCD touch driver) are dedicated and only one device each.

Version

v2.0.6

IDE Name

PlatformIO

Operating System

Windows 11 + Linux

Flash frequency

80 MHZ

PSRAM enabled

yes

Upload speed

921600

Description

Its not possible to use both buses (I2S+I2C) together at the same time, which is defenitly useful for my SUNTON 7" ESP32-S3 device (Touch screen use + play audio sounds). Using only one of the buses in my sketch works fine. As soon as I initialize both buses, neither of them works anymore: Audio library seems to work (info output) however no sound is played. Touch screen does not work i.e. I2C outputs I2C errors. Initialising the buses in different sequence does not solve the problem. Only sequence audio works is first I2C and then I2S but then I2C does not work. I wonder weather where the problem seems to be: ESP HW bug, IDF or arduino library. Running the handlers/buses on different cores brings no solution, problem is the same.

Sketch

main.cpp:

/* This is a test file for showing simultaneously use of I2s and I2c racing condition.
External I2S DAC MAX98573 is used.
Program should run on a 7"LCD Makerfab ESP32-S3
https://wiki.makerfabs.com/Sunton_ESP32_S3_5_7_inch_IPS_with_Touch.html

You can reproduce problem with just an ESP32-S3 DEV kit and an DAC MAX98573,
because its only an audio (I2S) and I2C problem, LCD and SD SPI make no difference.

Audio Library: https://github.com/schreibfaul1/ESP32-audioI2S.git
*/

#include <Arduino.h>
#include <WiFi.h>
#include "audio.h"
#include "i2c_driver.h"

#define I2S_DOUT 17
#define I2S_BCLK 19
#define I2S_LRC 18

Audio audio;

// ------------------------------------------------
// global variables and defines used
// ------------------------------------------------
// Set your Wi-Fi Credentials here
const char *ssid = "xxxxxx";
const char *password = "xxxxxxxxxxxx";
uint last_millis = 0;

//----- Audio Function --------------------------------------------------
void audio_init()
{
  audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
  audio.setVolume(6); // 0...21
}

void audio_info(const char *info)
{
  Serial.print("info        ");
  Serial.println(info);
}
void audio_id3data(const char *info)
{ // id3 metadata
  Serial.print("id3data     ");
  Serial.println(info);
}
void audio_eof_mp3(const char *info)
{ // end of file
  Serial.print("eof_mp3     ");
  Serial.println(info);
}
void audio_showstation(const char *info)
{
  Serial.print("station     ");
  Serial.println(info);
}
void audio_showstreamtitle(const char *info)
{
  Serial.print("streamtitle ");
  Serial.println(info);
}
void audio_bitrate(const char *info)
{
  Serial.print("bitrate     ");
  Serial.println(info);
}
void audio_commercial(const char *info)
{ // duration in sec
  Serial.print("commercial  ");
  Serial.println(info);
}
void audio_icyurl(const char *info)
{ // homepage
  Serial.print("icyurl      ");
  Serial.println(info);
}
void audio_lasthost(const char *info)
{ // stream URL played
  Serial.print("lasthost    ");
  Serial.println(info);
}

void setup()
{
  Serial.begin(115200);

  // ------------------------------------------------
  // Wifi connect
  // ------------------------------------------------
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  vTaskDelay(pdMS_TO_TICKS(500));
  if (WiFi.waitForConnectResult() != WL_CONNECTED)
  {
    Serial.printf("STA: Failed!\n");
    WiFi.disconnect(false);
    vTaskDelay(pdMS_TO_TICKS(500));
    WiFi.begin(ssid, password);
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);

  i2c_driver_init();
  gt911_touch_init();

  audio_init();
  audio.connecttohost("http://vis.media-ice.musicradio.com/CapitalMP3");

  // ------------------------------------------------
  // log memory
  // ------------------------------------------------
  log_d("Total heap: %d", ESP.getHeapSize());
  log_d("Free heap: %d", ESP.getFreeHeap());
  log_d("Total PSRAM: %d", ESP.getPsramSize());
  log_d("Free PSRAM: %d", ESP.getFreePsram());

  last_millis = millis();
}

void loop()
{
  audio.loop();

  if (millis() - last_millis > 10000) // 10sec only for test, normally gt911 poll several times per second. If you make several polls per second, audio stops playing and I2C does not work at all
  {
    gt_911_handler();
    last_millis = millis();
  }
}

i2c_driver:

/* Here I make use of the esp_lcd_touch and esp_lcd_touch_gt911 in release version 090
I made my own try to use idf code for i2c and gt911 polling.

The I2S and I2C racing condition is the same problem as with use of arduino wire library and the gt911 arduino Library
https://github.com/TAMCTec/gt911-arduino.git
*/
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#define CONFIG_LCD_HRES 800
#define CONFIG_LCD_VRES 480
#include "esp_lcd_touch.h"
#include "esp_lcd_touch_gt911.h"

static const char *TAG = "i2c-library";

#define I2C_MASTER_SCL_IO 20        /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO 19        /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_FREQ_HZ 100000   /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define WRITE_BIT I2C_MASTER_WRITE  /*!< I2C master write */
#define READ_BIT I2C_MASTER_READ    /*!< I2C master read */
#define ACK_CHECK_EN 0x1            /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0           /*!< I2C master will not check ack from slave */

i2c_port_t i2c_master_port = 0;
esp_lcd_touch_handle_t tp;
uint16_t touch_x[1];
uint16_t touch_y[1];
uint16_t touch_strength[1];
uint8_t touch_cnt = 0;

/**
 * @brief i2c master initialization
 */
static esp_err_t i2c_master_init(void)
{
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        conf.master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    return i2c_param_config(i2c_master_port, &conf);
}

void i2c_driver_init()
{
    i2c_driver_install(i2c_master_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
    i2c_master_init();

    // scan I2C bus
    uint8_t address;
    printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\r\n");
    for (int i = 0; i < 128; i += 16)
    {
        printf("%02x: ", i);
        for (int j = 0; j < 16; j++)
        {
            fflush(stdout);
            address = i + j;
            i2c_cmd_handle_t cmd = i2c_cmd_link_create();
            i2c_master_start(cmd);
            i2c_master_write_byte(cmd, (address << 1) | WRITE_BIT, ACK_CHECK_EN);
            i2c_master_stop(cmd);
            esp_err_t ret = i2c_master_cmd_begin(i2c_master_port, cmd, 50 / portTICK_RATE_MS);
            i2c_cmd_link_delete(cmd);
            if (ret == ESP_OK)
            {
                printf("%02x ", address);
            }
            else if (ret == ESP_ERR_TIMEOUT)
            {
                printf("UU ");
            }
            else
            {
                printf("-- ");
            }
        }
        printf("\r\n");
        // end scan
    }
}

void gt911_touch_init()
{
    esp_lcd_touch_config_t tp_cfg = {
        .x_max = CONFIG_LCD_HRES,
        .y_max = CONFIG_LCD_VRES,
        .rst_gpio_num = GPIO_NUM_38,
        .int_gpio_num = GPIO_NUM_NC,
        .levels = {
            .reset = 1,
            .interrupt = 0,
        },
        .flags = {
            .swap_xy = 0,
            .mirror_x = 0,
            .mirror_y = 0,
        },
        .device = {.i2c = {
                       .port = i2c_master_port,
                   }}};

    esp_lcd_touch_new_i2c_gt911(&tp_cfg, &tp);
}

void gt_911_handler()
{
    esp_lcd_touch_read_data(tp);
    bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, touch_x, touch_y, touch_strength, &touch_cnt, 1);
    if (touchpad_pressed)
    {
        log_d("x: %d", touch_x[0]);
        log_d("y: %d", touch_y[0]);
    }
}

Debug Message

E (11312) GT911: touch_gt911_i2c_read(276): I2C write error!
E (11313) GT911: esp_lcd_touch_gt911_read_data(133): I2C read error!
E (22343) GT911: touch_gt911_i2c_read(276): I2C write error!
E (22343) GT911: esp_lcd_touch_gt911_read_data(133): I2C read error!

Other Steps to Reproduce

Problem seems not to be new? : Problems while running i2s and i2c at the same time. #4686 There are several issues I found describing comperable problems but they are all no solution for my problem. Either to old (2017,2018) or not the same mcu (ESP32 not ESP32-S3) etc.

I have checked existing issues, online documentation and the Troubleshooting Guide

me-no-dev commented 1 year ago

You have the same pin defined for both buses:

#define I2S_BCLK 19
#define I2C_MASTER_SDA_IO 19
jpm1712 commented 1 year ago

@me-no-dev: You're right! Hours of searching and I didn't see it! However I took over the pins from the board schematics of the Makerfab/SUNTON 7" ESP32-S3 and I thought the PCB design was error free. Well... I measured the PCB and correct, I2S_BLCK and I2C_MASTER_SDA_IO are on the same GPIO PIN 19. So this is a "warning" to everybody using this board! I will inform Makerfab services tomorrow of this fault. Luckily the ESP32-S3 wroom module has 3 spare GPIO pins (35-37) so you can correct that yourself. Thanks a lot to me-no-dev again!

jpm1712 commented 1 year ago

One more comment: GPIO 35-37 are used for OSPI PSRAM: In module variants that have embedded OSPI PSRAM, i.e., that embed ESP32-S16R8, pins IO35,IO36, and IO37 connect to the OSPI PSRAM and are not available for other uses, So only pin left is GPIO 0 ..... which is used by the boot button. This shouldn't be a problem while up and running.

SuGlider commented 1 year ago

@jpm1712 It seems that the issue was solved by Sunton as you suggested:

Description of changes from V1.0 to V1.1:

  1. The IO corresponding to the original V1.0 of BCLK that controls audio is IO19, and V1.1 changes the IO corresponding to BCLK to IO0.
  2. V1.1 solves the problem that some computers fail to burn.
dannybackx commented 1 year ago

I am not sure which version of the board I have, how can you see ? Also I did a OTA test today and saw the LCD flicker while doing that, is that an effect of the same ? I don't see SPI or the flash drawn in the schematics I have, maybe I'm looking at it wrong.

eduardoludgero commented 1 year ago

I am not sure which version of the board I have, how can you see ? Also I did a OTA test today and saw the LCD flicker while doing that, is that an effect of the same ? I don't see SPI or the flash drawn in the schematics I have, maybe I'm looking at it wrong.

You can see the version on the PCB itself, next to the SD card connector. My version is v1.3.