Esp32-s3 + spi tft дисплей, низкая скорость обновления экрана

Форум, приветствую! Снова я не могу без вашей помощи.

Плата esp32-s3-nano, дисплей такой, если правильно понял, он на чипе ST7796

Код рабочий, но время обновления экрана целиком секунды три примерно, что ну никак меня не устраивает.

#include <SPI.h>

#define CS    8        
#define RS    10       
#define RESET 9

#define SPI_FREQUENCY 80000000
#define SPI_MODE SPI_MODE0

void Lcd_Writ_Bus(uint8_t d) {
	SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE));
  SPI.transfer(d);
	SPI.endTransaction();
}

void Lcd_Write_Com(uint8_t VH) {
  digitalWrite(RS, LOW);
  Lcd_Writ_Bus(VH);
}

void Lcd_Write_Data(uint8_t VH) {
  digitalWrite(RS, HIGH);
  Lcd_Writ_Bus(VH);
}

void Lcd_Write_Com_Data(uint8_t com, uint8_t dat) {
  Lcd_Write_Com(com);
  Lcd_Write_Data(dat);
}

void Address_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
  Lcd_Write_Com(0x2a);
	Lcd_Write_Data(x1>>8);
	Lcd_Write_Data(x1);
	Lcd_Write_Data(x2>>8);
	Lcd_Write_Data(x2);
  Lcd_Write_Com(0x2b);
	Lcd_Write_Data(y1>>8);
	Lcd_Write_Data(y1);
	Lcd_Write_Data(y2>>8);
	Lcd_Write_Data(y2);
	Lcd_Write_Com(0x2c); 							 
}

void Lcd_Init() {

  digitalWrite(RESET, HIGH);
  delay(5); 
  digitalWrite(RESET, LOW);
  delay(15);
  digitalWrite(RESET, HIGH);
  delay(15);

  digitalWrite(CS, LOW);

  Lcd_Write_Com(0x11);     

	delay(120);

	Lcd_Write_Com(0x36);
	Lcd_Write_Data(0x48);   

	Lcd_Write_Com(0x3A);     
	Lcd_Write_Data(0x55);   

	Lcd_Write_Com(0xF0);
	Lcd_Write_Data(0xC3);   

	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x96);   

	Lcd_Write_Com(0xB4);     
	Lcd_Write_Data(0x02);   

	Lcd_Write_Com(0xB7);     
	Lcd_Write_Data(0xC6);   

	Lcd_Write_Com(0xC0);     
	Lcd_Write_Data(0xC0);   
	Lcd_Write_Data(0x00);   

	Lcd_Write_Com(0xC1);     
	Lcd_Write_Data(0x13);   

	Lcd_Write_Com(0xC2);     
	Lcd_Write_Data(0xA7);   

	Lcd_Write_Com(0xC5);     
	Lcd_Write_Data(0x21);   

	Lcd_Write_Com(0xE8);     
	Lcd_Write_Data(0x40);   
	Lcd_Write_Data(0x8A);   
	Lcd_Write_Data(0x1B);   
	Lcd_Write_Data(0x1B);   
	Lcd_Write_Data(0x23);   
	Lcd_Write_Data(0x0A);   
	Lcd_Write_Data(0xAC);   
	Lcd_Write_Data(0x33);   

	Lcd_Write_Com(0xE0);     
	Lcd_Write_Data(0xD2);   
	Lcd_Write_Data(0x05);   
	Lcd_Write_Data(0x08);   
	Lcd_Write_Data(0x06);   
	Lcd_Write_Data(0x05);   
	Lcd_Write_Data(0x02);   
	Lcd_Write_Data(0x2A);   
	Lcd_Write_Data(0x44);   
	Lcd_Write_Data(0x46);   
	Lcd_Write_Data(0x39);   
	Lcd_Write_Data(0x15);   
	Lcd_Write_Data(0x15);   
	Lcd_Write_Data(0x2D);   
	Lcd_Write_Data(0x32);   

	Lcd_Write_Com(0xE1);     
	Lcd_Write_Data(0x96);   
	Lcd_Write_Data(0x08);   
	Lcd_Write_Data(0x0C);   
	Lcd_Write_Data(0x09);   
	Lcd_Write_Data(0x09);   
	Lcd_Write_Data(0x25);   
	Lcd_Write_Data(0x2E);   
	Lcd_Write_Data(0x43);   
	Lcd_Write_Data(0x42);   
	Lcd_Write_Data(0x35);   
	Lcd_Write_Data(0x11);   
	Lcd_Write_Data(0x11);   
	Lcd_Write_Data(0x28);   
	Lcd_Write_Data(0x2E);   

	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x3C);   
	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x69);  

	delay(120);                
	Lcd_Write_Com(0x21);     
	Lcd_Write_Com(0x29);
  digitalWrite(CS, HIGH);
}

