vindar / ILI9341_T4

Optimized ILI9341 screen driver library for Teensy 4/4.1, with vsync and diff. updates.
GNU Lesser General Public License v2.1
70 stars 12 forks source link

Problem with using audio library together #15

Closed windorey closed 12 months ago

windorey commented 1 year ago

Hi. I am using the LVGL music demo example, and I am using the Teensy Audio Library to play WAV in the background of the test. There is some sort of stuttering when updating the display, can it be fixed? Code:



#include <Audio.h>
#include <SdFat.h>
AudioPlaySdWav           playWav1;
// Use one of these 3 output types: Digital I2S, Digital S/PDIF, or Analog DAC
AudioOutputI2S           audioOutput;
//AudioOutputSPDIF       audioOutput;
//AudioOutputAnalog      audioOutput;
//On Teensy LC, use this for the Teensy Audio Shield:
//AudioOutputI2Sslave    audioOutput;

AudioConnection          patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection          patchCord2(playWav1, 1, audioOutput, 1);

#include <ILI9341_T4.h>  // the screen driver library

#include <lvgl.h>  // see the comment above for infos about installing and configuring LVGL.

#include "src/music/lv_demo_music.h"  // the src/music/ folder contain the source code for LVGL music demo.

//
// DEFAULT WIRING USING SPI 0 ON TEENSY 4/4.1
//
#define PIN_SCK 13   // mandatory
#define PIN_MISO 12  // mandatory
#define PIN_MOSI 11  // mandatory
#define PIN_DC 10    // mandatory, can be any pin but using pin 10 (or 36 or 37 on T4.1) provides greater performance

#define PIN_CS 9           // optional (but recommended), can be any pin.
#define PIN_RESET 6        // optional (but recommended), can be any pin.
#define PIN_BACKLIGHT 255  // optional, set this only if the screen LED pin is connected directly to the Teensy.
#define PIN_TOUCH_IRQ 255  // optional, set this only if the touchscreen is connected on the same SPI bus
#define PIN_TOUCH_CS 255   // optional, set this only if the touchscreen is connected on the same spi bus

// 40MHz SPI. Can do much better with short wires
#define SPI_SPEED 40000000

// screen size in landscape mode
#define LX 320
#define LY 240

// 2 diff buffers with about 8K memory each
ILI9341_T4::DiffBuffStatic<8000> diff1;
ILI9341_T4::DiffBuffStatic<8000> diff2;

// the internal framebuffer for the ILI9341_T4 driver (150KB)
// in DMAMEM to save space in the lower (faster) part of RAM.
DMAMEM uint16_t internal_fb[LX * LY];

// the screen driver object
ILI9341_T4::ILI9341Driver tft(PIN_CS, PIN_DC, PIN_SCK, PIN_MOSI, PIN_MISO, PIN_RESET, PIN_TOUCH_CS, PIN_TOUCH_IRQ);

// number of lines in lvgl's internal draw buffer
#define BUF_LY 40

DMAMEM lv_color_t lvgl_buf[LX * BUF_LY];  // memory for lvgl draw buffer, also in DMAMEM (25KB)

lv_disp_draw_buf_t draw_buf;  // lvgl 'draw buffer' object
lv_disp_drv_t disp_drv;       // lvgl 'display driver'
lv_indev_drv_t indev_drv;     // lvgl 'input device driver'
lv_disp_t* disp;              // pointer to lvgl display object

/** Callback to draw on the screen */
void my_disp_flush(lv_disp_drv_t* disp, const lv_area_t* area, lv_color_t* color_p) {
  const bool redraw_now = lv_disp_flush_is_last(disp);                                       // check if when should update the screen (or just buffer the changes).
  tft.updateRegion(redraw_now, (uint16_t*)color_p, area->x1, area->x2, area->y1, area->y2);  // update the interval framebuffer and then redraw the screen if requested
  lv_disp_flush_ready(disp);                                                                 // tell lvgl that we are done and that the lvgl draw buffer can be reused.
}

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

  AudioMemory(128);
  if (!(SD.begin(BUILTIN_SDCARD))) {
    // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  playWav1.play("test.wav");

  // ------------------------------
  // Init the ILI9341_T4 driver.
  // ------------------------------
  tft.output(&Serial);  // send debug info to serial port.
  while (!tft.begin(SPI_SPEED))
    ;                                  // init
  tft.setFramebuffer(internal_fb);     // set the internal framebuffer
  tft.setDiffBuffers(&diff1, &diff2);  // set the diff buffers
  tft.setRotation(1);                  // landscape mode 1 : 320x240
  tft.setDiffGap(4);                   // with have large 8K diff buffers so we can use a small gap.
  tft.setVSyncSpacing(1);              // lvgl is already controlling framerate: we just set this to 1 to minimize screen tearing.
  tft.setRefreshRate(100);             // 100Hz refresh, why not...
  tft.clear(0);                        // black screen to start.

  // ------------------------------
  // Init LVGL
  // ------------------------------
  lv_init();

  // initialize lvgl drawing buffer
  lv_disp_draw_buf_init(&draw_buf, lvgl_buf, nullptr, LX * BUF_LY);

  // Initialize lvgl display driver
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = LX;
  disp_drv.ver_res = LY;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  disp = lv_disp_drv_register(&disp_drv);
  disp->refr_timer->period = 15;  // set refresh rate around 66FPS.

  // ------------------------------
  // Run the demo
  // ------------------------------
  lv_demo_music();
}

void loop() {

  lv_task_handler();  // lvgl gui handler
}

/** end of file */

`
windorey commented 1 year ago

Adding video for extra information: https://youtu.be/brwrKG7tyhQ

vindar commented 1 year ago

I finally got an audio shield and I can indeed reproduce the bug.

The problem disappears when the display is moved to SPI1 which suggests a SPI conflict with the audio board on SPI0. I will try to investigate further...

vindar commented 1 year ago

Confirmed this is a bug in the Audio library, not this one. Reported to PJRC and it will be fixed in an upcoming release. Hopefully in Teensyduino 1.59 or 1.60.

Meanwhile, the simplest solution is to connect the display to SPI1 instead of SPI0. Alternatively, one can edit the file "Audio/spi_interupt.h" in the audio library and comment all lines with SPI.usingInterrupt(IRQ_SOFTWARE); but this may break things if the Audio library is used with the SD card on the Audio shield...