Atmega328p ve MicroSD kart ile TFT Ekranda Resim Gösterme

Herkese merhaba, bu makalede bir Atmega328P-PU mikrodenetleyicisi ile MicroSD karttan BMP tipindeki resim dosyalarını okuyup ST7789 sürücüsüne sahip bir IPS LCD ekranda gösterilmesini sağlayacağız.

Öncelikle bu projeyi çalışır vaziyete getirmek için saatlerce bir çok deneme yaptığımı söylemeliyim çünkü microSD kart modülü dışındaki tüm komponentleri ilk defa kullanma fırsatım oldu. Daha önceki eğitsel projelerimde Arduino ve türevlerini (Uno, Pro Mini) kullandım ve yeterince deneyimledikten sonra artık elektronik bileşenleri tak-çalıştır dışında daha derinlemesine kullanma zamanımın geldiğini farkettim.

İlk önce Atmega328 i herhangi bir modül-board kullanmadan programlama ve çalıştırmayı öğrendim ardından diğer bileşenlerle iletişim kurmasını sağladım. Elimdeki diğer bileşenler bir IPS LCD ekran, bir DS1307 RTC çipi bir 4×3 keyboard ve MicroSD kart okuyucu olduğundan hepsini kullanarak çalışan bir proje yapmaya karar verdim ve makale konusu olan projeyi geliştirdim.

Aslında nihai amacım uzun zamandan beri yapmak istediğim (ve bir prototipini delikli breadboard üzerinde gerçekleştirdiğim) bir kol saati yapmaktı, bu işin breadboard’lar üzerinde olmayacağını anladıktan sonra son bir protorip yapmak için parçaları da buna göre sipariş ettim. Yeterince know-how sahibi olduğumda gerçek bir PCB üzerinde üretmeyi planlıyorum.

Bu uzun girişten sonra proje detaylarına geçelim. Gerekli olan parça listesi aşağıdadır. Kendi projemde TP4056 MicroUSB şarj modülü ile 3.7v Lipo pil de kullandım fakat projeyi karmaşıklaştırmamak için burada kullanmıyorum ayrıca Atmega328’in üzerinde bootloader olması ve bir FTDI modülü ile programlanmaya hazır olması gerekiyor.

1- MicroSD kart modülü
2- Atmega328P-PU
3- ST7789 IPS LCD Ekran
4- 16Mhz Kristal
5- 1K Direnç
6- 2 Adet 10pF Kapasitor

Karşılaştığım en önemli sorun; ST7789 ekranının modül üzerinde CS pini olmamasından dolayı mikrodenetleyicinin SPI hattını başka bir bileşen ile kullanmasını engellemesiydi ve MicroSD modülü de SPI kullanıyordu. Bunun için iki bileşenin tüm pinlerini ayırmam ve ekran için Software SPI kullanmam gerekti, bu tür prototip projelerde pek sorun olmasa da ticari projelerde fazladan pin işgal ettiği için sorun teşkil edebilir bunu aşmak için COG tipindeki Lcd ekranları kullanabilirsiniz.

Diğer sorun ise; ST7789 için kullandığım Adafruit kütüphanesi Software SPI ile sıkıntı çıkardı ve Arduino_ST7789 isimli bir kütüphane kullandım. software SPI doğal olarak hardware SPI’dan yavaş çalıştığı için resmin ekrana basılması 4–5 saniye kadar sürdü.

Keypad’i tek bir buton için kullandım siz diğer butonları da kullanarak daha interaktif uygulamalar yapabilirsiniz. Yukarıdaki devreyi kurduysanız uygulamanın kodlarına geçebiliriz. Bu işlemi yapmadan önce çalışır durumdaki bir mikroSD kartınıza boyutları 240×240 piksel olan BMP formatında resimleri yüklemelisiniz. BMP olmasının sebebi Jpeg, Png gibi resim formatlarının sıkıştırılmış veya bir algoritma ile çözümlenmesi gerekmesi ve Atmega’nın bu algoritmaları çalıştırmak için yetersiz/yavaş olmasıdır.