void setup() {

  pinMode(CS, OUTPUT);
  pinMode(RS, OUTPUT);
  pinMode(RESET, OUTPUT);

  digitalWrite(CS, HIGH);
  digitalWrite(RS, HIGH);
  digitalWrite(RESET, HIGH);

  SPI.begin();
  //SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setBitOrder(MSBFIRST);
	SPI.setFrequency(SPI_FREQUENCY);
  SPI.setDataMode(SPI_MODE0);
  Lcd_Init();
  
}

void loop() {

  digitalWrite(CS, LOW);
  Address_set(0,0,319,479);
	uint32_t ms = millis();

  for (uint16_t x = 0; x < 480; x++) {

			uint16_t i = (x * 63 * 6 / 480 + ms) % (63 * 6);
      uint8_t r = 0;
      uint8_t g = 0;
      uint8_t b = 0;

			if (i < 63) {
        r = 63;
        g = i;
			} else if (i < 63 * 2) {
				r = 63 * 2 - i;
        g = 63;
			} else if (i < 63 * 3) {
        g = 63;
        b = i - 63 * 2;
			} else if (i < 63 * 4) {
        g = 63 * 4 - i;
        b = 63;
			} else if (i < 63 * 5) {
				r = i - 63 * 4;
        b = 63;
			} else {
        r = 63;
        b = 63 * 6 - i;
			}

    for (uint16_t y = 0; y < 320; y++) {

      uint8_t r2 = (uint16_t)r * y / 319;
      uint8_t g2 = (uint16_t)g * y / 319;
      uint8_t b2 = (uint16_t)b * y / 319;

      uint16_t color = (r2 >> 1) << 11 | g2 << 5 | (b2 >> 1);

      Lcd_Write_Data(color >> 8);
      Lcd_Write_Data(color);
    }
  }

  digitalWrite(CS, HIGH);

}

у меня подозрение, что основная потеря в скорости из-за использования SPI.h, который наверняка идет из пакета для arduino esp32-s3-nano от разработчиков, и не хочет взаимодействовать со spi на хорошей скорости. Есть мысли?

1 лайк

Пока все смотрят Парад Победы, я посмею предложить самостоятельно попробовать без строк со сложными вычислениями 198-200. Вобщем просто заливку статичным цветом. Тогда точно будет понятно где “тормоза”. В ESP я никакой.

ну навтыкай везде чтение счетчика циклов CPU, посмотри, какая функция съедает все время, а там будет видно, что дальше делать.

А что, работает SPI у тебя на 80МГц?

Сделал так:

#include <SPI.h>

#define CS    8 // GPIO 17
#define RS    10 // GPIO 21    
#define RESET 9

#define SPI_FREQUENCY 80000000
#define SPI_MODE SPI_MODE0

#define CS_H() GPIO.out_w1ts |= 0x20000
#define CS_L() GPIO.out_w1tc |= 0x20000
#define RS_H() GPIO.out_w1ts |= 0x200000
#define RS_L() GPIO.out_w1tc |= 0x200000

void Lcd_Write_Com(uint8_t VH) {
  RS_L();
	SPI.transfer(VH);
}

void Lcd_Write_Data(uint8_t VH) {
  RS_H();
	SPI.transfer(VH);
}

void Address_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
  Lcd_Write_Com(0x2a);
	Lcd_Write_Data(x1>>8);
	Lcd_Write_Data(x1);
	Lcd_Write_Data(x2>>8);
	Lcd_Write_Data(x2);
  Lcd_Write_Com(0x2b);
	Lcd_Write_Data(y1>>8);
	Lcd_Write_Data(y1);
	Lcd_Write_Data(y2>>8);
	Lcd_Write_Data(y2);
	Lcd_Write_Com(0x2c); 							 
}

