Arduino:Примеры/soft spitftbitmap

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

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


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

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

Код

  1  
  2 /***************************************************
  3 
  4 Вывод изображения на 1,8-дюймовый TFT-дисплей
  5 
  6 Этот скетч предназначен для 1,8-дюймового TFT-дисплея, 
  7 работающего через SPI-интерфейс (от Adafruit).
  8 
  9 Для этого скетча подойдет 1,8-дюймовая TFT-плата с SD-картой...
 10 ----> http://www.adafruit.com/products/358
 11 ...а также «голый» 1,8-дюймовый TFT-дисплей
 12 ----> http://www.adafruit.com/products/618
 13 
 14 Руководства и схемы подключения ищите по ссылкам выше. Этим дисплеям 
 15 для коммуникации требуется SPI-интерфейс с 4 или 5 контактами (контакт 
 16 RST опционален). 
 17 
 18 Adafruit инвестировала время и ресурсы, создавая эту библиотеку с 
 19 открытым кодом. Пожалуйста, поддержите Adafruit и оборудование с 
 20 открытым кодом, покупая продукты Adafruit!
 21 
 22 Библиотека написана Лимор Фрид (Limor Fried, Ladyada) для Adafruit 
 23 Industries. Весь текст выше должен быть включен при любом повторном 
 24 распространении.
 25 
 26 ****************************************************/
 27 
 28 #include <Adafruit_GFX.h>    // подключаем графическую библиотеку
 29 #include <Adafruit_ST7735.h> // подключаем библиотеку для управления дисплеем
 30 #include <SPI.h>
 31 #include <SD.h>
 32 
 33 #if defined(__SAM3X8E__)
 34     #undef __FlashStringHelper::F(string_literal)
 35     #define F(string_literal) string_literal
 36 #endif
 37 
 38 // Коммуникация будет вестись через аппаратный SPI-интерфейс,
 39 // и им будут пользоваться сразу два устройства: TFT-дисплей
 40 // и SD-карта. На платах Arduino аппаратный SPI всегда находится на
 41 // одних и тех же контактах, и переназначить их нельзя. 
 42 // На Arduino Uno, Duemilanove и т.д. за SPI отвечают следующие 
 43 // контакты: 11-ый контакт – MOSI, 12-ый контакт – MISO,
 44 // 13-ый контакт – SCK.
 45 #define SPI_SCK 13
 46 #define SPI_DI  12
 47 #define SPI_DO  11
 48 
 49 #define SD_CS    4    // CS-контакт для SD-карты
 50 //#define TFT_CS  10  // CS-контакт для TFT-дисплея
 51 //#define TFT_DC   9  // DC-контакт для дисплея (для переключения между передачей данных и команд)
 52 //#define TFT_RST  8  // RESET-контакт для TFT-дисплея (или подключите к +5V)
 53 
 54 // это контакты для подключения TFT-платы:
 55 #define TFT_CS   10
 56 #define TFT_DC   8
 57 #define TFT_RST  0  // этот контакт также можно подключить к RESET-контакту Arduino
 58 
 59 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, SPI_DO, SPI_SCK, TFT_RST);
 60 
 61 void setup(void) {
 62   Serial.begin(9600);
 63 
 64   // С 10 января 2012 года поставщик начал отправлять Adafruit 
 65   // чуть измененные 1,8-дюймовые дисплеи, из-за чего выравнивание
 66   // экрана сдвинулось на несколько пикселей. Это значит, что для 
 67   // таких дисплеев нужно использовать другой код инициализации.
 68   // О том, какой именно код нужен, сообщает цвет ярлыка, приклеенного
 69   // к дисплею. Если верхний и левый края дисплея обрезаны или если
 70   // там есть дополнительные «случайные» пиксели, попробуйте другой    
 71   // вариант. Если видите инверсию красного и зеленого цветов,
 72   // попробуйте вариант с черным ярлычком.
 73 
 74   Serial.print("Initializing SD card...");  //  "Инициализация SD-карты... "
 75   if (!SD.begin(SD_CS, SPI_DO, SPI_DI, SPI_SCK)) {
 76     Serial.println("failed!");   //  "не удалась"
 77     return;
 78   }
 79   Serial.println("OK!");
 80 
 81   bmpDraw("parrot.bmp", 0, 0);
 82 }
 83 
 84 void loop() {
 85 }
 86 
 87 // Эта функция открывает BMP-файл и печатает его на заданных 
 88 // координатах. Ее работу можно ускорить, считывая сразу несколько 
 89 // пикселей, а не пиксель за пикселем. Для этого нужно увеличить 
 90 // размер буфера, однако это, в свою очередь, потребует ресурсов 
 91 // RAM-памяти Arduino. Разумным компромиссом будет 20 пикселей.
 92 
 93 #define BUFFPIXEL 20
 94 
 95 void bmpDraw(char *filename, uint8_t x, uint8_t y) {
 96 
 97   File     bmpFile;
 98   int      bmpWidth, bmpHeight;   // ширина и высота в пикселях 
 99   uint8_t  bmpDepth;              // глубина цвета (по умолчанию должно стоять 24)
100   uint32_t bmpImageoffset;        // место, с которого в файле начинается само изображение
101   uint32_t rowSize;               // это значение не всегда будет равно bmpWidth; возможно, будет отступ 
102   uint8_t  sdbuffer[3*BUFFPIXEL]; // буфер для пикселей (значения R, G и B для каждого пикселя)
103   uint8_t  buffidx = sizeof(sdbuffer); // текущая позиция в буфере для пикселей
104   boolean  goodBmp = false;       // если скетч определит нужный тип заголовка, он выставит здесь «true»
105   boolean  flip    = true;        // данные BMP-файла хранятся по принципу «снизу вверх»
106   int      w, h, row, col; 
107   uint8_t  r, g, b;
108   uint32_t pos = 0, startTime = millis();
109 
110   if((x >= tft.width()) || (y >= tft.height())) return;
111 
112   Serial.println();
113   Serial.print("Loading image '");  //  "Загрузка изображения «"
114   Serial.print(filename);
115   Serial.println('\'');
116 
117   // открываем на SD-карте запрашиваемый файл:
118   if ((bmpFile = SD.open(filename)) == NULL) {
119     Serial.print("File not found");  //  "Файл не найден"
120     return;
121   }
122 
123   // анализируем заголовок BMP-Файла:
124   if(read16(bmpFile) == 0x4D42) { // сигнатура BMP-файла
125     Serial.print("File size: ");  //  "Размер файла: "
126     Serial.println(read32(bmpFile));
127     (void)read32(bmpFile); // считываем и игнорируем байты, вписанные при создании файла
128     bmpImageoffset = read32(bmpFile); // место, с которого начинается в файле само изображение (смещение)
129     Serial.print("Image Offset: ");  //  "Смещение: "
130     Serial.println(bmpImageoffset, DEC);
131     // считываем заголовок DIB:
132     Serial.print("Header size: ");  //  "Размер заголовка: "
133     Serial.println(read32(bmpFile));
134     bmpWidth  = read32(bmpFile); 
135     bmpHeight = read32(bmpFile);
136     if(read16(bmpFile) == 1) {  //  количество цветовых плоскостей – должно быть «1»
137       bmpDepth = read16(bmpFile);  // биты на пиксель
138       Serial.print("Bit Depth: ");  //  "Глубина цвета: "
139       Serial.println(bmpDepth);
140       if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // «0» – это без сжатия
141 
142         goodBmp = true; // поддерживаемый формат BMP – продолжаем дальше!
143         Serial.print("Image size: ");  //  "Размер изображения: "
144         Serial.print(bmpWidth);
145         Serial.print('x');
146         Serial.println(bmpHeight);
147 
148         // если нужно, границы BMP-изображения будут иметь 4-байтный отступ:
149         rowSize = (bmpWidth * 3 + 3) & ~3;
150 
151         // Если значение в bmpHeight отрицательное, то данные 
152         // изображения хранятся по принципу «сверху вниз»
153         // Это не канон, но иногда встречается. 
154         if(bmpHeight < 0) {
155           bmpHeight = -bmpHeight;
156           flip      = false;
157         }
158 
159         //  загружаем обрезанную область: 
160         w = bmpWidth;
161         h = bmpHeight;
162         if((x+w-1) >= tft.width())  w = tft.width()  - x;
163         if((y+h-1) >= tft.height()) h = tft.height() - y;
164 
165         // задаем адресный промежуток для границ обрезанного изображения: 
166         tft.setAddrWindow(x, y, x+w-1, y+h-1);
167 
168         for (row=0; row<h; row++) { // для каждого пиксельного ряда...
169 
170           // Ищем начало пиксельного ряда. Возможно, делать это для 
171           // каждой строки – слишком трудоемко, однако эта функция 
172           // выполняет много «грязной работы» вроде обрезки 
173           // изображения и создания отступа для нового пиксельного 
174           // ряда. Кроме того, этот поиск имеет место только в том 
175           // случае, если нужно поменять позицию файла (это позволяет 
176           // избежать кластерных расчетов в библиотеке SD).
177 
178           if(flip) //  данные BMP-файла хранятся по принципу «снизу вверх» (нормальный BMP) 
179             pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
180           else     //  BMP-файл хранится по принципу «сверху вниз»
181             pos = bmpImageoffset + row * rowSize;
182           if(bmpFile.position() != pos) { // нужно ли запустить поиск?
183             bmpFile.seek(pos);
184             buffidx = sizeof(sdbuffer); // перезагружаем буфер
185           }
186 
187           for (col=0; col<w; col++) { // для каждого пикселя...
188             // настало время считывать новые пиксельные данные?        
189             if (buffidx >= sizeof(sdbuffer)) { // да!
190               bmpFile.read(sdbuffer, sizeof(sdbuffer));
191               buffidx = 0; // выставляем индекс на начало 
192             }
193 
194             // конвертируем пиксели из формата BMP в формат TFT,
195             // а затем выводим их на дисплей:
196             b = sdbuffer[buffidx++];
197             g = sdbuffer[buffidx++];
198             r = sdbuffer[buffidx++];
199             tft.pushColor(tft.Color565(r,g,b));
200           } // последний пиксель
201         } // последняя строка пикселей 
202         Serial.print("Loaded in ");  //  "Картинка загрузилась за "
203         Serial.print(millis() - startTime);
204         Serial.println(" ms");  // "миллисекунд"
205       } // конец goodBMP
206     }
207   }
208 
209   bmpFile.close();
210   if(!goodBmp) Serial.println("BMP format not recognized.");  //  "Формат BMP не обнаружен" 
211 }
212 
213 // Эти функции считывают из BMP-файла 16-битные и 32-битные данные.
214 // Данные BMP-файла, как и у Arduino, хранятся по принципу 
215 // «от младшего к старшему». При портировании куда-либо этот порядок, 
216 // возможно, придется поменять.
217 
218 uint16_t read16(File f) {
219   uint16_t result;
220   ((uint8_t *)&result)[0] = f.read(); // самый младший бит
221   ((uint8_t *)&result)[1] = f.read(); // самый старший бит
222   return result;
223 }
224 
225 uint32_t read32(File f) {
226   uint32_t result;
227   ((uint8_t *)&result)[0] = f.read(); // самый младший бит
228   ((uint8_t *)&result)[1] = f.read();
229   ((uint8_t *)&result)[2] = f.read();
230   ((uint8_t *)&result)[3] = f.read(); // самый старший бит
231   return result;
232 }

См.также

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