Özellikle paralel arayüz kullanarak TFT sürmek istiyorsanız karşılaşacağınız hız problemi bir çok platformda gözle görülür bir sorun haline gelecek. Eski nesil MCU’lardan hatırlayacağınız paralel arayüzler TFT sürmek için bir çözüm olabilir. Bu yazıda STM32 içerisinde bulunan FSMC (Flexible Static Memory Controller) birimi üzerine konuşacağız. Keyifli okumalar.
Bilindiği üzere TFT sürmek aslında bir RAM’e erişmekten ibarettir. Bu RAM’e ne kadar hızlı erişirseniz, elde edeceğiniz performans o kadar iyi olur. Bu yüzden hız istenen yerlerde arayüz protokolünü seçerken SPI, IIC protokollerini içeren TFT arayüzlerinden uzak durmak gerekir. Bu aşamada 8, 16 Bit paralel arayüzleri kullanmak performansı hat safhaya çıkaracaktır. Paralel arayüzle birlikte gelen fazla pin kullanma ihtiyacı ve paralel arayüzü kontrol edecek birim ihtiyacı bu uygulamanın dezavantajı sayılabilir fakat hızlı bir TFT ihtiyacı için bu dezavantajlar rahatlıkla göz ardı edilebilir ki zaten paralel arayüzü sürecek birim FSMC olarak STM32 içerisinde bulunmaktadır.
Paralel arayüzü hızlı bir şekilde kontrol etmek adına geliştirilen FSMC birimi aslında bir SRAM sürücüsünden ibarettir. Hatta verilen Data/Command verileri için diğer RS, RW, RD gibi sinyalleri otomatik olarak üretmesi SRAM sürme işlemini sadece bir adrese erişmekten ibaret kılmaktadır. FSMC STM32 ailesinde en STM32F1 serisinden itibaren bulunmaktadır fakat her modelde bulunmadığına dikkat edin. STM32F1 için 100 pinli entegrelerde bulunur.
Benim bu uygulama için kullandığım HY-STM32-100P adlı kit üzerinde 2.4 Inc TFT bulunuyor. 240×320 piksel olan bu TFT paralel arayüze sahip. Bu uygulamada FSMC birimini 16 Bit olarak çalıştırıp ekrana bir resim basacağız.
Öncelikle D ve E portunda bulunan bağlantıları ayarlayarak başlayalım. Burada LCD Data pinleri, RS, RW, RD, NE1 pinlerini alternatif fonksiyon moduna alarak hazırlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
static void LCD_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* LCD Reset */ GPIO_InitStructure.GPIO_Pin = LCD_RESET_PIN; GPIO_Init(LCD_RESET_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /* D2-D3-RW-RD-D13-D14-D15-D0-D1 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init(GPIOD, &GPIO_InitStructure); /* D4-D5-D6-D7-D8-D9-D10-D11-D12 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init(GPIOE, &GPIO_InitStructure); /* FSMC NE1 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOD, &GPIO_InitStructure); /* FSMC RS */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_SetBits(GPIOD, GPIO_Pin_7); // CS GPIO_SetBits(GPIOD, GPIO_Pin_14| GPIO_Pin_15 |GPIO_Pin_0 | GPIO_Pin_1); GPIO_SetBits(GPIOE, GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10); GPIO_ResetBits(GPIOE, GPIO_Pin_0); GPIO_ResetBits(LCD_RESET_GPIO, LCD_RESET_PIN); // RESET GPIO_SetBits(GPIOD, GPIO_Pin_4); // RD=1 GPIO_SetBits(GPIOD, GPIO_Pin_5); // WR=1 } |
LCD Donanımsal reset pinine sahip olduğu için standart GPIO_Mode_Out_PP olarak ayarlanır. Data pinleri ise birime özel olduğundan GPIO_Mode_AF_PP olarak ayarlanır. Burada RW, RD, RS ve NE1 pinleri birime özeldir.
- FSMC için A16 pini, LCD için RS pinine denktir
- FSMC için NOE pini, LCD için RD pinine denktir
- FSMC için NEW pini, LCD için RW pinine denktirs
- FSMC için NE1 pini, LCD için CS pinine denktir
- FSMC için D0-15 pinleri, LCD için Data pinlerine denktir
Buradan anlaşılacağı üzere bilenen 8080 bağlantı arayüzü LCD için aşağıdaki şekilde olmaktadır.
Bağlantılardan sonra FSMC ayarlarını aşağıdaki gibi yapılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
static void LCD_FSMC_Init(void) { FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef FSMC_NORSRAMTimingInitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); FSMC_NORSRAMTimingInitStructure.FSMC_AddressSetupTime = 0x02; FSMC_NORSRAMTimingInitStructure.FSMC_AddressHoldTime = 0x00; FSMC_NORSRAMTimingInitStructure.FSMC_DataSetupTime = 0x05; FSMC_NORSRAMTimingInitStructure.FSMC_BusTurnAroundDuration = 0x00; FSMC_NORSRAMTimingInitStructure.FSMC_CLKDivision = 0x00; FSMC_NORSRAMTimingInitStructure.FSMC_DataLatency = 0x00; FSMC_NORSRAMTimingInitStructure.FSMC_AccessMode = FSMC_AccessMode_B; FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1; FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &FSMC_NORSRAMTimingInitStructure; FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE); } |
Burada iki ayar bulunur. Birincisi birim için yapılan ayar, ikincisi ise zamanlama ayarıdır. Bu ayarları detaylı şekilde öğrenmek isteyenler ST’nin yayınladığı FSMC uygulama notunu okuyabilirler.
Bundan sonra TFT hangi sürücü entegresini kullanıyorsa ona uygun komutları yollamak TFT’yi sürmeye hazır hale getirecektir. Bu bilgileri sürücünün datasheet dökümanından elde edebilirsiniz. Kullandığım geliştirme kiti üzerinde ILI9325 sürücüsü bulunmaktadır. Artık TFT’ye komut/veri yollamak için her şey hazır durumda. Bu verileri bir adrese erişiyor gibi kullanmaktan bahsetmiştim. Bu işlem için FSMC adreslerine erişmek yeterli. Bu adresler STM32F103VET6 için şu şekilde tanımlıdır.
1 2 |
#define Bank1_LCD_D ((uint32_t)0x60020000) // Data Address #define Bank1_LCD_C ((uint32_t)0x60000000) // Register Address |
Aşağıdaki fonksiyonları kullanarak gerekli erişimi sağlamak mümkün.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static void LCD_WR_REG(uint16_t index) { *(__IO uint16_t *) (Bank1_LCD_C) = index; } static void LCD_WR_CMD(uint16_t index, uint16_t val) { *(__IO uint16_t *)(Bank1_LCD_C) = index; *(__IO uint16_t *)(Bank1_LCD_D) = val; } static void LCD_WR_Data(uint16_t val) { *(__IO uint16_t *)(Bank1_LCD_D) = val; } |
Son olarak LCD hazırlama rutini aşağıdaki gibi tamamlanır. Bundan sonra ekrana dilediğimiz gibi erişebiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
void LCD_Init(void) { LCD_GPIO_Init(); LCD_FSMC_Init(); LCD_HardwareReset(); //############# void Power_Set(void) ################// LCD_WR_CMD(0x0000,0x0001); Delay(10); LCD_WR_CMD(0x0015,0x0030); LCD_WR_CMD(0x0011,0x0040); LCD_WR_CMD(0x0010,0x1628); LCD_WR_CMD(0x0012,0x0000); LCD_WR_CMD(0x0013,0x104d); Delay(10); LCD_WR_CMD(0x0012,0x0010); Delay(10); LCD_WR_CMD(0x0010,0x2620); LCD_WR_CMD(0x0013,0x344d); //304d Delay(10); LCD_WR_CMD(0x0001,0x0100); LCD_WR_CMD(0x0002,0x0300); LCD_WR_CMD(0x0003,0x1030); LCD_WR_CMD(0x0008,0x0604); LCD_WR_CMD(0x0009,0x0000); LCD_WR_CMD(0x000A,0x0008); LCD_WR_CMD(0x0041,0x0002); LCD_WR_CMD(0x0060,0x2700); LCD_WR_CMD(0x0061,0x0001); LCD_WR_CMD(0x0090,0x0182); LCD_WR_CMD(0x0093,0x0001); LCD_WR_CMD(0x00a3,0x0010); Delay(10); //################# void Gamma_Set(void) ####################// LCD_WR_CMD(0x30,0x0000); LCD_WR_CMD(0x31,0x0502); LCD_WR_CMD(0x32,0x0307); LCD_WR_CMD(0x33,0x0305); LCD_WR_CMD(0x34,0x0004); LCD_WR_CMD(0x35,0x0402); LCD_WR_CMD(0x36,0x0707); LCD_WR_CMD(0x37,0x0503); LCD_WR_CMD(0x38,0x1505); LCD_WR_CMD(0x39,0x1505); Delay(10); //################## void Display_ON(void) ####################// LCD_WR_CMD(0x0007,0x0001); Delay(10); LCD_WR_CMD(0x0007,0x0021); LCD_WR_CMD(0x0007,0x0023); Delay(10); LCD_WR_CMD(0x0007,0x0033); Delay(10); LCD_WR_CMD(0x0007,0x0133); } |
Örnek olması adına bu TFT’ye bir resim basmak için şu fonksiyonu kullanabiliriz. Burada başlangıç adresine dönüp sırayla 240×320 yani = 76800 adet byte veri basmak gerekiyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void LCD_DrawBMP(const uint8_t* bmp) { uint16_t temp = 0; uint32_t n = 0; LCD_WR_CMD(0x0003, 0x1018); LCD_WR_CMD(0x0050, 0); LCD_WR_CMD(0x0051, 239); LCD_WR_CMD(0x0052, 0); LCD_WR_CMD(0x0053, 319); LCD_WR_CMD(32, 0); LCD_WR_CMD(33, 0); LCD_WR_REG(34); while(n < 153600) { temp = (uint16_t)(bmp[n] << 8) + bmp[n+1]; LCD_WR_Data(temp); n += 2; } } |
Bir .h dosyasına resmin diziye dönüştürülmüş halini yükleyerek bu çalışmayı tamamlayabiliriz. Ben bitmap1 adında bir diziyi ekran bastım ve aldığım görüntü aşağıda gözükmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "main.h" #include "defines.h" #include "General.h" #include "LCD.h" #include "Bitmaps.h" int main(void) { /* General System Initialization */ General_Init(); LCD_Init(); LCD_DrawBMP(bitmap1); for(;;) { } } |
Kod derlenip yüklendikten bir kaç saniye sonra resim ekrana basılacaktır. Bunun sebebi Init fonksiyonu içerisinde bir takım gecikmeler olmasındandır.
Sonuç olarak
FSMC’nin aslında ilgili adreslere verilen değerler için sinyalleri otomatik olarak oluşturan ve RAM’e erişir gibi çalışmamızı sağlayan bir birim olduğu görülür. Paralel arayüzler için sağladığı hız avantajı, bir çok aygıt için FSMC’nin tercih edilme sebebi arasındadır. Bu birimin daha gelişmiş olan ve sadece LCD sürmeye yarayan birimlerine de göz atmanızı tavsiye ederim. Bunlar FSMC, FMC, LTDC olarak sıralanmaktadır.
Projeye github hesabı üzerinden ulaşabilirsiniz. Bir başka yazıda görüşmek üzere. Esen kalın.
Baran EKREM
[Düzenleme: Konuyla alakalı Soru Cevap Platformu için Link]