void Lcd_Init() {

  digitalWrite(RESET, HIGH);
  delay(5); 
  digitalWrite(RESET, LOW);
  delay(15);
  digitalWrite(RESET, HIGH);
  delay(15);

  digitalWrite(CS, LOW);

  Lcd_Write_Com(0x11);     

	delay(120);

	Lcd_Write_Com(0x36);
	Lcd_Write_Data(0x48);   

	Lcd_Write_Com(0x3A);     
	Lcd_Write_Data(0x55);   

	Lcd_Write_Com(0xF0);
	Lcd_Write_Data(0xC3);   

	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x96);   

	Lcd_Write_Com(0xB4);     
	Lcd_Write_Data(0x02);   

	Lcd_Write_Com(0xB7);     
	Lcd_Write_Data(0xC6);   

	Lcd_Write_Com(0xC0);     
	Lcd_Write_Data(0xC0);   
	Lcd_Write_Data(0x00);   

	Lcd_Write_Com(0xC1);     
	Lcd_Write_Data(0x13);   

	Lcd_Write_Com(0xC2);     
	Lcd_Write_Data(0xA7);   

	Lcd_Write_Com(0xC5);     
	Lcd_Write_Data(0x21);   

	Lcd_Write_Com(0xE8);     
	Lcd_Write_Data(0x40);   
	Lcd_Write_Data(0x8A);   
	Lcd_Write_Data(0x1B);   
	Lcd_Write_Data(0x1B);   
	Lcd_Write_Data(0x23);   
	Lcd_Write_Data(0x0A);   
	Lcd_Write_Data(0xAC);   
	Lcd_Write_Data(0x33);   

	Lcd_Write_Com(0xE0);     
	Lcd_Write_Data(0xD2);   
	Lcd_Write_Data(0x05);   
	Lcd_Write_Data(0x08);   
	Lcd_Write_Data(0x06);   
	Lcd_Write_Data(0x05);   
	Lcd_Write_Data(0x02);   
	Lcd_Write_Data(0x2A);   
	Lcd_Write_Data(0x44);   
	Lcd_Write_Data(0x46);   
	Lcd_Write_Data(0x39);   
	Lcd_Write_Data(0x15);   
	Lcd_Write_Data(0x15);   
	Lcd_Write_Data(0x2D);   
	Lcd_Write_Data(0x32);   

	Lcd_Write_Com(0xE1);     
	Lcd_Write_Data(0x96);   
	Lcd_Write_Data(0x08);   
	Lcd_Write_Data(0x0C);   
	Lcd_Write_Data(0x09);   
	Lcd_Write_Data(0x09);   
	Lcd_Write_Data(0x25);   
	Lcd_Write_Data(0x2E);   
	Lcd_Write_Data(0x43);   
	Lcd_Write_Data(0x42);   
	Lcd_Write_Data(0x35);   
	Lcd_Write_Data(0x11);   
	Lcd_Write_Data(0x11);   
	Lcd_Write_Data(0x28);   
	Lcd_Write_Data(0x2E);   

	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x3C);   
	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x69);  

	delay(120);                
	Lcd_Write_Com(0x21);     
	Lcd_Write_Com(0x29);
  digitalWrite(CS, HIGH);
}

void setup() {

  pinMode(CS, OUTPUT);
  pinMode(RS, OUTPUT);
  pinMode(RESET, OUTPUT);
  CS_H();
  RS_H();
  digitalWrite(RESET, HIGH);

  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  //SPI.setClockDivider(SPI_CLOCK_DIV2);
	SPI.setFrequency(SPI_FREQUENCY);
  Lcd_Init();
  
}

void loop() {

	uint32_t ms = micros();

  uint16_t buff[480];

	for (uint16_t x = 0; x < 480; x++) {

			uint16_t i = (x * 63 * 6 / 480 + ms) % (63 * 6);
      uint8_t r = 0;
      uint8_t g = 0;
      uint8_t b = 0;

			if (i < 63) {
        r = 63;
        g = i;
			} else if (i < 63 * 2) {
				r = 63 * 2 - i;
        g = 63;
			} else if (i < 63 * 3) {
        g = 63;
        b = i - 63 * 2;
			} else if (i < 63 * 4) {
        g = 63 * 4 - i;
        b = 63;
			} else if (i < 63 * 5) {
				r = i - 63 * 4;
        b = 63;
			} else {
        r = 63;
        b = 63 * 6 - i;
			}

      buff[x] = (r >> 1) << 11 | g << 5 | (b >> 1);

  }

  CS_L();
  Address_set(0,0,319,479);
	RS_H();
  for (uint16_t x = 0; x < 480; x++)
	  for (uint16_t y = 0; y < 320; y++) {
	    SPI.transfer(buff[x] >> 8);
	    SPI.transfer(buff[x]);
	  }
  CS_H();

}

