Stutter while using SPI

Windorey

Active member
Hello, I have a project where I have a TFT display and I'm using a display library that uses DMA.
The display library is very fast, clocking in about 500 FPS, but for some reason if I play a wav (using a high-quality SD card in the builtin slot), I get a very high amount of stuttering while the display updates.
Any reason?

My code:
Code:
#include "ILI9341_T4.h"
#define PIN_SCK 13
#define PIN_MISO 12
#define PIN_MOSI 11
#define PIN_DC 10
#define PIN_CS 9
#define PIN_RESET 8
#define PIN_BACKLIGHT 255
#define PIN_TOUCH_CS 7
#define PIN_TOUCH_IRQ 6
#define SPI_SPEED 115000000 /* SPI Speed */
#define LX 320
#define LY 240
ILI9341_T4::DiffBuffStatic<8192> diff1;
ILI9341_T4::DiffBuffStatic<8192> diff2;
DMAMEM uint16_t internal_fb[LX * LY];
ILI9341_T4::ILI9341Driver tft(PIN_CS, PIN_DC, PIN_SCK, PIN_MOSI, PIN_MISO, PIN_RESET, PIN_TOUCH_CS, PIN_TOUCH_IRQ);
#define BUF_LY 40

#include "lvgl.h"
#include "ui.h"
lv_color_t lvgl_buf[LX * BUF_LY];  // memory for lvgl draw buffer (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 touch_indev_drv;    // lvgl 'input device driver'
lv_indev_drv_t enc_indev_drv;
lv_disp_t* disp;  // pointer to lvgl display object
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 immediately
}
void touch_read(lv_indev_drv_t* indev_driver, lv_indev_data_t* data) {
  int touchX, touchY, touchZ;
  bool touched = tft.readTouch(touchX, touchY, touchZ);  // read the touchpad
  if (!touched) {                                        // nothing
    data->state = LV_INDEV_STATE_REL;
  } else {  // pressed
    data->state = LV_INDEV_STATE_PR;
    data->point.x = touchX;
    data->point.y = touchY;
  }
}

#define PIN_BUZZER 27

#define PIN_ENCODE_A 25
#define PIN_ENCODE_B 24
#define PIN_ENCODE_BTN 26
long enc_current_position = 1;
long enc_last_position = 0;
#include <RotaryEncoder.h>  // https://github.com/mathertel/RotaryEncoder
RotaryEncoder encoder(PIN_ENCODE_A, PIN_ENCODE_B, RotaryEncoder::LatchMode::TWO03);

void enc_read(lv_indev_drv_t* drv, lv_indev_data_t* data) {
  enc_current_position = encoder.getPosition();
  data->enc_diff = enc_current_position - enc_last_position;
  if (enc_current_position != enc_last_position) {
    tone(PIN_BUZZER, 3200, 2);
  }
  enc_last_position = enc_current_position;
  data->state = digitalRead(PIN_ENCODE_BTN) ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR;
}

void enc_update() {
  encoder.tick();
}

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioPlaySdWav playSdWav1;  //xy=514,381
AudioOutputI2S2 i2s2_1;     //xy=683,378
AudioConnection patchCord1(playSdWav1, 0, i2s2_1, 0);
AudioConnection patchCord2(playSdWav1, 1, i2s2_1, 1);



void show_err(const char* err, const char* details) {
  lv_obj_t* error_label = lv_label_create(lv_scr_act());

  lv_label_set_long_mode(error_label, LV_LABEL_LONG_WRAP); /*Break the long lines*/
  lv_label_set_recolor(error_label, true);                 /*Enable re-coloring by commands in the text*/
  lv_label_set_text_fmt(error_label, "#d63227 ERROR:# #bababa %s #\n\n"
                                     "#0088ff Details:# %s",
                        err, details);
  lv_obj_set_width(error_label, 280);
  lv_obj_set_style_text_align(error_label, LV_TEXT_ALIGN_CENTER, 0);
  lv_obj_align(error_label, LV_ALIGN_CENTER, 0, 0);
  lv_task_handler();
  lv_task_handler();
  lv_task_handler();
  lv_task_handler();
  lv_task_handler();
  while (1) { /* Halt the system */
    tone(PIN_BUZZER, 300, 200);
    delay(300);
    tone(PIN_BUZZER, 500, 200);
    delay(300);
    tone(PIN_BUZZER, 680, 200);
    delay(3000);
  }
}

void err_tone() {
  tone(PIN_BUZZER, 300, 200);
  delay(300);
  tone(PIN_BUZZER, 500, 200);
  delay(300);
  tone(PIN_BUZZER, 680, 200);
  delay(3000);
}

void setup() {

  tone(PIN_BUZZER, 1800, 200);
  pinMode(PIN_ENCODE_BTN, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(PIN_ENCODE_A), enc_update, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN_ENCODE_B), enc_update, CHANGE);

  Serial.begin(9600);

  AudioMemory(128);

  tft.output(&Serial);                       // send debug info to serial port.
  while (!tft.begin(SPI_SPEED)) err_tone();  // init
  tft.setFramebuffer(internal_fb);           // set the internal framebuffer
  tft.setDiffBuffers(&diff1, &diff2);        // set the diff buffers
  tft.setRotation(1);                        // portrait mode 0 : 240x320
  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...
  int tft_cal_data[] = { 369, 3672, 3852, 465 };
  tft.setTouchCalibration(tft_cal_data);

  lv_init();

  lv_disp_draw_buf_init(&draw_buf, lvgl_buf, nullptr, LX * BUF_LY);

  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 = 10;


  lv_indev_drv_init(&touch_indev_drv);
  touch_indev_drv.type = LV_INDEV_TYPE_POINTER;
  touch_indev_drv.read_cb = touch_read;
  lv_indev_drv_register(&touch_indev_drv);

  /* Register encoder indev driver */
  lv_indev_drv_init(&enc_indev_drv);
  enc_indev_drv.type = LV_INDEV_TYPE_ENCODER;
  enc_indev_drv.read_cb = enc_read;
  lv_indev_drv_register(&enc_indev_drv);

  lv_disp_t* dispp = lv_disp_get_default();
  lv_theme_t* theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_LIGHT_BLUE),
                                            true, LV_FONT_DEFAULT);
  lv_disp_set_theme(dispp, theme);

  if (!(SD.begin(BUILTIN_SDCARD))) {
    show_err("Unable to begin the SD card", "Unable to begin the SD card, please check the connections and if the card is inserted.");
  }

  ui_init();
  tone(PIN_BUZZER, 900, 200);
  playSdWav1.play("SONG.WAV");
}



void loop() {
  lv_task_handler();  // lvgl gui handler
  delay(20);
}
 
Back
Top