Experiments trying to speed up SPI on Q and Zephyr

Another way to potentially speed up the SPI transfers is to use one of the async
versions of the spi_transceive functions like: spi_transceive_cb

For example I added into the ST77xx library a function:

static void spi_fillrect_callback (const struct device *dev, int result, void *data) {
  UNUSED(dev);
  UNUSED(result);
  *((volatile bool *)data) = true;
}

void ST77XX_zephyr::fillRect_async(int16_t x, int16_t y, int16_t w, int16_t h,
                           uint16_t color) {
  beginSPITransaction();
  setAddr(x, y, x + w - 1, y + h - 1);
  writecommand_cont(ST77XX_RAMWR);
  setDataMode();
  volatile bool async_cb_called = false;

  uint32_t count_pixels = w * h;
  uint16_t array_fill_count = min(count_pixels, sizeof(s_row_buff)/sizeof(s_row_buff[0]));
  struct spi_buf tx_buf = { .buf = (void*)s_row_buff, .len = (size_t)(array_fill_count * 2 )};
  const struct spi_buf_set tx_buf_set = { .buffers = &tx_buf, .count = 1 };

  for (uint16_t i = 0; i < array_fill_count; i++) s_row_buff[i] = color;
  while (count_pixels) {
    async_cb_called = false;
    spi_transceive_cb(_spi_dev, &_config16, &tx_buf_set, nullptr, &spi_fillrect_callback, (void*)&async_cb_called);
    while (!async_cb_called) {}

    count_pixels -= array_fill_count;
    if (count_pixels < array_fill_count) {
      array_fill_count = count_pixels;
      tx_buf.len = (size_t)(array_fill_count * 2 );
    }
  }

  endSPITransaction();
}

And hacked it into the test app:


  Serial.print("fillRect_async: ");
  delay(250);
  em = 0;
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_BLACK);
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_BLUE);
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_GREEN);
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_RED);
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_YELLOW);
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_WHITE);
  tft.fillRect_async(0, 0, tft.width(), tft.height(), ST77XX_ORANGE);
  dt = em;
  Serial.print(dt);
  Serial.print(" ");
  em = 0;
  tft.writeRect_SPI_transfer((tft.width() - IMAGE_WIDTH) / 2, (tft.height() - IMAGE_HEIGHT) / 2, IMAGE_WIDTH, IMAGE_HEIGHT, CopperAnnie);
  dt = em;
  Serial.println(dt);

So the code is more or less identical to the tft.fillRect() code except I use the
different call with callback where I simply set a flag ...

Test run timings:

TFT Library: 4195 66
fillRect_async: 3669 335
fillRect_SPI_transfer: 18770 336
fillRect_SPI_transfer16: 10245 184
fillRect_SPI_transfer_buf: 5648 102
fillRect_SPI_transfer_txbuf: 5603 32

Note the Aync is using the slow SPI transfer version for the writeRect.
But the fillRects is faster by about 13%

Which you can see in the SPI clock timing:

versus

Probably can speed up the fill by using multiple copies of the tx_buf
per call..

Again trying things out

1 Like