переписал порты на регистры, но это не особо важно, вывод экрана сделал в отдельном цикле через буфер. Теперь вывод чуть менее 2 сек, но ощущается все равно очень долго, относительно большую область экрана перерисовать будет жесть как медленно ((

яхз как проверить, но изменение частоты, ровно как и изменение делителя, на скорость вывода влияют ожидаемо (в кол-ве раз)

По перерисовке в целом понятно, либо разбираться как скролл в ОЗУ экрана реализуется, и дорисовывать только дельты снизу/сверху. Либо отказаться от плавного скролла в пользу ступенчатого, и в целом интерфейс продумывать, чтобы был минимум изменяющихся областей на экране.

Либо разбираться, можно ли эмулировать очень быстрый spi для этого чиппа экрана, если он вообще такое умеет. Так-то, да, данных передается 307200 байт на 1 полный экран. Я пока не знаю что конкретно означает частота SPI (бит в секунду или что?), и насколько полученный сейчас результат соответствует должному.

Хотя всё равно слабо верится что отрисовка экрана за 1.7 секунды это его потолок, чушь какая-то…

Проблема в spi, создал через SPIClass и скорость обновления выросла до примерно 0,4 сек на экран, что уже лучше, хотя ощущение что оно должно уметь быстрее (или так и должно быть?)

#include <SPI.h>

#define CS    8 // GPIO 17
#define RS    10 // GPIO 21    
#define RESET 9

#define SPI_FREQUENCY 80000000
#define SPI_MODE SPI_MODE0

#define CS_H() GPIO.out_w1ts |= 0x20000
#define CS_L() GPIO.out_w1tc |= 0x20000
#define RS_H() GPIO.out_w1ts |= 0x200000
#define RS_L() GPIO.out_w1tc |= 0x200000

SPIClass spi(HSPI);

void Lcd_Write_Com(uint8_t VH) {
  spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
  RS_L();
	spi.transfer(VH);
  spi.endTransaction();
}

void Lcd_Write_Data(uint8_t VH) {
  spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
  RS_H();
	spi.transfer(VH);
  spi.endTransaction();
}

void Address_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
  Lcd_Write_Com(0x2a);
	Lcd_Write_Data(x1>>8);
	Lcd_Write_Data(x1);
	Lcd_Write_Data(x2>>8);
	Lcd_Write_Data(x2);
  Lcd_Write_Com(0x2b);
	Lcd_Write_Data(y1>>8);
	Lcd_Write_Data(y1);
	Lcd_Write_Data(y2>>8);
	Lcd_Write_Data(y2);
	Lcd_Write_Com(0x2c); 							 
}

void Lcd_Init() {

  digitalWrite(RESET, HIGH);
  delay(5); 
  digitalWrite(RESET, LOW);
  delay(15);
  digitalWrite(RESET, HIGH);
  delay(15);

  digitalWrite(CS, LOW);

  Lcd_Write_Com(0x11);     

	delay(120);

	Lcd_Write_Com(0x36);
	Lcd_Write_Data(0x48);   

	Lcd_Write_Com(0x3A);     
	Lcd_Write_Data(0x55);   

	Lcd_Write_Com(0xF0);
	Lcd_Write_Data(0xC3);   

	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x96);   

	Lcd_Write_Com(0xB4);     
	Lcd_Write_Data(0x02);   

	Lcd_Write_Com(0xB7);     
	Lcd_Write_Data(0xC6);   

	Lcd_Write_Com(0xC0);     
	Lcd_Write_Data(0xC0);   
	Lcd_Write_Data(0x00);   

	Lcd_Write_Com(0xC1);     
	Lcd_Write_Data(0x13);   

	Lcd_Write_Com(0xC2);     
	Lcd_Write_Data(0xA7);   

	Lcd_Write_Com(0xC5);     
	Lcd_Write_Data(0x21);   

	Lcd_Write_Com(0xE8);     
	Lcd_Write_Data(0x40);   
	Lcd_Write_Data(0x8A);   
	Lcd_Write_Data(0x1B);   
	Lcd_Write_Data(0x1B);   
	Lcd_Write_Data(0x23);   
	Lcd_Write_Data(0x0A);   
	Lcd_Write_Data(0xAC);   
	Lcd_Write_Data(0x33);   

	Lcd_Write_Com(0xE0);     
	Lcd_Write_Data(0xD2);   
	Lcd_Write_Data(0x05);   
	Lcd_Write_Data(0x08);   
	Lcd_Write_Data(0x06);   
	Lcd_Write_Data(0x05);   
	Lcd_Write_Data(0x02);   
	Lcd_Write_Data(0x2A);   
	Lcd_Write_Data(0x44);   
	Lcd_Write_Data(0x46);   
	Lcd_Write_Data(0x39);   
	Lcd_Write_Data(0x15);   
	Lcd_Write_Data(0x15);   
	Lcd_Write_Data(0x2D);   
	Lcd_Write_Data(0x32);   

	Lcd_Write_Com(0xE1);     
	Lcd_Write_Data(0x96);   
	Lcd_Write_Data(0x08);   
	Lcd_Write_Data(0x0C);   
	Lcd_Write_Data(0x09);   
	Lcd_Write_Data(0x09);   
	Lcd_Write_Data(0x25);   
	Lcd_Write_Data(0x2E);   
	Lcd_Write_Data(0x43);   
	Lcd_Write_Data(0x42);   
	Lcd_Write_Data(0x35);   
	Lcd_Write_Data(0x11);   
	Lcd_Write_Data(0x11);   
	Lcd_Write_Data(0x28);   
	Lcd_Write_Data(0x2E);   

	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x3C);   
	Lcd_Write_Com(0xF0);     
	Lcd_Write_Data(0x69);  

	delay(120);                
	Lcd_Write_Com(0x21);     
	Lcd_Write_Com(0x29);
  digitalWrite(CS, HIGH);
}

