Arduino:Примеры/shieldtest

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

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Перемещение картинки на TFT-дисплее (при помощи джойстика)[1]

Это скетч для библиотеки ST7735, который при помощи платы Arduino и джойстика позволяет двигать картинку на TFT-дисплее. Для скетча потребуется вот этот TFT-модуль, оснащенный джойстиком (он представляет собой небольшую черную кнопку) и TFT-дисплеем.

Код

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

Перемещение картинки на TFT-дисплее (при помощи джойстика)
Этот скетч предназначен для использования 
с 1,8-дюймовым TFT-модулем (с джойстиком):
----> http://www.adafruit.com/products/802

Руководства и схемы подключения ищите по ссылке выше. 
Этим дисплеям для коммуникации требуется SPI-интерфейс 
с 4 контактами. Кроме того, для джойстика потребуется еще 
один контакт, и здесь для этого используется 3-ий аналоговый.

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

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

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

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SD.h>
#include <SPI.h>

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif

// Коммуникация будет вестись через аппаратный SPI-интерфейс,
// и им будут пользоваться сразу два устройства: TFT-дисплей
// и SD-карта. На платах Arduino аппаратный SPI всегда находится на
// одних и тех же контактах, и переназначить их нельзя. 
// На Arduino Uno, Duemilanove и т.д. за SPI отвечают следующие 
// контакты: 11-ый контакт – MOSI, 12-ый контакт – MISO,
// 13-ый контакт – SCK. 
#define SD_CS    4   // CS-контакт для SD-карты
#define TFT_CS  10   // CS-контакт для TFT-дисплея
#define TFT_DC   8   // DC-контакт для дисплея (для переключения между передачей данных и команд)
#define TFT_RST  -1  // RESET-контакт для TFT-дисплея (или подключите к +5V)

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

#define BUTTON_NONE 0
#define BUTTON_DOWN 1
#define BUTTON_RIGHT 2
#define BUTTON_SELECT 3
#define BUTTON_UP 4
#define BUTTON_LEFT 5

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

  // инициализируем 1,8-дюймовый TFT-дисплей:
  tft.initR(INITR_BLACKTAB);   // инициализируем чип ST7735S, черный ярлычок

  Serial.println("OK!");
  tft.fillScreen(ST7735_BLACK);
}


uint8_t readButton(void) {
  float a = analogRead(3);
  
  a *= 5.0;
  a /= 1024.0;
  
  Serial.print("Button read analog = ");  //  "Считываем данные с аналогового джойстика = ")
  Serial.println(a);
  if (a < 0.2) return BUTTON_DOWN;
  if (a < 1.0) return BUTTON_RIGHT;
  if (a < 1.5) return BUTTON_SELECT;
  if (a < 2.0) return BUTTON_UP;
  if (a < 3.2) return BUTTON_LEFT;
  else return BUTTON_NONE;
}

uint8_t buttonhistory = 0;

void loop() {
  uint8_t b = readButton();
  tft.setTextSize(3);
  if (b == BUTTON_DOWN) {
    tft.setTextColor(ST7735_RED);
    tft.setCursor(0, 10);
    tft.print("Down ");  //  "Вниз "
    buttonhistory |= 1;
  }
  if (b == BUTTON_LEFT) {
    tft.setTextColor(ST7735_YELLOW);
    tft.setCursor(0, 35);
     tft.print("Left ");  //  "Влево "
    buttonhistory |= 2;
  }
  if (b == BUTTON_UP) {
    tft.setTextColor(ST7735_GREEN);
    tft.setCursor(0, 60);
    tft.print("Up");   //  "Вверх "
    buttonhistory |= 4;
  }
  if (b == BUTTON_RIGHT) {
    tft.setTextColor(ST7735_BLUE);
    tft.setCursor(0, 85);
    tft.print("Right");  //  "Вправо "
    buttonhistory |= 8;
  }
  if ((b == BUTTON_SELECT) && (buttonhistory == 0xF)) {
    tft.setTextColor(ST7735_MAGENTA);
    tft.setCursor(0, 110);
    tft.print("SELECT");  //  "ВЫБОР"
    buttonhistory |= 8;
    delay(2000);
    Serial.print("Initializing SD card...");  //  "Инициализация SD-карты...")
    if (!SD.begin(SD_CS)) {
      Serial.println("failed!");  //  "не удалась!")
      return;
    }
    bmpDraw("parrot.bmp", 0, 0);
    while (1);
  }
  delay(100);
}

// Эта функция открывает 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;
}

См.также

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