#include <Keypad.h>
#include <Adafruit_GFX.h>
#include <Arduino_ST7789.h>
#include <SD.h>
#include <SPI.h>

#define BUFFPIXEL 20

// Duemilanove, etc., pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK.
#define SD_CS 4 // Chip select line for SD card
#define TFT_DC 8
#define TFT_RST 9
#define TFT_CS 4
#define TFT_MOSI 6
#define TFT_SCLK 7
Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_CS); //for display with CS pin
int imagecount = 0;
int oldimagecount = 0;
const byte satir = 1;
const byte sutun = 1;
byte satirPin[satir] = { 2 };
byte sutunPin[sutun] = { 3 };
char keys[satir][sutun] = {
    {‘1’}
};
uint8_t buttonhistory = 0;

Keypad keypad = Keypad(makeKeymap(keys), satirPin, sutunPin, satir, sutun);

void setup(void)
{
    Serial.begin(9600);
    // Initialize 1.3" TFT
    tft.init(240, 240); // initialize a ST7789 chip, black tab
    Serial.println(“OK !”);
    tft.fillScreen(BLACK);
    Serial.print(“Initializing SD card…”);
    if (!SD.begin(SD_CS)) {
        Serial.println(“failed !”);
        return;
    }
    Serial.println(“done”);
    bmpDraw(“resim.bmp”, 0, 0);
}

void loop()
{
    char key = keypad.getKey();
    if (key == ‘1’)
        imagecount += 1;
    if (oldimagecount == 0 && imagecount == 1) {
        bmpDraw(“resim2.bmp”, 0, 0);
        oldimagecount = 1;
    }
    if (oldimagecount == 1 && imagecount == 2) {
        bmpDraw(“resim3.bmp”, 0, 0);
        oldimagecount = 2;
    }
    if (oldimagecount == 2 && imagecount == 3) {
        bmpDraw(“resim4.bmp”, 0, 0);
        oldimagecount = 3;
    }
    if (oldimagecount == 3 && imagecount == 4) {
        bmpDraw(“resim5.bmp”, 0, 0);
        oldimagecount = 0;
        imagecount = 0;
    }
}
// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates. It’s sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel). Increasing the buffer
// size takes more of the Arduino’s precious RAM but
// makes loading a little faster. 20 pixels seems a
// good balance.