void setup() {

  pinMode(CS, OUTPUT);
  pinMode(RS, OUTPUT);
  pinMode(RESET, OUTPUT);
  CS_H();
  RS_H();
  digitalWrite(RESET, HIGH);

  /*SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  //SPI.setClockDivider(SPI_CLOCK_DIV2);
	SPI.setFrequency(SPI_FREQUENCY);*/

  spi.begin(13, 12, 11, 8);

  Lcd_Init();
  
}

void loop() {

	uint32_t ms = micros();

  uint16_t buff[480];

	for (uint16_t x = 0; x < 480; x++) {

			uint16_t i = (x * 63 * 6 / 480 + ms) % (63 * 6);
      uint8_t r = 0;
      uint8_t g = 0;
      uint8_t b = 0;

			if (i < 63) {
        r = 63;
        g = i;
			} else if (i < 63 * 2) {
				r = 63 * 2 - i;
        g = 63;
			} else if (i < 63 * 3) {
        g = 63;
        b = i - 63 * 2;
			} else if (i < 63 * 4) {
        g = 63 * 4 - i;
        b = 63;
			} else if (i < 63 * 5) {
				r = i - 63 * 4;
        b = 63;
			} else {
        r = 63;
        b = 63 * 6 - i;
			}

      buff[x] = (r >> 1) << 11 | g << 5 | (b >> 1);

  }

  CS_L();
  Address_set(0,0,319,479);
  spi.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));
	RS_H();
  for (uint16_t x = 0; x < 480; x++)
	  for (uint16_t y = 0; y < 320; y++) {
	    spi.transfer(buff[x] >> 8);
	    spi.transfer(buff[x]);
	  }
  spi.endTransaction();
  CS_H();

}

Последнее время хочется отказаться от пакета для esp32s3nano в пользу какого-нибудь стандартного для esp32, а то постоянно такие проблемы, а стандартные ESP-IDF функции либо не перенесены в API либо перенесены с изменениями, и хз что откуда вызывается

/*#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_now.h>
#include <WiFi.h>*/

#include <Wire.h>
#include "driver/spi_master.h"



#define RS    21 // GPIO 21
#define RESET 18 // GPIO 18

#define CS    17 // GPIO 17
#define MISO  47 // GPIO 47
#define MOSI  38 // GPIO 38
#define SCK   48 // GPIO 48

#define SPI_FREQUENCY 80000000

#define SPI_ON_PORT SPI3_HOST

#define CS_H() GPIO.out_w1ts |= 0x20000
#define CS_L() GPIO.out_w1tc |= 0x20000
#define RS_H() GPIO.out_w1ts |= 0x200000
#define RS_L() GPIO.out_w1tc |= 0x200000
#define RESET_H() GPIO.out_w1ts |= 0x40000
#define RESET_L() GPIO.out_w1tc |= 0x40000



spi_device_handle_t spi_handle;



void spisend (uint8_t *data, uint32_t bytes) {

  spi_transaction_t transaction;
  memset(&transaction, 0, sizeof(spi_transaction_t));
  transaction.tx_buffer = data;
  transaction.length = bytes*8;
  spi_device_transmit(spi_handle, &transaction);

}

void lcd_write_command(uint8_t value) {

  RS_L();
  spisend(&value, 1);

}

void lcd_write_data(uint8_t value) {

  RS_H();
  spisend(&value, 1);

}

