Arduino:Примеры/soft spitftbitmap
Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Содержание | Знакомство с Arduino | Продукты | Основы | Справочник языка Arduino | Примеры | Библиотеки | Хакинг | Изменения | Сравнение языков Arduino и Processing |
Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.
Вывод изображения на 1,8-дюймовый TFT-дисплей[1]
Это скетч для библиотеки ST7735, который при помощи платы Arduino показывает на TFT-дисплее (1,8-дюймовом) заданную картинку. Для скетча потребуется 1,8-дюймовая TFT-плата с SD-картой или «голый» 1,8-дюймовый TFT-дисплей.
Код
/***************************************************
Вывод изображения на 1,8-дюймовый TFT-дисплей
Этот скетч предназначен для 1,8-дюймового TFT-дисплея,
работающего через SPI-интерфейс (от Adafruit).
Для этого скетча подойдет 1,8-дюймовая TFT-плата с SD-картой...
----> http://www.adafruit.com/products/358
...а также «голый» 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>
#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 SPI_SCK 13
#define SPI_DI 12
#define SPI_DO 11
#define SD_CS 4 // CS-контакт для SD-карты
//#define TFT_CS 10 // CS-контакт для TFT-дисплея
//#define TFT_DC 9 // DC-контакт для дисплея (для переключения между передачей данных и команд)
//#define TFT_RST 8 // RESET-контакт для TFT-дисплея (или подключите к +5V)
// это контакты для подключения TFT-платы:
#define TFT_CS 10
#define TFT_DC 8
#define TFT_RST 0 // этот контакт также можно подключить к RESET-контакту Arduino
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, SPI_DO, SPI_SCK, TFT_RST);
void setup(void) {
Serial.begin(9600);
// С 10 января 2012 года поставщик начал отправлять Adafruit
// чуть измененные 1,8-дюймовые дисплеи, из-за чего выравнивание
// экрана сдвинулось на несколько пикселей. Это значит, что для
// таких дисплеев нужно использовать другой код инициализации.
// О том, какой именно код нужен, сообщает цвет ярлыка, приклеенного
// к дисплею. Если верхний и левый края дисплея обрезаны или если
// там есть дополнительные «случайные» пиксели, попробуйте другой
// вариант. Если видите инверсию красного и зеленого цветов,
// попробуйте вариант с черным ярлычком.
Serial.print("Initializing SD card..."); // "Инициализация SD-карты... "
if (!SD.begin(SD_CS, SPI_DO, SPI_DI, SPI_SCK)) {
Serial.println("failed!"); // "не удалась"
return;
}
Serial.println("OK!");
bmpDraw("parrot.bmp", 0, 0);
}
void loop() {
}
// Эта функция открывает 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;
}