как подключить дисплей на базе чипа ILI9225 к отладочной плате ESP32
В данной статье пойдет речь о том, как подключить дисплей на базе чипа ILI9225 к отладочной плате ESP32, а так же будет приведен пример вывода изображения в формате JPEG с флэш карты.
JPEG - это один из популярных растровых графических форматов, применяемый для хранения фотографий и подобных им изображений. Он занимает меньше места в файле и обеспечивает более высокую скорость отображения, чем Bitmap. TFT дисплей на базе ILI9225 имеет слот для SD-карты и подключется к контроллеру через SPI. Программа будет считывать с флэш карты файл jpg в корневом каталоге (размер изображения должен быть 176*220, а имя файла состоять из цифр от 0 до 4). Работа програмы сводится к отображению файла в течение 2 секунд на дисплее, а затем будет сделан переход к следующему файлу.
Схема подключения
Для работы в среде Arduino IDE понадобятся две библиотеки.
Ссылки на скачивания:
- библиотека для декодировния JPEG: https://github.com/arduinopavlodar/JPEGDecoder
- баблиотека для работы с дисплеем: https://github.com/arduinopavlodar/TFT_22_ILI9225
Код
#include "SPI.h" #include "TFT_22_ILI9225.h" #include "SD.h" #include "FS.h" #define SD_CS 5 #define SPI_MOSI 23 #define SPI_MISO 19 #define SPI_SCK 18 #include // Библиотека декодера JPEG /* ########################################## ### ######################################### ############################################ #### ######################################## */ #define TFT_RST 4 // IO 26 #define TFT_RS 2 // IO 25 #define TFT_CLK 14 // HSPI-SCK // # define TFT_SDO 12 // HSPI-MISO #define TFT_SDI 13 // HSPI-MOSI #define TFT_CS 15 // HSPI-SS0 #define TFT_LED 0 // 0, если подключен к + 5V напрямую SPIClass hspi (HSPI); #define TFT_BRIGHTNESS 200 // Начальная яркость подсветки TFT (необязательно) // Использовать аппаратный SPI (быстрее на Uno: 13-SCK, 12-MISO, 11-MOSI) TFT_22_ILI9225 tft = TFT_22_ILI9225 (TFT_RST, TFT_RS, TFT_CS, TFT_LED, TFT_BRIGHTNESS); /* ########################################## ### ######################################### ############################################ #### ######################################### */ /* =================================================================================== Этот скетч содержит вспомогательные функции для рендеринга изображений Jpeg. =============================================================================== */ // Возвращаем минимум из двух значений a и b #define minimum (a, b) (((a) <(b))? (A): (b)) // =============================================== = =================================== // Выводим информацию о декодированном изображении Jpeg // === = =================================================== = ============================== void jpegInfo() { Serial.println(F("===============")); Serial.println(F("JPEG image info")); Serial.println(F("===============")); Serial.print(F( "Width :")); Serial.println(JpegDec.width); Serial.print(F( "Height :")); Serial.println(JpegDec.height); Serial.print(F( "Components :")); Serial.println(JpegDec.comps); Serial.print(F( "MCU / row :")); Serial.println(JpegDec.MCUSPerRow); Serial.print(F( "MCU / col :")); Serial.println(JpegDec.MCUSPerCol); Serial.print(F( "Scan type :")); Serial.println(JpegDec.scanType); Serial.print(F( "MCU width :")); Serial.println(JpegDec.MCUWidth); Serial.print(F( "MCU height :")); Serial.println(JpegDec.MCUHeight); Serial.println(F("===============")); } // =============================================== = =================================== // Декодирование и рендеринг на экран TFT // === = =================================================== = ============================== void IRAM_ATTR renderJPEG ( int xpos, int ypos) { // получить информацию об изображении uint16_t * pImg; uint16_t mcu_w = JpegDec.MCUWidth; uint16_t mcu_h = JpegDec.MCUHeight; uint16_t max_x = JpegDec.width; uint16_t max_y = JpegDec.height; // Изображения Jpeg рисуются как набор блоков изображения (тайлов), называемых минимальными единицами кодирования (MCU) // Обычно эти MCU представляют собой блоки размером 16x16 пикселей // Определение ширины и высоты блоков изображения правого и нижнего края uint16_t min_w = minimum (mcu_w, max_x % mcu_w); uint16_t min_h = минимум (mcu_h, max_y % mcu_h); // сохраняем текущий размер блока изображения uint16_t win_w = mcu_w; uint16_t win_h = mcu_h; // записываем текущее время, чтобы мы могли измерить, сколько времени нужно, чтобы нарисовать изображение uint32_t drawTime = millis (); // сохраняем координаты правого и нижнего краев для помощи при обрезке изображения // до размера экрана max_x + = xpos; max_y + = ypos; // читаем каждый блок MCU, пока не закончится while (JpegDec.read ()) { // сохраняем указатель на блок изображения pImg = JpegDec.pImage; // вычисляем, где на экране должен быть нарисован блок изображения int mcu_x = JpegDec.MCUx * mcu_w + xpos; int mcu_y = JpegDec.MCUy * mcu_h + ypos; // проверяем, нужно ли изменить размер блока изображения для правого и нижнего краев if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; for ( int y = 0 ; y < win_h; y ++ ) { for ( int x = 0 ; x < win_w; x ++ ) { // Вставляем каждый пиксель в область TFT MCU tft.drawPixel (mcu_x + x, mcu_y + y, ( * pImg)); pImg ++ ; // Увеличиваем указатель } } } /* ********************************************** ************************************** *********** ************************************************* ************************* */ // вычисляем, сколько времени потребовалось для рисования изображения drawTime = millis () - drawTime; // вывод результатов на последовательный порт Serial.print(F( "Total render time was : ")); Serial.print(drawTime); Serial.println(F(" ms")); Serial.println(F("")); } // ============================================== == ================================== // Открыть файл изображения Jpeg и отобразить его с заданными координатами. / / ============================================= ==== ================================= void jpegDraw ( const char * filename, int x, int y) { // Пытаемся открыть запрошенный файл на SD-карте if (!SD.open(filename)) { //, O_READ Serial.println("Jpeg file not found on SD card."); return; } Serial.print(F("Decoding image '")); Serial.print(filename); Serial.println('\''); // инициализируем декодер, проверяем совместимость и получаем доступ к информации об изображении boolean decoded = JpegDec.decodeSdFile(filename); if (decoded) { // выводим информацию об изображении в последовательный порт jpegInfo (); // рендерим изображение на экран с заданными координатами if((JpegDec.width == 176) && (JpegDec.height == 220)) renderJPEG(x, y); else Serial.println(F("Jpeg size not supported.")); } else { Serial.println(F("Jpeg file format not supported.")); } } /* ########################################## ### ######################################### ############################################ #### ############################################################################# */ // Настройка void setup() { Serial.begin(115200); while (!Serial) {}; delay(1000); #if defined(ESP32) hspi.begin(); tft.begin(hspi); Serial.println("hspi init done."); #else tft.begin(); #endif / * ********************************************** ************************************** *********** ************************************************* ************************** * / tft.setFont(Terminal6x8); Serial.print("Initializing SD card..."); //if (!SD.begin(32, 14, 13, 27)) { if (!SD.begin(SD_CS)) { Serial.println("initialization failed!"); tft.drawText(10, 110, "SD initialization failed!", COLOR_RED); while(1); } Serial.println("initialization done."); delay(1000); } /*################################################################################################## ##################################################################################################*/ // Loop void loop() { // draw jpeg image at 0,0 for(int l=0; l<5; l++){ String name = "/" + String(l) + ".jpg"; jpegDraw( name.c_str(), 0, 0 ); delay(2000); } }