void Address_set( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {

  lcd_write_command(0x2a);
  lcd_write_data(y0>>8);
  lcd_write_data(y0);
  lcd_write_data(y1>>8);
  lcd_write_data(y1);
  lcd_write_command(0x2b);
  lcd_write_data(x0>>8);
  lcd_write_data(x0);
  lcd_write_data(x1>>8);
  lcd_write_data(x1);
  lcd_write_command(0x2c); 
                 
}

void lcd_init() {

  // настройка портов
  gpio_reset_pin((gpio_num_t)CS);
  gpio_set_direction((gpio_num_t)CS, GPIO_MODE_OUTPUT);
  gpio_set_pull_mode((gpio_num_t)CS, GPIO_FLOATING);
  gpio_reset_pin((gpio_num_t)RS);
  gpio_set_direction((gpio_num_t)RS, GPIO_MODE_OUTPUT);
  gpio_set_pull_mode((gpio_num_t)RS, GPIO_FLOATING);
  gpio_reset_pin((gpio_num_t)RESET);
  gpio_set_direction((gpio_num_t)RESET, GPIO_MODE_OUTPUT);
  gpio_set_pull_mode((gpio_num_t)RESET, GPIO_FLOATING);

  // инициализация SPI
  spi_bus_config_t buscfg = {
    .mosi_io_num = MOSI,
    //.data0_io_num = -1,
    .miso_io_num = MISO,
    //.data1_io_num = -1,
    .sclk_io_num = SCK,  
    .quadwp_io_num = -1,
    //.data2_io_num = -1,
    .quadhd_io_num = -1,
    //.data3_io_num = -1,
    .data4_io_num = -1,
    .data5_io_num = -1,
    .data6_io_num = -1,
    .data7_io_num = -1,
    .max_transfer_sz = 480*2,
    .flags = 0,
    .intr_flags = 0
  };

  spi_device_interface_config_t devcfg = {
    .command_bits = 0,
    .address_bits = 0,
    .dummy_bits = 0,
    .mode = 0,
    .duty_cycle_pos = 128,      //50% duty cycle
    .cs_ena_pretrans = 0,
    .cs_ena_posttrans = 0,
    .clock_speed_hz = 80000000,
    .input_delay_ns = 0,
    .spics_io_num = CS,
    .flags = 0,
    .queue_size = 3,
    .pre_cb = NULL,
    .post_cb = NULL
  };

  spi_bus_initialize(SPI_ON_PORT, &buscfg, SPI_DMA_CH_AUTO);
  spi_bus_add_device(SPI_ON_PORT, &devcfg, &spi_handle);

  // сброс экрана
  RESET_H();
  delay(5); 
  RESET_L();
  delay(15);
  RESET_H();
  delay(15);

  // отправка регистров настроек

  CS_L();

  lcd_write_command(0x11);     

  delay(120);

  lcd_write_command(0x36);
  lcd_write_data(0x48);   

  lcd_write_command(0x3A);     
  lcd_write_data(0x55);   

  lcd_write_command(0xF0);
  lcd_write_data(0xC3);   

  lcd_write_command(0xF0);     
  lcd_write_data(0x96);   

  lcd_write_command(0xB4);     
  lcd_write_data(0x02);   

  lcd_write_command(0xB7);     
  lcd_write_data(0xC6);   

  lcd_write_command(0xC0);     
  lcd_write_data(0xC0);   
  lcd_write_data(0x00);   

  lcd_write_command(0xC1);     
  lcd_write_data(0x13);   

  lcd_write_command(0xC2);     
  lcd_write_data(0xA7);   

  lcd_write_command(0xC5);     
  lcd_write_data(0x21);   

  lcd_write_command(0xE8);     
  lcd_write_data(0x40);   
  lcd_write_data(0x8A);   
  lcd_write_data(0x1B);   
  lcd_write_data(0x1B);   
  lcd_write_data(0x23);   
  lcd_write_data(0x0A);   
  lcd_write_data(0xAC);   
  lcd_write_data(0x33);   

  lcd_write_command(0xE0);     
  lcd_write_data(0xD2);   
  lcd_write_data(0x05);   
  lcd_write_data(0x08);   
  lcd_write_data(0x06);   
  lcd_write_data(0x05);   
  lcd_write_data(0x02);   
  lcd_write_data(0x2A);   
  lcd_write_data(0x44);   
  lcd_write_data(0x46);   
  lcd_write_data(0x39);   
  lcd_write_data(0x15);   
  lcd_write_data(0x15);   
  lcd_write_data(0x2D);   
  lcd_write_data(0x32);   

  lcd_write_command(0xE1);     
  lcd_write_data(0x96);   
  lcd_write_data(0x08);   
  lcd_write_data(0x0C);   
  lcd_write_data(0x09);   
  lcd_write_data(0x09);   
  lcd_write_data(0x25);   
  lcd_write_data(0x2E);   
  lcd_write_data(0x43);   
  lcd_write_data(0x42);   
  lcd_write_data(0x35);   
  lcd_write_data(0x11);   
  lcd_write_data(0x11);   
  lcd_write_data(0x28);   
  lcd_write_data(0x2E);   

  lcd_write_command(0xF0);     
  lcd_write_data(0x3C);   
  lcd_write_command(0xF0);     
  lcd_write_data(0x69);  

  delay(120);                
  lcd_write_command(0x21);     
  lcd_write_command(0x29);

  CS_H();

}





uint16_t TOUCH_X, TOUCH_Y;

void touch_read() {

  // запрос количества нажатий
  Wire.beginTransmission(0x38);
  Wire.write(0x02);
  Wire.endTransmission();
  Wire.requestFrom(0x38, (uint8_t)1);
  // если количество нажатий равно одному, запрос координат
  if (Wire.read() == 1) {
    Wire.beginTransmission(0x38);
    Wire.write(0X03);
    Wire.endTransmission();
    Wire.requestFrom(0x38, 4);
    TOUCH_Y = ((Wire.read() & 0x0F) << 8) + Wire.read();
    TOUCH_X = ((Wire.read() & 0x0F) << 8) + Wire.read();
  }

}

void touch_init(uint8_t pinInt, uint8_t pinRst) {

  pinMode(pinInt, INPUT);
  pinMode(pinRst, OUTPUT);
  digitalWrite(pinInt, 1);
  digitalWrite(pinRst, 1);
  delay(20);
  digitalWrite(pinRst, 0);
  delay(20);
  digitalWrite(pinRst, 1);
  delay(500);
  Wire.begin();

}


// буфер экрана
uint8_t BUFFER[32][32][2];

void setup() {

  lcd_init();
  touch_init(7, 6); // GPIO 10, GPIO 9
  
  // заполнение белым
  for (uint16_t x = 0; x < 32; x++)
    for (uint16_t y = 0; y < 32; y++) {
      BUFFER[x][y][0] = 0xff;
      BUFFER[x][y][1] = 0xff;
    }
  for (uint16_t i = 0; i < 15; i++)
    for (uint16_t j = 0; j < 10; j++) {
      CS_L();
      Address_set(i * 32, j * 32, i * 32 + 31, j * 32 + 31);
      RS_H();
      spisend((uint8_t *)BUFFER, 32*32*2);
      CS_H();
    }
  
}



void loop() {

  /* ДЕМО СПЕКТР
  uint32_t ms = millis() >> 3;

  for (uint16_t i = 0; i < 15; i++) {
    for (uint16_t j = 0; j < 10; j++) {

      for (uint16_t x = i * 32; x < i * 32 + 32; x++) {

        uint32_t n = (x * 63 * 6 / 480 + ms) % (63 * 6);
        uint8_t r = 0;
        uint8_t g = 0;
        uint8_t b = 0;
        if (n < 63) {
          r = 63;
          g = n;
        } else if (n < 63 * 2) {
          r = 63 * 2 - n;
          g = 63;
        } else if (n < 63 * 3) {
          g = 63;
          b = n - 63 * 2;
        } else if (n < 63 * 4) {
          g = 63 * 4 - n;
          b = 63;
        } else if (n < 63 * 5) {
          r = n - 63 * 4;
          b = 63;
        } else {
          r = 63;
          b = 63 * 6 - n;
        }

        for (uint16_t y = j * 32; y < j * 32 + 32; y++) {

          uint8_t r2 = (uint16_t)r * y / 319;
          uint8_t g2 = (uint16_t)g * y / 319;
          uint8_t b2 = (uint16_t)b * y / 319;
  
          BUFFER[x % 32][y % 32][0] = (r2 >> 1) << 3 | g2 >> 3;
          BUFFER[x % 32][y % 32][1] = g2 << 5 | (b2 >> 1);
      
        }
      }
  
      CS_L();
      Address_set(i * 32, j * 32, i * 32 + 31, j * 32 + 31);
      RS_H();
      spisend((uint8_t *)BUFFER, 32*32*2);
      CS_H();

    }

  }
  /**/

  //* ДЕМО ТАЧСКРИН
  static uint16_t XX = 0, YY = 0;

  touch_read();

  if(TOUCH_X != XX && TOUCH_Y != YY) {

    uint8_t i = XX / 32;
    uint8_t j = YY / 32;

    for (uint16_t x = 0; x < 32; x++)
      for (uint16_t y = 0; y < 32; y++) {
          BUFFER[x][y][0] = 0xff;
          BUFFER[x][y][1] = 0xff;
      }
    CS_L();
    Address_set(i * 32, j * 32, i * 32 + 31, j * 32 + 31);
    RS_H();
    spisend((uint8_t *)BUFFER, 32*32*2);
    CS_H();

    XX = TOUCH_X;
    YY = TOUCH_Y;
    i = XX / 32;
    j = YY / 32;

    
    uint8_t c1 = (j * 32 / 10) << 3;
    uint8_t c2 = (i * 32 / 15);

    for (uint16_t x = 0; x < 32; x++)
      for (uint16_t y = 0; y < 32; y++) {
        BUFFER[x][y][0] = c1;
        BUFFER[x][y][1] = c2;
      }
    CS_L();
    Address_set(i * 32, j * 32, i * 32 + 31, j * 32 + 31);
    RS_H();
    spisend((uint8_t *)BUFFER, 32*32*2);
    CS_H();

  }
  /**/

}

Досношал я этот кровососущий экран с тачскрином… Вернее spi для arduino nano esp32 (esp32-s3-nano).

Дело было действительно в шине. Насколько я могу судить, отправка через самый стандартный spi.h реализуется в этом пакете через функции esp-idf, да таким образом, что если отправляешь по 1 байту, то на каждый байт создается своя spi_transaction_t. В итоге отправка занимает в разы больше времени, чем должна. Буфер 32*32 и использование spi из driver/spi_master.h решило проблему.

Но это неточно… По крайней мере теперь скорость обновления экрана целиком я не могу на глаз замерить - быстро, правда без VSINC))) Сегментно итого быстрее.

