image2cpp - Resimden C Dizisi Oluşturucu (Mono ve Renkli)
image2cpp (LCD Assistant), bir resmi C dizisine veya bayt dizisine çeviren, elinizdeki diziyi de tekrar resme döndüren ücretsiz bir araçtır. Arduino, ESP32 ve Raspberry Pi projelerinde kullanılan iki ekran tipini de kapsar: 1 bit tek renkli (SSD1306 / SH1106 OLED ve Nokia 5110) ve RGB565 veya RGB888 renkli (ST7735, ST7789, ILI9341, ILI9488 TFT paneller). Çıktı olarak Adafruit GFX, U8g2, düz bayt veya MicroPython / CircuitPython seçebilirsiniz.
1. Fotoğraf seç
veya
1. Düz Bayt dizisi yapıştır
2. Fotoğraf Ayarları
3. Çıktı
LovyanGFX.hpp başlığı ve hazır bir lcd.pushImage(0, 0, genişlik, yükseklik, dizi) satırıyla birlikte üretir. Renkli RGB565 çizim moduyla birlikte kullanın; renkler ters çıkarsa çıktıdaki lcd.setSwapBytes(true) satırını açıp kapatın.bytearray formatında çıktı oluşturur.GFXbitmapFont formatında bir çıktı oluşturur. Değiştirilmiş bir versiyonuyla kullanılan Adafruit GFX kütüphanesine aittir. GitHub projesi ve örnek kod bitmap-fontHer fotoğraf için 1 karakter uzunluğunda bir glif tanımlayıcısı sağlanmazsa, ilk ASCII karakter değeri kullanılır. Değer, her glif için 1 artırılır.
Aşağıdaki fotoğraf gibi fotoğrafınız ekranınızda bozuk görünüyorsa, farklı bir mod kullanın.
tft.setSwapBytes(true) satırını ekleyin ya da kaldırın, Adafruit GFX drawRGBBitmap() ise standart bayt sırasını bekler. Bu, RGB565 dizilerinde gerçek hayatta en sık düşülen hatadır.Monokrom bitmap dizisi aslında nedir?
Monokrom bir bitmap piksel başına tek bit tutar: 1 pikselin yandığı, 0 söndüğü anlamına gelir. Ne gri ton vardır ne renk. 128x64 bir OLED'in tüm ekranının yalnızca 1.024 bayt tutmasının sebebi budur. Dolayısıyla bir resmi bitmap dizisine çevirmek iki karara indirgenir ve ekranda bozuk çıkan görüntülerin neredeyse tamamı bu iki karardan birinin yanlış verilmesinden doğar: hangi sekiz piksel aynı bayta paketlenecek, ve o baytın içindeki bitler hangi sırayla sayılacak.
Yatay bayt sırası, her seferinde bir satır
Yatay modda dönüştürücü resmi soldan sağa, yukarıdan aşağıya tarar ve aynı satırdaki sekiz komşu pikseli tek bayta paketler. Gruptaki en soldaki piksel en anlamlı bite (MSB) düşer. 128 pikselik bir satır 16 bayt olur, bir sonraki satır hemen ardından başlar; yani dizi, resmin bir metin sayfası gibi soldan sağa okunmuş hâlidir. Adafruit_GFX drawBitmap() ve U8g2 drawXBMP() tam olarak bunu bekler.
Bir ayrıntı neredeyse herkesi yakalar: satır her zaman bayt sınırında biter. Resim 60 piksel genişse her satır yine 8 bayt yer kaplar ve son 4 bit dolgudur. drawBitmap() çağrısına genişlik olarak 60 verdiğinizde kütüphane, satır başına 64 bit saklayan bir diziden 60 bit okur; her satır 4 piksel daha kayar ve resim çapraz biçimde eğrilir. Monokrom genişlikleri 8'in katı tutmak bu hata sınıfının tamamını ortadan kaldırır.
Dikey (sayfa) bayt sırası, her seferinde sekiz satır
Dikey modda bir bayt, tek bir sütunda üst üste duran sekiz pikseli tutar; en üstteki piksel en anlamsız bite (LSB) düşer. Resim sekiz piksel yüksekliğinde yatay şeritlere, yani sayfalara bölünür ve her sayfa ekran boyunca uzanan bir bayt dizisi olarak saklanır.
Donanımı görene kadar bu düzen keyfi görünür. SSD1306 denetleyicisinin kendi ekran belleği tam olarak böyle dizilmiştir: 128x64 bir panel, 128 baytlık 8 sayfadır ve I2C üzerinden yazılan tek bir bayt, tek işlemde 8 pikselden oluşan dikey bir sütunu yakar. Nokia 5110 (PCD8544) da aynı mantıkla çalışır. Dikey mod, diziyi mikrodenetleyicide hiç yeniden paketlemeden doğrudan o belleğe boşaltabilesiniz diye vardır. Yatay mod ise bir çizim kütüphanesinin satır mantığına uyduğu için vardır; bitmap'i kırpabilmesini ve sayfa sınırına takılmadan istediği y koordinatına yerleştirebilmesini sağlayan şey odur.
| Bayt sırası | Bir bayt neyi tutar | Bit 7 (MSB) | 128x64 dizi | Kullanan |
|---|---|---|---|---|
| Yatay, MSB önce | Aynı satırdan 8 piksel | gruptaki en soldaki piksel | 1.024 bayt | Adafruit_GFX drawBitmap(), MicroPython MONO_HLSB |
| Yatay, LSB önce (XBM) | Aynı satırdan 8 piksel | gruptaki en sağdaki piksel | 1.024 bayt | U8g2 drawXBM(), .xbm dosyaları |
| Dikey (sayfa) | Aynı sütundan 8 piksel | gruptaki en alttaki piksel | 1.024 bayt | ham SSD1306 / SH1106 / PCD8544 tamponları |
Hangi çıktı biçimini seçmeliyim?
Çizim modu, piksellerin baytlara nasıl paketleneceğini belirler. Zevke göre değil, ekran sürücüsüne göre seçin; yoksa resim dilimlenmiş veya aynalanmış çıkar.
| Çizim modu | Bayt / piksel | Tipik ekranlar | Çizim çağrısı |
|---|---|---|---|
| Mono, yatay | 1 bit | SSD1306, SH1106, SSD1309 OLED | drawBitmap() / drawXBM() |
| Mono, dikey | 1 bit | Nokia 5110 (PCD8544), SSD1306 sayfa modu | ham tampon yazma |
| Renkli RGB565 | 2 bayt | ST7735, ST7789, ILI9341, ILI9488, GC9A01 | drawRGBBitmap() / pushImage() |
| Alfa maskesi | 1 bit | renkli bitmap yanında saydamlık maskesi olarak | maskeli blit |
| Renkli RGB888 | 3 bayt | 24 bit çerçeve tamponları, ESP32 RGB LCD panelleri | panel sürücüsü |
| Renkli HSV | 3 bayt | LED matrisler, çalışma anında ton kaydıran efektler | özel |
Diziyi kodunuzda kullanmak
Tek renkli OLED, Adafruit SSD1306:
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1);
// yukarıdaki Çıktı kutusundaki diziyi yapıştırın
const unsigned char myBitmap [] PROGMEM = { /* ... */ };
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.drawBitmap(0, 0, myBitmap, 128, 64, SSD1306_WHITE);
display.display();
}U8g2 ile tek renkli (XBM bayt sırası):
u8g2.firstPage();
do {
u8g2.drawXBMP(0, 0, 128, 64, myBitmap);
} while (u8g2.nextPage());ST7735 / ILI9341 üzerinde renkli RGB565, Adafruit GFX:
#include <Adafruit_ILI9341.h>
// RGB565 çıktısı piksel başına 2 bayt, bu yüzden unsigned char değil uint16_t
const uint16_t myImage [] PROGMEM = { /* ... */ };
tft.drawRGBBitmap(0, 0, myImage, 160, 128);TFT_eSPI ile renkli RGB565 (daha hızlı, ESP32 için ideal):
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
tft.setSwapBytes(true); // renkler ters görünüyorsa bu satırı kaldırın
tft.pushImage(0, 0, 160, 128, myImage);Adafruit GFX, U8g2, ham SSD1306 ve klasik LCD Assistant
Bu dördü aynı pikselleri tüketir ama aynı baytları tüketmez. Gerçekte hangi çağrıyı yapıyorsanız ona uyan çıktıyı üretin.
Adafruit_GFX drawBitmap: yatay, MSB önce, PROGMEM
drawBitmap(), Adafruit_GFX'ten türeyen her ekran kütüphanesinin ortak çağrısıdır; bu yüzden tek bir dizi SSD1306 OLED'de de, SH1106'da da, monokrom e-ink panelde de çalışır. Baytları yatay ve MSB önce okur, üstelik bunu pgm_read_byte() üzerinden yapar. Dizinin AVR kartlarda PROGMEM ile tanımlanması gerekmesinin sebebi budur. Arduino Uno'da PROGMEM'i unutursanız tam ekran bir bitmap flash yerine RAM'in yarısını yer. ESP32 ve RP2040'ta PROGMEM zaten hiçbir şey yapmaz ve zarar vermez, taşınabilirlik için bırakın. Son parametre renktir; ön plan rengi yerine arka plan rengini geçmek, şekli ekranda hâlihazırda duran görüntünün içinden oyup çıkarır.
// Çizim modu: Mono - Yatay, çıkış formatı: Arduino kodu
const unsigned char logo [] PROGMEM = { 0x00, 0x7e, 0x81, /* ... */ };
display.clearDisplay();
display.drawBitmap(0, 0, logo, 128, 64, SSD1306_WHITE); // yanan pikseller
display.drawBitmap(0, 0, logo, 128, 64, SSD1306_BLACK); // şekli oyarak çiz
display.display();U8g2 drawXBMP ve herkesi düşüren XBM bit sırası
U8g2 bitmap'leri XBM biçiminde, yani eski X11 bitmap biçiminde çizer. XBM ise baytın içindeki bitleri ters yönde numaralar: bit 0 en soldaki pikseldir, en sağdaki değil. Baytlar hâlâ yataydır, dizinin uzunluğu da tıpatıp aynıdır; yalnızca her baytın bitleri ters çevrilmiştir. GFX için üretilmiş bir diziyi drawXBMP()'ye verirseniz genel şekil ayakta kalır ama her 8 piksellik grup kendi içinde aynalanır; sonuç, resminizin ince ince doğranıp yeniden yapıştırılmış hâli gibi görünür. Çıktı bölümündeki Bitleri bayt olarak değiştirin kutusu tam olarak bunun içindir: U8g2 için işaretleyin, Adafruit_GFX için boş bırakın.
// Çizim modu: Mono - Yatay, "Bitleri bayt olarak değiştirin" işaretli
static const unsigned char logo_xbm[] U8X8_PROGMEM = { 0x00, 0x7e, 0x81, /* ... */ };
u8g2.firstPage();
do {
u8g2.drawXBMP(0, 0, 128, 64, logo_xbm);
} while (u8g2.nextPage());drawXBMP() PROGMEM sürümüdür, drawXBM() ise zaten RAM'de duran bir dizi içindir. U8g2'de drawBitmap() adında bir metot da vardır ama o Adafruit'in çağrısı değildir ve aynı parametreleri almaz; alışkanlıkla birini diğerinin yerine yazmayın.
Ham SSD1306 sayfa modu, tamponu kendiniz yazmak
Hiç grafik kütüphanesi kullanmıyorsanız, örneğin çıplak bir I2C sürücüsüyle veya ESP-IDF üzerinde çalışıyorsanız, paneli sayfa ve sütun adresiyle hedefler ve baytları doğrudan belleğine gönderirsiniz. Bunun için Mono - Dikey çizim modunu seçin: dizi o zaman zaten denetleyicinin kendi düzeninde olur ve aktarım, mikrodenetleyicide hiç bit oynatmadan düz bir kopyalamaya döner.
// Çizim modu: Mono - Dikey. 128x64 = 128 baytlık 8 sayfa
const uint8_t frame[1024] = { /* ... */ };
for (uint8_t page = 0; page < 8; page++) {
ssd1306_cmd(0xB0 | page); // sayfa başlangıç adresi
ssd1306_cmd(0x00); // alt sütun adresi = 0
ssd1306_cmd(0x10); // üst sütun adresi = 0
ssd1306_data(&frame[page * 128], 128);
}Klasik Windows programı LCD Assistant
LCD Assistant, tarayıcılar bu işi yapamadan çok önce, 2000'lerin sonunda yazılmış küçük bir Windows masaüstü programıdır. Monokrom bir BMP dosyası açar ve C tablosu yazar; yatay veya dikey bayt sırası ile 8 ya da 16 bitlik tablo genişliği sunar. Çıktısı bugünün Arduino kütüphanelerinin istediğinden üç noktada ayrılır. Yalnızca hâlihazırda 1 bit olan BMP kabul eder, yani resmi önce bir editörde eşiklemeniz gerekir. Varsayılanı dikey sıradır, oysa drawBitmap() için yanlış olan sıra odur. Ve çıplak bir const unsigned char tablosu üretir, PROGMEM niteleyicisini AVR'de siz elle eklemek zorundasınız. Ayrıca dithering yoktur, benzetilmiş panel üzerinde önizleme yoktur ve elinizdeki bir diziyi geri resme çevirme imkânı yoktur.
Buraya LCD Assistant indirmek için geldiyseniz indirmenize gerek yok. Bu sayfa aynı dönüşümü tarayıcıda yapar, yalnızca 1 bit BMP yerine PNG, JPG, BMP, GIF ve WebP dosyalarını doğrudan kabul eder ve sonucu koda yapıştırmadan önce benzetilmiş bir OLED üzerinde gösterir.
Hex kodu mu, C dizisi mi, XBM mi?
Burada piksellerin paketlenmesi ile metnin biçimlenmesi iki ayrı karardır. Çizim modu baytları belirler, Kod çıkış formatı ise o baytların nasıl yazdırılacağını. Aranan neredeyse her şeyi üç biçim karşılar.
- C dizisi. Arduino kodu seçeneği,
const unsigned char isim [] PROGMEM = { 0x00, 0xFF, ... };şeklinde eksiksiz bir tanım verir; doğrudansetup()üstüne yapıştırılır. Adafruit_GFX ve U8g2 için istediğiniz budur. - Düz hex. Düz bayt (plain bytes) seçeneği C tanımını atar, geriye yalnızca bayt değerleri kalır; bunlar bir Python listesine, bir JSON dosyasına, bir struct başlatıcısına veya bambaşka bir dile olduğu gibi girer. Üstüne Çıktıdan '0x' ve virgülleri kaldır kutusunu da işaretlerseniz çıplak bir hex dizesi elde edersiniz; pratikte "bitmap hex kodu" derken kastedilen budur.
- XBM. XBM, farklı bir bayt paketlemesinden çok, farklı bir bit sırası ve etrafına sarılmış bir C tablosudur. Mono - Yatay seçin, Bitleri bayt olarak değiştirin kutusunu işaretleyin; elde ettiğiniz dizi XBM sıralamasındadır ve
drawXBMP()'ye doğrudan girer. Gerçek bir.xbmdosyası ise aynı tablonun üstüne genişlik ve yükseklik için iki#definesatırı eklenmiş hâlidir.
MicroPython / CircuitPython seçeneği aynı monokrom baytları framebuf.FrameBuffer(..., framebuf.MONO_HLSB) için bir bytearray içine sarar; bu da yatay MSB önce düzeninin başka bir addaki hâlidir. Binary .bin indirmesi ise hiç metin biçimlendirmesi olmayan ham baytlardır, SD karttan veya SPIFFS'ten akıtmak için.
Bitmap bozuk, aynalı, ters veya döndürülmüş çıkıyorsa
Bozuk çıkan bitmap'lerin neredeyse tamamı birkaç hatadan biridir ve hangisi olduğunu belirti söyler. Kodunuzda bir şey değiştirmeden önce bu tabloyu yukarıdan aşağıya tarayın.
| Gördüğünüz | Sebebi | Çözümü |
|---|---|---|
| Resim 8 piksellik yatay şeritlere bölünmüş, her şerit yana doğru sürünmüş | Bayt sırası yanlış: dikey veri yatay çizim çağrısına gitmiş veya tersi | Çizim modunu Mono - Yatay ile Mono - Dikey arasında değiştirip kodu yeniden üretin |
| Şekil tanınıyor ama her 8 piksellik grup kendi içinde soldan sağa aynalanmış, ince doğranmış gibi | Bit sırası yanlış: MSB önce veri XBM çağrısına gitmiş veya tersi | Bitleri bayt olarak değiştirin kutusunu değiştirin. U8g2 drawXBM için işaretli, Adafruit_GFX drawBitmap için boş |
| Resim her satırda biraz daha yana kayıyor, sonunda çapraz bir eğrilme çıkıyor | Genişlik uyuşmazlığı, çoğunlukla 8'in katı olmayan bir genişlik | Tuval genişliğini 8'in katı yapın ve aynı genişliği çizim çağrısına da geçin |
| Her şey negatif: arka plan yanıyor, konu sönük | Panel 1'i sönük sayıyor ya da kaynak resmin arka planı koyu | Fotoğraf Ayarları'ndaki Fotoğraf renklerini tersine çevir kutusunu işaretleyin veya SSD1306_BLACK ile çizin |
| Resmin tamamı 90 derece dönmüş veya blok hâlinde aynalanmış | Panel yönelimi, dizi değil | Buradaki Fotoğrafı döndür ve Fotoğrafı çevir ayarlarını kullanın ya da kütüphanede setRotation() çağırın |
| Yalnızca ekranın en üst sekizde birinde bir şeyler var | Dizi kareden kısa, çoğunlukla 128x8 çıktının 128x64 sanılması | Tuval boyutunu kontrol edin ve dizi uzunluğunun genişlik / 8 * yükseklik olduğunu doğrulayın |
| Boş ekran veya rastgele gürültü | Dizi kütüphanenin beklediği yerde değil ya da işaretçi tipi yanlış | AVR'de PROGMEM ekleyin, RGB565 dizilerini unsigned char değil uint16_t tutun |
Sorunu hızlıca ayırmanın yolu: diziyi aynı genişlik, yükseklik ve çizim moduyla sayfanın üstündeki Düz Bayt dizisi yapıştır kutusuna geri yapıştırın. Buradaki önizleme doğru görünüyor ama panel görünmüyorsa dizi sağlamdır, hata kodunuzdadır. Önizleme de bozuksa dışa aktarma ayarları yanlıştır.
Ne kadar bellek tutar?
1 bit bitmap genişlik / 8 * yükseklik bayt tutar. RGB565 bitmap ise genişlik * yükseklik * 2. Aradaki fark hangi kartı kullanabileceğinizi belirler:
- 128x64 tek renkli OLED: 1.024 bayt. Arduino Uno flash'ına düşünmeden sığar.
- 160x128 RGB565 (ST7735): 40.960 bayt. Uno'nun 32 KB'lık tüm flash'ından zaten büyük. ESP32, RP2040 kullanın veya SD karttan akıtın.
- 320x240 RGB565 (ILI9341): 153.600 bayt. Sadece ESP32 veya SD kart.
Renkli resim sığmıyorsa Floyd-Steinberg dithering ile 1 bite çevirin. Küçük panelde dithering'li hâli, aşırı küçültülmüş renkli hâlinden genelde daha okunaklı görünür.
Yaygın bitmap boyutlarının flash maliyeti
1 bit bir resimde hesap genişlik / 8 * yükseklik'tir, genişliği 8'in bir üst katına yuvarlayarak. Kartların flash bütçesine vurulduğunda tablo şöyle çıkıyor:
| Bitmap | Bayt | Uno'nun 32 KB flash'ında payı | ESP32'nin 4 MB flash'ında payı |
|---|---|---|---|
| 32x32 simge, 1 bit | 128 | %0,4 | ihmal edilebilir |
| 84x48 tam ekran, Nokia 5110 | 528 | %1,6 | ihmal edilebilir |
| 128x32 tam ekran, küçük OLED | 512 | %1,6 | ihmal edilebilir |
| 128x64 tam ekran, standart OLED | 1.024 | %3,1 | %0,02 |
| 160x128 RGB565, ST7735 | 40.960 | sığmaz | %1,0 |
| 320x240 RGB565, ILI9341 | 153.600 | sığmaz | %3,7 |
Pratik okuması şu: Uno'da bir açılış ekranı ve birkaç simge kaldırabilirsiniz, bir simge kütüphanesi kaldıramazsınız; üstelik bootloader ile SSD1306 sürücüsü siz daha ilk bitmap'i eklemeden birkaç KB'ı almış olur. Yirmi adet 32x32 simge toplamda 2.560 bayt tutar ve genellikle tek bir tam ekran resimden daha iyi bir yatırımdır. ESP32'de ise flash sorun olmaktan çıkar, soru RAM'e döner: Adafruit_SSD1306 her hâlükârda RAM'de 1.024 baytlık bir çerçeve tamponu tutar, buna karşılık 320x240 RGB565 tam bir çerçeve tamponu 150 KB olurdu. Renkli panellerin tamamı tamponlanmak yerine şerit şerit çizilmesinin sebebi budur.
Panel hazır ayarları ve canlı flash göstergesi
Tuval boyutunu elle yazmak yerine 2. bölümdeki Panel hazır ayarı menüsünden panelinizi seçebilirsiniz. Listede SSD1306'nın 128x64 ve 128x32 hâlleri, Nokia 5110 için 84x48, ST7735 için 128x160 ve 128x128, ST7789 için 240x240, 240x320 ve 135x240, yuvarlak GC9A01 için 240x240, ILI9341 için 240x320 ve ILI9488 için 320x480 var. Seçim, yüklü her fotoğrafın genişlik ve yükseklik alanını doğrudan doldurur; henüz fotoğraf yoksa değerler bayt yapıştırma kutusunun boyut alanlarına yazılır. Doğru çözünürlükle başlamak, sonradan ekranda ortaya çıkan kayma ve kırpılma hatalarının çoğunu baştan siler.
4. bölümdeki Flash maliyeti satırı, seçili çizim moduna göre sonucun kaç bayt tutacağını anında gösterir; tuval boyutunu, çizim modunu veya indeksli palet kutusunu her değiştirdiğinizde rakam yenilenir. Değer 30 KB'ı aştığında Arduino Uno uyarısı çıkar, çünkü 32 KB'lık flash'ın bootloader ve kütüphanelerden geriye bıraktığı pay bu kadardır. 1,3 MB'ı aştığında ikinci uyarı gelir: varsayılan ESP32 uygulama bölümü o civardadır ve sketch de aynı bölüme sığmak zorundadır. İki eşiğin arasındaki her şey ESP32, ESP8266 veya RP2040 için rahat, Uno için değildir.
Kaynak resmi hazırlamak: eşikleme mi, dithering mi?
1 bit bir ekranda piksel başına iki durum vardır, dolayısıyla giren her pikselin ya açık ya kapalı olması gerekir. Bu kararı vermenin iki yolu var ve doğrusunu seçmek, kütüphanedeki hiçbir ayardan daha çok fark yaratır.
Eşikleme, Titreme menüsündeki Binary seçeneğidir. Her pikselin parlaklığını Parlaklık / alfa eşiği değeriyle (varsayılan 128) karşılaştırır ve yuvarlar. Keskin kenarlı her şey için doğru seçim budur: logolar, çizgi grafikler, simgeler, yazı, QR kodları, şema sembolleri. Sonuç nettir ve panel küçüldükçe de net kalır. Logonuz kayboluyor ya da tamamen dolu bir lekeye dönüşüyorsa dithering'e sarılmadan önce eşiği oynatın.
Dithering, açık ve kapalı pikselleri bir desen hâlinde serpiştirerek bölgenin ortalama parlaklığını korur; yani çözünürlüğü verip görünürde gri kazanır. Floyd-Steinberg yuvarlama hatasını komşu piksellere dağıtır ve en fotoğrafımsı sonucu verir. Orijinal Macintosh'un algoritması olan Atkinson ise hatanın bir kısmını bilerek atar; uçlarda biraz ayrıntı kaybeder ama orta tonları daha temiz ve daha az gürültülü bırakır, küçük bir OLED'de genellikle daha iyi görünür. Bayer sabit bir matris kullanır; gözle görülür bir kareleme deseni bırakır ama kareden kareye kararlıdır, animasyon yapıyorsanız bu önemlidir.
Fotoğrafların neden nadiren tuttuğuna gelince: 128x64 bir OLED'de 8.192 piksel vardır, kabaca bir telefon küçük resminin yirmide biri kadar; dithering ise tek bir gri tonu ima etmek için o piksellerden birkaçını harcar. O boyutta bir yüz dokuya dönüşür. Tek bir konuya sıkıca kırpın, çevirmeden önce Kontrast kaydırıcısını yukarı çekin ve okunabilir sonucun çoğu zaman fotoğrafın kendisi değil, silueti olacağını kabul edin. Eşiklemeden önce kontrastı yükseltmek, dithering algoritması değiştirmekten çok daha fazla resmi kurtarır.
image2cpp nedir, bu sürümün farkı ne?
image2cpp, Jasper van Loenen'in yazdığı, GitHub'da açık kaynak olarak yayımlanan ve javl.github.io/image2cpp adresinde barınan, tarayıcıda çalışan bir resimden C dizisi dönüştürücüsüdür. Bir resmi Arduino bitmap'ine çevirmenin varsayılan cevabı hâline gelmesinin sebebi basit: kurulum istemiyor, tamamen istemcide çalışıyor ve SSD1306 dünyasının gerçekten kullandığı yatay ile dikey bayt sıralarının ikisini de biliyor. Bir eğitim videosunda "resmi image2cpp ile çevirin" dendiğinde kastedilen araç odur; bu sayfa da aynı açık kaynak çekirdeğin üzerine kurulmuştur ve altbilgide kaynağı belirtilmiştir.
O çekirdeğin iyi yaptığı her şey burada korunuyor: dönüşüm yerelde oluyor ve hiçbir dosya yüklenmiyor, birden fazla resim sıraya alınabiliyor, hem düz bayt hem Arduino çıktısı var ve elinizdeki bir dizi geri resme çözülebiliyor. Üzerine eklenenler ise şunlar:
- Renkli çıktı. RGB565, RGB888, HSV ve 1 bit alfa maskesi; böylece aynı sayfa yalnızca monokrom OLED'lere değil, ST7735, ST7789, ILI9341, ILI9488 ve GC9A01 panellerine de hizmet ediyor.
- Daha fazla dithering. Düz eşiklemenin yanında Floyd-Steinberg, Atkinson ve Bayer; üstüne dönüşümden önce uygulanan parlaklık ve kontrast kaydırıcıları.
- Gerçekçi önizleme. Sonuç, klasik yeşil LCD, mavi OLED ve sarı-üstü-mavi OLED dahil benzetilmiş bir panel üzerinde gösteriliyor; hiçbir kablo bağlamadan okunabilirliğe karar verebiliyorsunuz.
- MicroPython ve CircuitPython çıktısı
bytearrayolarak, ayrıca SD kart ve SPIFFS akışları için binary.binindirmesi. - Türkçe arayüz ve bu rehberin Türkçesi; İngilizce sürüm /en/tools/lcd-assistant/ adresinde.
Bunların hiçbiri orijinali yanlış yapmaz. Yalnızca monokrom bir diziye ihtiyacınız varsa ve image2cpp zaten yer imlerinizdeyse işinizi gayet iyi görür. Buradakini tercih etmenin sebebi renkli paneller, ek dithering seçenekleri ve OLED'in gerçekte nasıl görüneceğini gösteren önizlemedir.
Sık sorulan sorular
image2cpp renkli resim destekliyor mu?
Evet. Klasik 1 bit tek renkli modun yanında RGB565 (2 bayt/piksel), RGB888 (3 bayt/piksel) ve HSV dizi çıktısı verir. Renkli RGB565 - 2 bayt/piksel çizim modunu seçerseniz ST7735, ST7789, ILI9341 veya ILI9488 TFT ekrana doğrudan gönderebileceğiniz bir dizi elde edersiniz.
RGB565 nedir, neden piksel başına 2 bayt?
RGB565 bir rengi 16 bite sıkıştırır: 5 bit kırmızı, 6 bit yeşil, 5 bit mavi. Fazladan bit yeşile gider çünkü göz en çok yeşile duyarlıdır. Sonuç, 24 bit RGB888'in yarısı bellekte 65.536 renk. Arduino ve ESP32 üzerindeki neredeyse her küçük TFT sürücüsü bu yüzden RGB565 kullanır.
Diziyi SSD1306 128x64 OLED ekranda nasıl gösteririm?
Tuvali 128x64 yapın, yatay tek renkli çizim modunu ve Adafruit GFX çıktı biçimini seçin, sonra display.drawBitmap(0, 0, myBitmap, 128, 64, SSD1306_WHITE) ve ardından display.display() çağırın.
Yatay mı dikey bayt sırası mı kullanmalıyım?
Adafruit GFX drawBitmap ve U8g2 drawXBM için yatay; bu çoğu SSD1306 ve SH1106 kodunu kapsar. Nokia 5110 (PCD8544) gibi sayfa adresli sürücüler ve ham SSD1306 sayfa tamponları için dikey. Resim yatay şeritlere bölünüyorsa yanlış olanı seçmişsinizdir.
Bitmap ekranda neden bozuk veya kaymış görünüyor?
Neredeyse her zaman genişlik uyuşmazlığı. Dizinin genişliği drawBitmap'e verdiğiniz genişlikle aynı olmalı ve 1 bit modlarda 8'in katı olmalı; aksi halde her satır doldurulur ve resim çapraz kayar. İkinci olarak bayt sırasını, RGB565'teyseniz ve şekil değil renkler ters görünüyorsa üçüncü olarak setSwapBytes() ayarını kontrol edin.
Adafruit GFX ile U8g2 çıktısı arasındaki fark ne?
Adafruit GFX, drawBitmap ile çizilen bir PROGMEM bayt dizisi bekler ve 1 bitini açık piksel sayar. U8g2 ise drawXBM ile XBM sıralaması kullanır; orada her baytın içindeki bitler ters çevrilidir. Yanlışını seçmek her 8 pikseli aynalar.
C dizisini tekrar resme çevirebilir miyim?
Evet. Mevcut bir bayt dizisini Düz Bayt dizisi yapıştır kutusuna yapıştırın, doğru genişlik, yükseklik ve çizim modunu seçin; önizleme diziyi resme geri çevirir. Başkasının kodundan gelen bir bitmap'i kontrol etmek için kullanışlı.
Hareketli GIF çevirebilir miyim?
Burada hayır. Bu dönüştürücü tek bir durağan kare okur, çünkü dosyayı bir HTML canvas'a çiziyor ve canvas aynı anda tek kare tutuyor. Hareketli GIF verdiğinizde yalnızca birinci kareyi alırsınız. Animasyonlar için GIF'ten OLED animasyon dönüştürücüyü kullanın; o araç GIF formatını doğrudan ayrıştırıp her kareyi ayrı bir dizi olarak verir, üstüne kareler arasında dolaşan hazır kodu da üretir.
Hangi resim boyutunu kullanmalıyım?
Ekranınıza göre: yaygın SSD1306 OLED'ler için 128x64 veya 128x32, Nokia 5110 için 84x48, ST7735 için 160x128, yuvarlak GC9A01 için 240x240, ILI9341 için 320x240. 320x240 RGB565 bir resmin 153.600 bayt tuttuğunu unutmayın; bu Arduino Uno'ya sığmaz, ESP32 veya SD kart gerekir.
Arduino ile OLED ekranda resim nasıl gösterilir?
Resmi yükleyin, tuvali panelinizin boyutuna ayarlayın (SSD1306 için genellikle 128x64), Çizim modu olarak Mono - Yatay, 1 bit/piksel ve çıkış formatı olarak Arduino kodu seçin. Üretilen diziyi setup() üstüne yapıştırın, ardından display.drawBitmap(0, 0, logo, 128, 64, SSD1306_WHITE) ve display.display() çağırın. Ekranda hiçbir şey çıkmıyorsa önce I2C adresini (çoğunlukla 0x3C), sonra dizinin PROGMEM ile tanımlandığını kontrol edin.
Resmi C dizisine çevirme adım adım nasıl yapılır?
Dört adım: dosyayı seçin, 2. bölümde tuval boyutunu ve gerekiyorsa eşik ile dithering ayarını yapın, 4. bölümde ekranınıza uyan Çizim modunu seçin, Kod Oluştura basın. Çıktı kutusundaki dizi doğrudan derlenebilir hâldedir. Sonucu koda yapıştırmadan önce 3. bölümdeki önizlemeye bakın: orada bozuk görünüyorsa panelde de bozuk görünecektir.
U8g2 ile resim göstermek için hangi ayarları seçmeliyim?
Mono - Yatay çizim modunu seçin ve Bitleri bayt olarak değiştirin kutusunu işaretleyin, çünkü U8g2 XBM bit sırası kullanır. Diziyi static const unsigned char isim[] U8X8_PROGMEM olarak tanımlayın ve firstPage ile nextPage döngüsü içinde u8g2.drawXBMP(x, y, genişlik, yükseklik, isim) çağırın. O kutuyu işaretlemezseniz görüntü her 8 piksellik grup içinde aynalanır.
JPG fotoğrafı monokrom bitmap'e nasıl çeviririm?
JPG'yi yükleyin, sonra her şeyden önce eşikleme mi dithering mi kullanacağınıza karar verin. Logo veya çizgi grafik için Titreme menüsünden Binary seçin ve şekil temizlenene kadar Parlaklık / alfa eşiği değerini oynatın. Fotoğraf için Floyd-Steinberg ya da Atkinson seçin ve önce Kontrast kaydırıcısını yukarı çekin; 128x64 bir panelde yalnızca 8.192 piksel olduğu için düşük kontrastlı görüntü gri bir dokuya dönüşür. Kareyi tümüyle çevirmek yerine tek bir konuya sıkıca kırpın.
C dizisi yerine düz hex kodu alabilir miyim?
Kod çıkış formatını Düz bayt (plain bytes) yapın; C tanımı düşer, geriye yalnızca bayt değerleri kalır. Üstüne Çıktıdan '0x' ve virgülleri kaldır kutusunu da işaretlerseniz Python'a, JSON'a, bir struct başlatıcısına veya hex editöre yapıştırabileceğiniz çıplak bir hex dizesi elde edersiniz. Bitmap'i derlemek yerine SD karttan akıtacaksanız Binary (.bin) dosya olarak indir düğmesi aynı baytları dosya olarak verir.
Bu araç XBM dönüştürücü olarak kullanılabilir mi?
Evet, tek ayarla. Mono - Yatay seçin ve Bitleri bayt olarak değiştirin kutusunu işaretleyin: XBM, aynı yatay paketlemenin baytların içindeki bitleri ters çevrilmiş hâlidir, dolayısıyla elde ettiğiniz dizi XBM sıralamasındadır ve drawXBM() ile drawXBMP()'de doğrudan çalışır. Gerçek bir .xbm dosyası için tablonun üstüne genişlik ve yükseklik #define satırlarını ekleyip dosyayı .xbm uzantısıyla kaydedin.
SSD1306 OLED için özel font üretebilir miyim?
Kısmen. Adafruit GFXbitmapFont çıkış formatı, karakter görselleri şeridinden bir GFX font tablosu üretir; ilk ASCII karakteri ve x advance alanlarını doldurup setFont() ile kullanacağınız özel bir font elde edebilirsiniz. Ancak bu bir karakter çizim editörü değildir. 16x2 veya 20x4 HD44780 karakter LCD'lerin 5x8 özel karakterleri bambaşka bir konudur, onun için LCD Karakter Oluşturucu aracını kullanın.
LCD Assistant programını indirmem gerekir mi?
Hayır. LCD Assistant, 1 bit BMP dosyasını C tablosuna çeviren bir Windows masaüstü programıdır; bu sayfa aynı işi kurulum istemeden, her işletim sisteminde, tarayıcıda yapar. Üstelik yalnızca önceden eşiklenmiş BMP değil PNG, JPG, GIF ve WebP kabul eder, drawBitmap()'in beklediği yatay sırayı varsayılan alır ve PROGMEM niteleyicisini çıktıya sizin yerinize ekler.
BMP dosyası ile bitmap dizisi arasındaki fark nedir?
BMP bir kaptır: genişliği, yüksekliği, renk derinliğini ve satır dolgusunu anlatan bir başlık, ardından piksel verisi. Bitmap dizisi ise yalnızca piksel verisidir; belirli bir ekranın beklediği şekilde paketlenmiş ve C kaynağı olarak yazılmıştır. Mikrodenetleyicinin flash'ında dosya sistemi de yoktur BMP çözücü de, ihtiyacı olan şey dizidir. Bu araç da onu üretir; "image to bmp array" ile "image to bitmap array" aramalarının aynı kapıya çıkmasının sebebi budur.
İndeksli palet modu ne zaman işe yarar?
RGB565 çizim modunu seçtiğinizde beliren İndeksli palet kutusu, resmi en fazla 256 renge indirger ve tek bir dizi yerine bir uint16_t isim_palette[] ile bir uint8_t isim_pixels[] üretir. Piksel başına 2 bayt yerine 1 bayt harcandığı için sonuç kabaca yarı yer tutar: 240x240 bir görselde 115.200 bayt yerine 58.112 bayt. Logo, simge, arayüz çizimi ve az renkli grafiklerde fark gözle görülmez, çünkü zaten 256 renkten azını kullanırlar. Fotoğraf ve degradelerde bantlaşma başlar, orada tam RGB565 dizisinde kalmak daha iyidir. Çizim tarafındaki bedeli ise diziyi doğrudan pushImage çağrısına verememektir: satır satır palete bakıp bir uint16_t tamponu doldurmanız gerekir, çıktının sonundaki hazır döngü tam olarak bunu yapar.