Arduino:Примеры/spitftbitmap

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Вывод изображения на 1,8-дюймовый или 1,44-дюймовый TFT-дисплей[1]

Это скетч для библиотеки ST7735, который при помощи платы Arduino показывает на TFT-дисплее (1,8-дюймовый или 1,44-дюймовый) заданную картинку. Для скетча потребуется 1,8-дюймовая TFT-плата с SD-картой или «голый» 1,8-дюймовый TFT-дисплей.

Код

 
/***************************************************

Вывод изображения на 1,8-дюймовый или 1,44-дюймовый TFT-дисплей

Этот скетч предназначен для 1,8-дюймового или 1,44-дюймового 
TFT-дисплея, работающего через SPI-интерфейс (от Adafruit).

Для этого скетча подойдет 1,8-дюймовая TFT-плата с SD-картой
----> http://www.adafruit.com/products/358
или 1,8-дюймовый TFT-модуль
----> https://www.adafruit.com/product/802
или 1,44-дюймовая TFT-плата
----> https://www.adafruit.com/product/2088
или «голый» 1,8-дюймовый TFT-дисплей
----> http://www.adafruit.com/products/618

Руководства и схемы подключения ищите по ссылкам выше. Этим дисплеям 
для коммуникации требуется SPI-интерфейс с 4 или 5 контактами (контакт 
RST опционален). 

Adafruit инвестировала время и ресурсы, создавая эту библиотеку с 
открытым кодом. Пожалуйста, поддержите Adafruit и оборудование с 
открытым кодом, покупая продукты Adafruit!

Библиотека написана Лимор Фрид (Limor Fried, Ladyada) для Adafruit 
Industries. Весь текст выше должен быть включен при любом повторном 
распространении.

****************************************************/

#include <Adafruit_GFX.h>    // подключаем графическую библиотеку
#include <Adafruit_ST7735.h> // подключаем библиотеку для управления дисплеем
#include <SPI.h>
#include <SD.h>

// Коммуникация будет вестись через аппаратный SPI-интерфейс,
// и им будут пользоваться сразу два устройства: TFT-дисплей
// и SD-карта. На платах Arduino аппаратный SPI всегда находится на
// одних и тех же контактах, и переназначить их нельзя. 
// На Arduino Uno, Duemilanove и т.д. за SPI отвечают следующие 
// контакты: 11-ый контакт – MOSI, 12-ый контакт – MISO,
// 13-ый контакт – SCK. 

#define TFT_CS  10  // CS-контакт для TFT-дисплея 
#define TFT_RST  9  // RESET-контакт для TFT-дисплея (или см. ниже)
#define TFT_DC   8  // DC-контакт для TFT-дисплея (для переключения между передачей данных и команд)
#define SD_CS    4  // CS-контакт для SD-карты 

// Это RESET-контакт для TFT-модуля
//#define TFT_RST  0  // этот контакт также можно подключить к RESET-контакту Arduino

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

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

  // инициализатор для 1,8-дюймового дисплея:
  tft.initR(INITR_BLACKTAB);

  // инициализатор для 1,44-дюймового дисплея (раскомментируйте):
  //tft.initR(INITR_144GREENTAB);

  Serial.print("Initializing SD card...");  //  "Инициализация SD-карты... "
  if (!SD.begin(SD_CS)) {
    Serial.println("failed!");  //  "не удалась"
    return;
  }
  Serial.println("OK!");

  // поменяйте название картинки, если нужно:
  bmpDraw("parrot.bmp", 0, 0);
  // ждем 5 секунд:
  delay(5000);
}

void loop() {
// раскомментируйте этот код, если хотите нарисовать картинку 
// в другом месте и немного ее повращать.
/*
  tft.fillScreen(ST7735_BLACK); // очищаем дисплей
  for(uint8_t i=0; i<4; i++)    // рисуем 4 попугая
    bmpDraw("parrot.bmp", tft.width() / 4 * i, tft.height() / 4 * i);
  delay(1000);
  tft.setRotation(tft.getRotation() + 1); // вращение на 90 градусов
*/
}

// Эта функция открывает BMP-файл и печатает его на заданных 
// координатах. Ее работу можно ускорить, считывая сразу несколько 
// пикселей, а не пиксель за пикселем. Для этого нужно увеличить 
// размер буфера, однако это, в свою очередь, потребует ресурсов 
// RAM-памяти Arduino. Разумным компромиссом будет 20 пикселей.