Если кто опыт имел со spi и его настройкой функциями esp-idf, прошу проверить параметры вначале ф-ии lcd_init и ткнуть носом в ошибки - исправлю, проверю и переотмечу решение вопроса

Ну да, Arduino Core сделан через жопу.
А ESP-IDF который они используют - старый. Сильно отстает от main ветки.
Геморой, да.

Кстати, по поводу твоих вычислений: в ESP32 есть т.н. TIE (Tensilica Instruction Extensions), в частности, там есть векторные инструкции, 128 битные. Можно перемножать вектор (читай - массив) на вектор.

Это используют люди в DSP на основе ESP32.

Инструкции эти ассемблерные хоть и описаны, но фигово, лучше смотреть в чужой код. Там, вроде бы, и FFT акселерацию можно получить, но это не точно :slight_smile:

Во чо пишут:

Normally, the data that needs to be transferred to or from a Device is read from or written to a chunk of memory indicated by the members spi_transaction_t::rx_buffer and spi_transaction_t::tx_buffer. If DMA is enabled for transfers, the buffers are required to be:

  1. Allocated in DMA-capable internal memory (MALLOC_CAP_DMA), see DMA-Capable Memory.
  2. 32-bit aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes).

любопытно… но как это мне использовать? разве что при расчетах геометрии можно было бы, но я в 3d не работаю, пока требуется только 2d и расчет z от иных параметров. А двумерные вектора перемножать через матрицы… звучит как что-то для ленивых богатеев))