void bmpDraw(char* filename, uint8_t x, uint8_t y)
{
    File bmpFile;
    int bmpWidth, bmpHeight; // W+H in pixels
    uint8_t bmpDepth; // Bit depth (currently must be 24)
    uint32_t bmpImageoffset; // Start of image data in file
    uint32_t rowSize; // Not always = bmpWidth; may have padding
    uint8_t sdbuffer[3 * BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
    uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
    boolean goodBmp = false; // Set to true on valid header parse
    boolean flip = true; // BMP is stored bottom-to-top
    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(‘\’’);
    // Open requested file on SD card
    if ((bmpFile = SD.open(filename)) == NULL) {
        Serial.print(“File not found”);
        return;
    }
    // Parse BMP header
    if (read16(bmpFile) == 0x4D42) { // BMP signature
        Serial.print(“File size
                     : “);
        Serial.println(read32(bmpFile));
        (void)read32(bmpFile); // Read & ignore creator bytes
        bmpImageoffset = read32(bmpFile); // Start of image data
        Serial.print(“Image Offset
                     : “);
        Serial.println(bmpImageoffset, DEC);
        // Read DIB header
        Serial.print(“Header size
                     : “);
        Serial.println(read32(bmpFile));
        bmpWidth = read32(bmpFile);
        bmpHeight = read32(bmpFile);
        if (read16(bmpFile) == 1) { // # planes — must be ‘1’
            bmpDepth = read16(bmpFile); // bits per pixel
            Serial.print(“Bit Depth
                         : “);
            Serial.println(bmpDepth);
            if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
                goodBmp = true; // Supported BMP format — proceed!
                Serial.print(“Image size
                             : “);
                Serial.print(bmpWidth);
                Serial.print(‘x’);
                Serial.println(bmpHeight);
                // BMP rows are padded (if needed) to 4-byte boundary
                rowSize = (bmpWidth * 3 + 3) & ~3;
                // If bmpHeight is negative, image is in top-down order.
                // This is not canon but has been observed in the wild.
                if (bmpHeight < 0) {
                    bmpHeight = -bmpHeight;
                    flip = false;
                }
                // Crop area to be loaded
                w = bmpWidth;
                h = bmpHeight;
                if ((x + w — 1) >= tft.width())
                    w = tft.width() — x;
                if ((y + h — 1) >= tft.height())
                    h = tft.height() — y;
                // Set TFT address window to clipped image bounds
                tft.startWrite();
                tft.setAddrWindow(x, y, w, h);
                for (row = 0; row < h; row++) { // For each scanline…
                    // Seek to start of scan line. It might seem labor-
                    // intensive to be doing this on every line, but this
                    // method covers a lot of gritty details like cropping
                    // and scanline padding. Also, the seek only takes
                    // place if the file position actually needs to change
                    // (avoids a lot of cluster math in SD library).
                    if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
                        pos = bmpImageoffset + (bmpHeight — 1 — row) * rowSize;
                    else // Bitmap is stored top-to-bottom
                        pos = bmpImageoffset + row * rowSize;
                    if (bmpFile.position() != pos) { // Need seek?
                        tft.endWrite();
                        bmpFile.seek(pos);
                        buffidx = sizeof(sdbuffer); // Force buffer reload
                    }
                    for (col = 0; col < w; col++) { // For each pixel…
                        // Time to read more pixel data?
                        if (buffidx >= sizeof(sdbuffer)) { // Indeed
                            bmpFile.read(sdbuffer, sizeof(sdbuffer));
                            buffidx = 0; // Set index to beginning
                            tft.startWrite();
                        }
                        // Convert pixel from BMP to TFT format, push to display
                        b = sdbuffer[buffidx++];
                        g = sdbuffer[buffidx++];
                        r = sdbuffer[buffidx++];
                        tft.pushColor(tft.color565(r, g, b));
                    } // end pixel
                } // end scanline
                tft.endWrite();
                Serial.print(“Loaded in “);
                Serial.print(millis() — startTime);
                Serial.println(“ ms”);
            } // end goodBmp
        }
    }
    bmpFile.close();
    if (!goodBmp)
        Serial.println(“BMP format not recognized.”);
}
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File f)
{
    uint16_t result;
    ((uint8_t*)&result)[0] = f.read(); // LSB
    ((uint8_t*)&result)[1] = f.read(); // MSB
    return result;
}

uint32_t read32(File f)
{
    uint32_t result;
    ((uint8_t*)&result)[0] = f.read(); // LSB
    ((uint8_t*)&result)[1] = f.read();
    ((uint8_t*)&result)[2] = f.read();
    ((uint8_t*)&result)[3] = f.read(); // MSB
    return result;
}

Yukarıdaki uygulama ilk çalıştığında SD kartınızın içerisindeki resim.bmp isimli dosyayı okuyup ekrana basar ardından “1” tuşuna bastıkça resim2,resim3,resim4, resim5.bmp dosyalarını ekrana basar. Buradaki “bmpDraw” isimli fonksiyonu Adafruit-ST7735-Library kütüphanesinden aldım

Umarım faydalı olmuştur. Yeni makalelerde görüşmek üzere.

Çalışma Görüntüsü

0.0 Ort. (0% puan) - 0 oy

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.