#define BUFFPIXEL 20

void bmpDraw(char *filename, uint8_t x, uint8_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // ширина и высота в пикселях
  uint8_t  bmpDepth;              // глубина цвета (по умолчанию должно стоять 24)
  uint32_t bmpImageoffset;        // место, с которого в файле начинается само изображение
  uint32_t rowSize;               // это значение не всегда будет равно bmpWidth; возможно, будет отступ
  uint8_t  sdbuffer[3*BUFFPIXEL]; // буфер для пикселей (значения R, G и B для каждого пикселя)
  uint8_t  buffidx = sizeof(sdbuffer); // текущая позиция в буфере для пикселей
  boolean  goodBmp = false;       // если скетч определит нужный тип заголовка, он выставит здесь «true» 
  boolean  flip    = true;        // данные BMP-файла хранятся по принципу «снизу вверх» 
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.println();
  Serial.print("Loading image '");  //  "Загрузка изображения «"
  Serial.print(filename);
  Serial.println('\'');

  // открываем на SD-карте запрашиваемый файл:
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");  //  "Файл не найден"
    return;
  }

  // анализируем заголовок BMP-Файла: 
  if(read16(bmpFile) == 0x4D42) { // сигнатура BMP-файла
    Serial.print("File size: ");  //  "Размер файла: "
    Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // считываем и игнорируем байты, вписанные при создании файла 
    bmpImageoffset = read32(bmpFile); // место, с которого начинается в файле само изображение (смещение)
    Serial.print("Image Offset: ");  //  "Смещение: "
    Serial.println(bmpImageoffset, DEC);
    // считываем заголовок DIB:
    Serial.print("Header size: ");  //  "Размер заголовка: "
    Serial.println(read32(bmpFile)); 
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) {  //  количество цветовых плоскостей – должно быть «1»
      bmpDepth = read16(bmpFile); // биты на пиксель 
      Serial.print("Bit Depth: ");  //  "Глубина цвета: "
      Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) {  //  «0» – это без сжатия

        goodBmp = true; // поддерживаемый формат BMP – продолжаем дальше!
        Serial.print("Image size: ");  //  "Размер изображения: "
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // если нужно, границы BMP-изображения будут иметь 4-байтный отступ: 
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // Если значение в bmpHeight отрицательное, то данные 
        // изображения хранятся по принципу «сверху вниз»
        // Это не канон, но иногда встречается.  
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // загружаем обрезанную область: 
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // задаем адресный промежуток для границ обрезанного изображения: 
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // для каждого пиксельного ряда...

          // Ищем начало пиксельного ряда. Возможно, делать это для 
          // каждой строки – слишком трудоемко, однако эта функция 
          // выполняет много «грязной работы» вроде обрезки 
          // изображения и создания отступа для нового пиксельного 
          // ряда. Кроме того, этот поиск имеет место только в том 
          // случае, если нужно поменять позицию файла (это позволяет 
          // избежать кластерных расчетов в библиотеке SD). 

          if(flip) // данные BMP-файла хранятся по принципу «снизу вверх» (нормальный BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // BMP-файл хранится по принципу «сверху вниз» 
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // нужно ли запустить поиск? 
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // перезагружаем буфер
          }

          for (col=0; col<w; col++) { // для каждого пикселя...
            // настало время считывать новые пиксельные данные?         
            if (buffidx >= sizeof(sdbuffer)) { // да! 
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // выставляем индекс на начало 
            }

            // конвертируем пиксели из формата BMP в формат TFT,
            // а затем выводим их на дисплей:
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.Color565(r,g,b));
          } // последний пиксель
        } // последняя строка пикселей
        Serial.print("Loaded in ");  //  "Картинка загрузилась за "
        Serial.print(millis() - startTime);
        Serial.println(" ms");  // "миллисекунд"
      } // конец goodBMP 
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");  //  "Формат BMP не обнаружен"
}

// Эти функции считывают из BMP-файла 16-битные и 32-битные данные.
// Данные BMP-файла, как и у Arduino, хранятся по принципу 
// «от младшего к старшему». При портировании куда-либо этот порядок, 
// возможно, придется поменять.

uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // самый младший бит
  ((uint8_t *)&result)[1] = f.read(); // самый старший бит
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // самый младший бит
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // самый старший бит 
  return result;
}

См.также

Внешние ссылки