попробовал DMA_ATTR uint8_t BUFFER[buff_size][buff_size][2]; оно либо игнорится, либо никак на быстродействии не сказалось. Хотя возможно можно было весь экран целиком в DMA расположить (ща попробую, вдруг не, целиком не лезет). Пока в вопросах памяти esp32 я ноль

Удалось выцепить ситуацию, когда буфер не успел отправиться по шине, но уже меняется из основного кода программы. В описании spi_transaction_t было требование не изменять буфер до конца отправки. А как собсно стопорить процесс и ожидать завершения?

UPD: вру, не удалось. Сенсорный экран иногда возвращал координаты вне области экрана и происходило смещение. Но вопрос ожидания отправки все равно считаю актуальным.

Семафор захватывать, наверное.

1 лайк

Тебе нужен объект синхронизации. Как выше заметили - семафор, например.

Не знаю, как в SPI это сделано, но в “обычном” DMA режиме принято посылать прерывание (DMA контроллер посылает), когда буфер передан.

(или передан частично. на PowerPC в случае Ethernet и i386 в случае SoundBlaster/DOS DMA может генерировать прерывания по передаче, например, половины буфера. Пока передается вторая половина, можно заполнять первую)

В PowerPC, например, я подготавливаю N буферов на отправку и говорю: а сгенерируй мне прерывание, когда будет отправлена половина буферов, а потом еще разок, когда отправится вторая половина.

В ESP32 должно быть плюс-минус так же.

Как оказалось, и вроде работает как надо, есть дополнительные ф-ии работы со spi (инфа с сайта)

Спойлер
spi_device_queue_trans Ставит SPI-транзакцию SPI в очередь для выполнения как interrupt-транзакцию. Результат транзакции извлекается вызовом spi_device_get_trans_result. Примечание: обычно устройство не может запустить (поставить в очередь) одновременно polling-транзакции и interrupt-транзакции.
spi_device_get_trans_result Извлекает результат SPI-транзакции, которая была ранее поставлена в очередь вызовом spi_device_queue_trans. Эта подпрограмма будет ждать, пока транзакция для указанного устройства не будет успешно выполнена. Функция возвратит описание завершенной транзакции, чтобы программа могла проверить результат, и по этому результату выполнить нужные действия, например освободить память, или повторно исопльзовать буферы для других транзакций.
spi_device_transmit Посылает SPI-транзакцию с ожидением её завершения, и возвратит результат транзакции. Эта функция эквивалентна последовательным вызовам функций spi_device_queue_trans() и spi_device_get_trans_result(). По этой причине не используйте spi_device_transmit, кода существует транзакция, которая была отдельно поставлена в очередь (запущена) из вызова spi_device_queue_trans() или polling_start/transmit, и которая еще не завершена. Примечание: функция spi_device_transmit не является потокобезопасной (not thread safe), когда несколько задач обращаются к одному устройству SPI. Обычно устройство не может запустить (поставить в очередь) одновременно polling-транзакции и interrupt-транзакции.

Таким образом, у меня рассинхрон наверное все же был из-за редких вылетов тач модуля за индексы экрана, а так функции эквивалентны:

void spisend (uint8_t *data, uint32_t bytes) {
  spi_transaction_t transaction;
  memset(&transaction, 0, sizeof(spi_transaction_t));
  transaction.tx_buffer = data;
  transaction.length = bytes*8;
  spi_device_queue_trans(spi_handle, &transaction, portMAX_DELAY);
  spi_transaction_t* p = &transaction;
  spi_device_get_trans_result(spi_handle, &p, portMAX_DELAY);
}
void spisend (uint8_t *data, uint32_t bytes) {
  spi_transaction_t transaction;
  memset(&transaction, 0, sizeof(spi_transaction_t));
  transaction.tx_buffer = data;
  transaction.length = bytes*8;
  spi_device_transmit(spi_handle, &transaction);
}