AVR-Grafik LCD Kullanımı
Grafik LCD Kullanımı
Biçerdöver tablasını ayarlanan yükseklikte sabit tutmakta kullanılan sistemi, eski bir model için kendim yapmaya karar verdim. Karakter LCD ile denemelerini yaptım ama görsel olarak tatmin olmadım. Grafik LCD ile daha iyi bir şey yaparım umuduyla araştırmalarıma başladım. Önceki yazılarımda da bahsettim bir amatör olarak öğrendiklerimi bir not defterine yazıp saklamaktansa benim gibi amatörler için ulaşılabilir bir kaynak olması açısında buraya yazıyorum.
Karakter LCD konusuna buradan ulaşabilirsiniz. Grafik LCD’nin farkı ekranın tamamının noktalardan (piksel) oluşması ve bizim bu noktalardan istediğimizi “1” ya da “0” yapabilmemizdir. Karakter LCD de kayıtlı olarak gelen ve 8 adet ile sınırlı oluşturulabilen harf, rakam ve şekiller dışında seçeneğimiz yoktur. Grafik LCD (GLCD) de ise çözünürlük elverdikçe sınırsız diyebiliriz. GLCD ekranların birbirinden farklı kontrolcü/sürücüleri mevcut. KS0108(S6B0108), ST7920 (SPI destekli) ve T6963C gibi ürünler mevcut, sanırım piyasada en çok bulacağınız KS0108 ve kopya ürünlerdir. Bu yazıda KS0108’i anlatmaya çalışacağım.
GLCD
İlk olarak GLCD pin isimleriyle başlayalım. Piyasada bulunan ekranların pin yerleşimi birbirinden farklıdır. Bu nedenle üreticinin bilgi sayfasını kontrol etmeniz gerekiyor. Üründe “1” ile gösterilen pinden başlayarak “20” ile gösterilen pine kadar görevleri aşağıdaki gibidir.
1-VDD (VCC): Pozitif besleme +5V.
2-VSS: Negatif besleme (GND).
3-VO: Ekran zıtlık ayarı, potansiyometrenin orta bacağı bu pine bağlanacak.
4,11- DB0,DB7: Paralel veri pinleri.
12- CS1: Bir numaralı ekran seçiminin yapıldığı pin.
13- CS2: İki numaralı ekran seçiminin yapıldığı pin.
14-RST: GLCD sıfırlama pini.
15-R/W: Okuma ya da Yazma seçiminin yapıldığı pin.
16-RS (D/I): Komut ya da Veri yollanması için gerekli seçimin yapıldığı pin. Register seçimi.
17-EN (E): İletişim için gerekli olan saat darbesinin gönderildiği pin.
18-VEE: Negatif voltaj çıkışı. Potansiyometrenin dış bacaklarından biri bu pine bağlanacak.
19- A(LED+): Arka aydınlatma Led artı pini.
20- K(LED-): Arka aydınlatma Led eksi pini.
KS0108 kullanan 128×64 piksel bir ekranda iki tane KS0108 bulunmaktadır. 128×64 aslında 64×64 iki ekrandan oluşur ve iki farklı sürücü tarafından yönetilir. CS0 ve CS1 numaralı pinler bu seçimi yapmamızı sağlar. Bu pinlerden hangisi “Low” ise o ekran veri alır-verir. İki ekranı aynı anda kontrol etmekte mümkündür.
Ekranlar 64 düşey sütun ve 8 yatay sayfadan oluşur. Her sayfada 8 piksel bulunur. Bu şekilde 64 yatay 64 düşey piksel oluşur. Ekranda bir “A” harfi yazmak istediğimizde öncelikle ekranı seçmeliyiz. Ekran seçimi yapıldıktan sonra sayfayı seçmeli ve gerekli veriyi göndermeliyiz. Şekilde görüldüğü gibi yazabilmemiz için göndereceğimiz veri ilk sütun için “0B 0111 1110” ya da “0x7E” ikinci sütun için “0B 0001 0001” ya da “0x11” olacaktır.
Bahsettiğim sayfa seçimleri ve benzeri işlemlerin nasıl yapılacağı bilgi sayfasında mevcut. Bu bilgilerden faydalanarak devam edelim. GLCD veri göndermek-almak ya da gerekli ayarlama ve seçimleri yapmak için RS ve R/W pinlerini kullanırız.
Satır, sütun seçimi ve ekran açma kapatma gibi komutları işlemek için RS ve RW “Low” olmalıdır. GLCD durum kontrolü için RS “Low” ve RW “High” olmalıdır. Veri gönderirken RS “High” RW “Low” veri okurken RS “High” ve RW “High” olmalıdır. Pin seçimleri bu şekilde yaptıktan sonra aşağıdaki tabloda diğer ayrıntıları görebiliriz.
Komutlar
Display on/off: Ekran açma/kapatma. GLCD ye bağlı data pinleri “0B 0011 1110” (0x3E) olursa/gönderirsek ekran kapanır. “0B 0011 1111” (0x3F) olursa ekran açılır.
Set Address: Y adresi olarak görünmekte ama ben x-y koordinat sistemi gibi düşünerek sütunlara “X” diyeceğim. 64 adet sütun olan ekranlardan hangi sütunu seçmek istediğimize bağlı olarak veriyi gönderiyoruz. İlk sütunun adresi (data pinleri) “0B 0100 0000” (0x40) ve sağa doğru sütun ilavesinde bir artırarak 63 numaralı sütuna ulaşabiliriz. Son sütunun adresi “0B 0111 1111” (0x7F). Gördüğünüz gibi işlemler ikili sayı sistemine göre yapılıyor. D7 ve D6 değişmeden “1” artırılıyor.
Set Page: Sayfa seçimi X adresi olarak görünmekte ama “Y” diyeceğim. Yukarıda belirttiğim 8 adet sayfanın seçimi için veriyi gönderiyoruz. İlk sayfa için (data pinleri) “0B 1011 1000” (0xB8) ve aşağı doğru son sayfa için “0B 1011 1111” (0xBF) olmalıdır.
Display Start Line: Ekranda üst sıradaki yerini ayarlar. Ekranı yukarı/aşağı kaydırabiliriz. İlk sayfanın DB0 numaralı pikselinin üstte olması için (data pinleri) “0B 1100 0000” (0xC0) son sayfanın DB7 numaralı pikseli için “0B 1111 1111” (0xFF) olmalıdır.
Status Read: GLCD veri alma/verme için uygun değilse, başka bir işlem ile meşgulse gönderdiğimz komutalara hatalı cevaplar verecektir. Bu nedenle uygunluk durumu, ekranın durumu ve reset durumu gibi verilerin okunduğu komuttur.
Write Display Data: Ekran, sayfa ve sütun seçimi yapıldıktan sonra RS “High” ve RW “Low” yapılarak ekrana veri gönderir ve pikselleri “1” ya da “0” yaparak istediğimiz görüntüyü oluşturabiliriz.
Read Display Data: Ekran, sayfa ve sütun seçimi yapıldıktan sonra RS “High” ve RW “High” yapılarak ekranda piksellerin “1” ya da “0” durumunu okuyabiliriz. Bu okuma piksel olarak yapacağımız işlemlerde gerekli olacaktır. Artık komutların ve pinlerin işlevine göre makroları ve fonksiyonları oluşturabiliriz.
Makrolar
Buraya kadar anlattığım işlemlerde bazı pinleri “1” High ya da “0” Low yapmak gerekiyor. Bunu kolayca tek komutla yapmak ve de hafızada fazladan yer kaplamadan yapmanın yolu ön tanımlı makrolardır. Ben uygulamayı Atmega32A ile yapıyorum farklı bir denetleyici için yani taşınabilir olması için Pin-Port tanımlarını değiştirmek yeterlidir. Port ve pinlerin giriş-çıkış durumlarını ve pinlerin 1-0 durumlarını tanımlıyorum. Bu tanımlardan sonra sıra fonksiyonlara geliyor.
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 |
//////////port #define DATA_P PORTA //PORTB-PORTC vb #define DATA_PIN PINA //PINB -PINC vb #define CMD_P PORTD //PORTB-PORTC vb #define DATA_DIR DDRA //DDRB-DDRC vb #define CMD_DIR DDRD //DDRB-DDRC vb ///////// pin #define CS1 PORTD2 //CMD port seçimine göre pinler ayarlanır #define CS2 PORTD3 #define RST PORTD4 #define RW PORTD5 #define RS PORTD6 #define EN PORTD7 ///////// dir #define DATA_OUT DATA_DIR|=(0xFF) // data portu çıkış yapıldı #define DATA_IN DATA_DIR&=~(0xFF) // data portu giriş yapıldı #define CMD_OUT CMD_DIR|=(1<<CS1)|(1<<CS2)|(1<<RS)|(1<<RW)|(1<<EN)|(1<<RST) //cmd portu çıkış yapıldı ///////// #define CS1_H CMD_P|=(1<<CS1) //cs1 high #define CS1_L CMD_P&=~(1<<CS1) //cs1 low #define CS2_H CMD_P|=(1<<CS2) #define CS2_L CMD_P&=~(1<<CS2) #define RS_H CMD_P|=(1<<RS) #define RS_L CMD_P&=~(1<<RS) #define RW_H CMD_P|=(1<<RW) #define RW_L CMD_P&=~(1<<RW) #define EN_H CMD_P|=(1<<EN) #define EN_L CMD_P&=~(1<<EN) #define RST_H CMD_P|=(1<<RST) #define RST_L CMD_P&=~(1<<RST) #define DISP_ON 0x3F #define DISP_OFF 0x3E #define X_ADR 0x40 #define Y_ADR 0xB8 #define Z_ADR 0xC0 |
Yazma Fonksiyonları
Yukarıdaki şemada GLCD yazma zaman çizelgesi bulunmaktadır. Veriyi yazmadan önce yapılması gerekenler görülmektedir. EN pininin high ve low olması yani bir dönem zamanı “tC” min 1ms olmalıdır. EN pini high olmadan “tASU” 140ns önce ekran seçimi yapılmalı, RS pini high ya da low ve RW low olmalıdır. E pini high olduktan sonra tekrar low olmadan “tDSU” 200ns önce data pinlerine veri yazılmalıdır. Bu işlemleri iki farklı fonksiyonda yapacağız. Bu fonksiyonlardan biri veri yazmak için diğeri komut yazmak için oluşturacağız. Bu arada önemli not GLCD de EN pini düşen kenarda veri yazılır, yükselen kenarda veri okunur.
1 2 3 4 5 6 7 8 9 |
void glcd_data(char _data){//glcd ram veri yazan fonksiyon glcd_durum();// busy flag kontrolü RS_H; //rs high yapıldı RW_L; //rw low yapıldı DATA_P=_data; //data portuna fonksiyon parametresi _data yazıldı EN_H; //en high saat darbesi _delay_us(1);//twh en az 450ns EN_L; //en low saat darbesi } |
Çizelgede görünen süreleri karşılamak için EN saat darbesi öncesi ekran seçimi ve register seçimi yapılıyor. Yazma yapılacağından RW low yapılarak data portuna istenen veriye göre pin durumu yazılıyor. Örneğin (0xF0) “0B 1111 0000” gibi bir veri yazmak istersek data portu 7-0 pinleri arası “11110000” şeklinde pinler “1” ya da “0” olacaktır. Böylece tASu ve tDSU süreleri karşılanacaktır. Sonrasında EN high-low yapılarak işlem tamamlanır.
1 2 3 4 5 6 7 8 9 |
void glcd_cmd(char _cmd){//glcd komut yazan fonksiyon glcd_durum();// busy flag kontrolü RS_L;//rs low yapıldı RW_L;//rw low yapıldı DATA_P=_cmd;//data portuna fonksiyon parametresi _cmd yazıldı EN_H;//en high saat darbesi _delay_us(1);//twh en az 450ns EN_L;//en low saat darbesi } |
Veri yazmada olduğu gibi komut yazmada da aynı işlemler yapılır. Aradaki fark RS pininin high-low durumu yani komut ya da veri seçimidir.
Okuma Fonksiyonları
Yazma için olduğu gibi okuma içinde zaman çizelgesinde yapılması gerekenler görülmektedir. RW high durumdayken RS low olduğunda durum okuması yapılırken RS high olduğunda GLCD veri okuması yapılır. Okuma yaparken “tD” 320ns süresi önemli bu nedenle önce EN high yapılmalı sonra data portu okunmalıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
uint8_t glcd_oku(){// veri okuma fonksiyonu glcd_durum();// busy flag kontrolü uint8_t _rdata; //8bit değişken oluşturuldu DATA_IN; //data portu giriş yapıldı RS_H; //rs high yapıldı RW_H; //rw high yapıldı EN_H; //en high saat darbesi _delay_us(1);// tWH ve tD süresi için bekleme _rdata=DATA_PIN;// data pini okunuyor değişkene atanıyor EN_L; //en low saat darbesi DATA_OUT;//data portu çıkış yapıldı return _rdata;// değişken veri olarak dönüş yapıyor } |
Okuma fonksiyonu sadece piksel olarak işlem yaptığımızda gereklidir. Örnek olarak “0” numaralı satırda yukarıdan aşağıya doğru birer artarak (bir piksel) çizgi çizmek istersek göndereceğimiz veri sırasıyla {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80} olacaktır. Bu şekilde yazdığımızda kayan bir nokta görürüz. Amaca ulaşmak için ekranın tüm bilgisini tutacağımız bir bellek oluşturmak işlemi buna göre yapmak mümkündür. Sadece düşeyde iş gören önceki satırla şimdiki satır aynıysa daha önce gönderilen veriyi şimdikiyle “OR” işlemiyle yazmakta bir seçenek. (data=0x01|0x02; gibi) ben böyle bir işlemi çok uzun sürmediği için veri okumasıyla yapmayı tercih ettim. Süre çok önemliyse RAM de bellek içinde ekranın bir görüntüsünü oluşturmak mantıklı olacaktır. Önemli not okuma yaparken boş okuma sonra veri okuma doğru olacaktır. Durum okumasında buna gerek yoktur.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void glcd_durum(){// busy flag kontrolü uint8_t durum; //8bit değişken oluşturuldu RS_L; //rs low yapıldı RW_H; //rw high yapıldı DATA_IN; //data portu giriş yapıldı do { EN_H; //en high saat darbesi _delay_us(1);// tWH ve tD süresi için bekleme durum=DATA_PIN; // data pini okunuyor değişkene atanıyor EN_L; //en low saat darbesi } while (durum&0x80); //busy flag D7 pini high ise işlem yapılıyordur, döngüden çıkılmaz okuma tekrarlanır DATA_OUT; //data portu çıkış yapıldı } |
GLCD zaman çizelgesinde görülen fakat fonksiyonlarda belirtmediğimiz gerekli süreleri sağlamak gereksiz beklemeler yapmamak için bu fonksiyonu kullanmalıyız. Belirtilen süreler değişkenlik gösterebilmektedir. Bir komutta sorun olmazken çok sayıda ve arka arkaya komutlar gönderdiğimizde bu fonksiyon daha önemli olmaktadır. Buraya kadar ki fonksiyonlar kalan işlemlere temel oluşturacaktır.
Ana Fonksiyonlar
Başlatma fonksiyonu: İlk açılışta GLCD nin hazırlanması gerekir. Açılışta sıfırlamak, X-Y ve Z adreslerini başlangıca almak gerekiyor.
1 2 3 4 5 6 7 8 9 10 11 12 |
void glcd_basla(){ DATA_OUT; // data portu çıkış yapıldı CMD_OUT; // cmd portu çıkış yapıldı RST_L; //reset _delay_ms(1000); RST_H;// reset bitti glcd_chip_sec(cift);// iki ekran seçildi glcd_cmd(X_ADR);// x 0 sütünda glcd_cmd(Y_ADR);// y 0 numaralı sayfada glcd_cmd(Z_ADR);// 0 numaralı sayfanın 0 numaralı pikseli en üstte glcd_cmd(DISP_ON);// ekran açıldı } |
Ekran Seçimi: 64×64 iki ekrandan oluşan ve iki adet sürücü tarafından kontrol edilen GLCD de ekran seçimi CS1 ve CS2 pinleri ile yapılır. Seçili olan ekranı bilmek ve işlem yapmak için chip değişkenine yazıyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#define cift 0 #define sol 1 #define sag 2 void glcd_chip_sec(uint8_t _chip){ if (_chip==sol){ CS1_L; CS2_H; chip=1; // global değişkene yazıldı } if(_chip==sag){ CS1_H; CS2_L; chip=2; // global değişkene yazıldı } if(_chip==cift){ CS1_L; CS2_L; } } |
Satır ve Sütun Seçimi: Sütun “x” ve satır “y” seçimin yapıldığı fonksiyondur. Satırlar piksel olarak girilir sayfa olarak seçilir. 128 (0-127) sütun 64 (0-63) satır vardır. Fonksiyon parametreye göre sayfa ve ekran seçimini gerçekleştirir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void glcd_git(uint8_t x, uint8_t y){ sutun=x;//sütun değişikliğini global bir değişkende tutuyoruz satir=y satır değişikliğini global bir değişkende tutuyoruz x&=127; // eğer girilen x değeri 127den büyükse başa alıyoruz. y&=63; // eğer girilen y değeri 63den büyükse başa alıyoruz. if (x>=64){// x değeri 64-127 arasındaysa sağ ekran glcd_chip_sec(sag); x&=63;//x değeri sağ ekranda da 0-63 arası olmalıdır. }else { glcd_chip_sec(sol);//0-63 arasındaysa sol ekran seçilir } glcd_cmd(X_ADR|x);// komut sütun seçimi glcd_cmd(Y_ADR|(y>>3));// komut satır seçimi (y>>3) 3 bit sağa kaydırma 8’e bölmek demektir (y/8) aynı şeydir. } |
Ekran Silme: Ekrandaki görüntüyü silen fonksiyon, burada yapılan ekrana “0x00” yazmaktır. Bunu yaparken iki ekran birlikte seçiliyor. For döngüsüyle 8 sayfanın tamamında, her sayfanın içinde bulunan 64 sütuna “0x00” verisi yazılıyor.
1 2 3 4 5 6 7 8 9 |
void glcd_sil(){ glcd_chip_sec(cift); for (uint8_t j=0;j<8;j++){ glcd_cmd(Y_ADR+j); for (uint8_t i=0;i<64;i++) { glcd_data(0x00); } } } |
Ekran Yazı, Grafik Fonksiyonları
Yukarıda anlatmaya çalıştığım fonksiyonları kullanarak çeşitli programlarla oluşturacağınız farklı fontlar ile harf ve rakamlar yazabilirsiniz. Şekiller çizebilir yine programlarla dönüştüreceğiniz fotoğrafları görüntüleyebilirsiniz. İlk olarak karakter LCD’ de kullanılan karakterleri ekrana yazmamızı sağlayan fonksiyonla başlayalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void glcd_yaz(uint8_t _chr){//girilen parametre ascii table karakter kodu _chr-=0x20; // ilk karkater space ascii 0x20 (32) for (uint8_t i=0;i<5;i++){// her arakter 5 sütundan yani 5 adet veriden oluşuyor. if (sutun>=64){ // sol ekran sütun sınırı glcd_git(sutun,satir);//sağ ekrana geçiş } if ((chip==2)&&(sutun>=125)){// ekran ve sütun bilgisi kullanılıyor glcd_git(0,satir+8);// ekrana sığma şansı kalmayan karakterler alt satıra geçer } glcd_data(pgm_read_byte_near(&font_5x8[_chr][i]));// for ile her seferinde bir veri yazılıyor sutun++;// glcd sütun otomatik artar, bizim bunu takip etmemiz gerektiğinden değişkeni artırıyoruz } glcd_data(0x00);// karakterler arası boşluk sutun++; } void glcd_dizi(char *str){ uint8_t i=0; while (str[i]){ glcd_yaz(str[i]); i++; } } |
Yaz fonksiyonu: İnternetten bulacağınız ya da programlarla oluşturacağınız çok boyutlu karakter dizisi ile ASCII tablosundaki karakterleri yazdırabilirsiniz. { 0x7E, 0x11, 0x11, 0x11, 0x7E }, Büyük “A” harfi için veri bu şekildedir. LCD için oluşturulan karakterler 8 yatay, 5 dikey noktadan oluşur. Bildiğiniz gibi düşeyde her sütun bir bayt toplam 5 bayt bir karakter oluşturur. Çok boyutlu dizinin ilk karakteri 0x20 ile başlıyor, parametre olarak ‘A’ girdiğimizde _chr=0x41 (65) olarak fonksiyon işleyecektir. Tablodaki “_” yazılacaktır. Bunun olmaması için parametreden 0x20 (32) çıkartıyoruz böylece “A” yazmak istediğimizde _chr= 0x21(33) olarak işlem yapılacaktır. İki ekranın ortasında kalan karakterler için diğer ekrana geçiş yapılıyor ve ekranın sonunda kalan karakterleri bölmemek için alt satırdan devam ediyoruz.
Tabloda her karakteri 6 bayt veriden oluşturup 0x00 ile karakter arası boşluk ayarlayabilirdik ama gereksiz yer tutmamak için karakter bitişinde boşluk bırakıyoruz. Sütun otomatik olarak değiştiriliyor, bunun takibini yapmazsak diğer ekrana geçiş ve alt satıra geçiş gibi işlemleri yapmak için farklı algoritmalar yapmamız gerekir.
Dizi Fonksiyonu: Girilen diziyi tek karakter halinde dizi elemanı bitene kadar yazdırır. Str[i]= 0 (null) olunca döngüden çıkılır.
1 2 3 4 5 6 7 8 9 |
void glcd_piksel(uint8_t _x, uint8_t _y){//istenen pikselin adresi (koordinatları) uint8_t _data=0; // geçici bir değişken oluşturuldu glcd_git(_x,_y);// parametre koordinatlarına gidildi glcd_oku(); // boş okuma yapıldı _data=glcd_oku();// ikinci okuma değişkene yazıldı _data|=(1<<(_y%8)); //düşeyde 8 bit 1 sayfadır. istenen bit için 8'e bölerek kalan bulunuyor ve o kadar bit kaydırma yapılıyor. "OR" işlemi yapılıyor glcd_git(_x,_y);// okuma yapınca sütun kaydırıldı bu nedenle tekrar istenen adrese gidiliyor glcd_data(_data);// veri yazıldı } |
Piksel Fonksiyonu: Bu fonksiyon ile 64 yatay 128 düşey pikselin istenen bir tanesine 0x01 yazılır. Yukarıda değindiğim gibi bu işlemi yaparken 8 bitten oluşan sayfa verisini korumak için okuma yapılmaktadır. Ekran görüntüsünü oluşturup hafızada yer tutmamak için bu yöntemi kullanmaktayım. Parametre olarak (5,5) yazıldığında sütun 5 ile D5 satıra bir nokta yazılacaktır. Bunu yapabilmek için GLCD ye “0B 0010 0000” (0x10) yazmamız gerekir. Data verisini 8’ e böldüğümüzde kalan 5 olur ve 5 bit sola kaydırma yaparak istediğimiz sonucu buluruz. Okuma yaparak mevcut veriyi “OR” işlemiyle koruyarak işlemi tamamlarız.
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 |
void glcd_cizgi(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2){ int8_t x=0,y=0,xadim=0, yadim=0; int16_t _hata; uint8_t dx,dy; if (x2>x1){ xadim=1; dx=(x2-x1); }else{ xadim=-1; dx=(x1-x2); } if (y2>y1){ yadim=1; dy=(y2-y1); }else{ yadim=-1; dy=(y1-y2); } x=x1; y=y1; if (dx>=dy){ _hata=2*dy-dx; for (uint8_t i=0;i<dx;++i){ glcd_piksel(x,y); if (_hata<0){ _hata+=2*dy; x+=xadim; }else{ _hata+=2*dy-2*dx; x+=xadim; y+=yadim; } } }else{ _hata=2*dx-dy; for (uint8_t i=0;i<dy;++i){ glcd_piksel(x,y); if (_hata<0){ _hata+=2*dx; y+=yadim; }else{ _hata+=2*dx-2*dy; x+=xadim; y+=yadim; } } } } |
Çizgi Fonksiyonu: Bresenham çizgi algoritması ile hızı ve düzgün sonuçlar alınabiliyor. Doğru denklemiyle denemelerim oldu ama çok düzgün sonuçlar alamadım. Bu algoritmada sürekli artan “x” ya da “y” durumuna göre hareket ediliyor. Çok fazla kaynak mevcut anlatma kısmını geçiyorum. Bu fonksiyon ile farklı fonksiyonlar türetilebilir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void glcd_resim(const unsigned char *img){ uint16_t sira=0; glcd_git(0,0); for(uint8_t _sayfa=0;_sayfa<8;_sayfa++){ glcd_chip_sec(sol);//sol ekran ile başlar glcd_cmd(Y_ADR+_sayfa); glcd_cmd(X_ADR); for(uint8_t _sutun=0;_sutun<128;_sutun++){ if (_sutun==64){// sütun değeri 64 sağ ekrana geçer glcd_chip_sec(sag); glcd_cmd(Y_ADR+_sayfa); glcd_cmd(X_ADR); } glcd_data(pgm_read_byte_near(&img[sira])); sira++; } } } |
Resim Fonksiyonu: Program aracılığıyla dönüştüreceğiniz fotoğraflı ekrana aktarabilirsiniz. Deneme çalışması aşağıdadır.
Bu yazı Haluk ŞİMŞEK tarafından yazılmıştı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 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
/* * glcd_atmega32a.c * * Created: 07.08.2019 21:51:54 * Author : haluk */ #define F_CPU 16000000l #include <avr/io.h> #include <util/delay.h> #include <avr/pgmspace.h> //////////port #define DATA_P PORTA //PORTB-PORTC vb #define DATA_PIN PINA //PINB -PINC vb #define CMD_P PORTD //PORTB-PORTC vb #define DATA_DIR DDRA //DDRB-DDRC vb #define CMD_DIR DDRD //DDRB-DDRC vb ///////// pin #define CS1 PORTD2 //CMD port seçimine göre pinler ayarlanır #define CS2 PORTD3 #define RST PORTD4 #define RW PORTD5 #define RS PORTD6 #define EN PORTD7 ///////// dir #define DATA_OUT DATA_DIR|=(0xFF) // data portu çıkış yapıldı #define DATA_IN DATA_DIR&=~(0xFF) // data portu giriş yapıldı #define CMD_OUT CMD_DIR|=(1<<CS1)|(1<<CS2)|(1<<RS)|(1<<RW)|(1<<EN)|(1<<RST) //cmd portu çıkış yapıldı ///////// #define CS1_H CMD_P|=(1<<CS1)//cs1 high #define CS1_L CMD_P&=~(1<<CS1) //cs1 low #define CS2_H CMD_P|=(1<<CS2) #define CS2_L CMD_P&=~(1<<CS2) #define RS_H CMD_P|=(1<<RS) #define RS_L CMD_P&=~(1<<RS) #define RW_H CMD_P|=(1<<RW) #define RW_L CMD_P&=~(1<<RW) #define EN_H CMD_P|=(1<<EN) #define EN_L CMD_P&=~(1<<EN) #define RST_H CMD_P|=(1<<RST) #define RST_L CMD_P&=~(1<<RST) #define DISP_ON 0x3F #define DISP_OFF 0x3E #define X_ADR 0x40 #define Y_ADR 0xB8 #define Z_ADR 0xC0 #define cift 0 #define sol 1 #define sag 2 ////// uint8_t sutun=0, satir=0,chip=1; ////// void glcd_basla(); void glcd_cmd(char _cmd); void glcd_data(char _data); void glcd_durum(); uint8_t glcd_oku(); void glcd_chip_sec(uint8_t _chip); void glcd_sil(); void glcd_git(uint8_t x, uint8_t y); void glcd_yaz(uint8_t _chr); void glcd_dizi(char *str); void glcd_piksel(uint8_t _x, uint8_t _y); void glcd_cizgi(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); void glcd_resim(const unsigned char *img); const uint8_t font_5x8 [95] [5] PROGMEM = { { 0x00, 0x00, 0x00, 0x00, 0x00 }, // space (0x20) { 0x00, 0x00, 0x2F, 0x00, 0x00 }, // ! { 0x00, 0x07, 0x00, 0x07, 0x00 }, // " { 0x14, 0x7F, 0x14, 0x7F, 0x14 }, // # { 0x24, 0x2A, 0x7F, 0x2A, 0x12 }, // $ { 0x23, 0x13, 0x08, 0x64, 0x62 }, // % { 0x36, 0x49, 0x55, 0x22, 0x50 }, // & { 0x00, 0x05, 0x03, 0x00, 0x00 }, // ' { 0x00, 0x1C, 0x22, 0x41, 0x00 }, // ( { 0x00, 0x41, 0x22, 0x1C, 0x00 }, // ) { 0x14, 0x08, 0x3E, 0x08, 0x14 }, // * { 0x08, 0x08, 0x3E, 0x08, 0x08 }, // + { 0x00, 0x50, 0x30, 0x00, 0x00 }, // , { 0x08, 0x08, 0x08, 0x08, 0x08 }, // - { 0x00, 0x30, 0x30, 0x00, 0x00 }, // . { 0x20, 0x10, 0x08, 0x04, 0x02 }, // / { 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 0 (0x30) { 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 1 { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2 { 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 3 { 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 4 { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5 { 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 6 { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7 { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8 { 0x06, 0x49, 0x49, 0x29, 0x1E }, // 9 { 0x00, 0x36, 0x36, 0x00, 0x00 }, // : { 0x00, 0x56, 0x36, 0x00, 0x00 }, // ; { 0x08, 0x14, 0x22, 0x41, 0x00 }, // < { 0x14, 0x14, 0x14, 0x14, 0x14 }, // = { 0x00, 0x41, 0x22, 0x14, 0x08 }, // > { 0x02, 0x01, 0x51, 0x09, 0x06 }, // ? { 0x32, 0x49, 0x79, 0x41, 0x3E }, // @ (0x40) { 0x7E, 0x11, 0x11, 0x11, 0x7E }, // A { 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B { 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C { 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D { 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E { 0x7F, 0x09, 0x09, 0x09, 0x01 }, // F { 0x3E, 0x41, 0x49, 0x49, 0x7A }, // G { 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H { 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I { 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J { 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K { 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L { 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // M { 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N { 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O { 0x3F, 0x09, 0x09, 0x09, 0x06 }, // P (0x50) { 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q { 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R { 0x46, 0x49, 0x49, 0x49, 0x31 }, // S { 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T { 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U { 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V { 0x3F, 0x40, 0x30, 0x40, 0x3F }, // W { 0x63, 0x14, 0x08, 0x14, 0x63 }, // X { 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y { 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z { 0x00, 0x7F, 0x41, 0x41, 0x00 }, // [ { 0x02, 0x04, 0x08, 0x10, 0x20 }, // \. { 0x00, 0x41, 0x41, 0x7F, 0x00 }, // ] { 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^ { 0x40, 0x40, 0x40, 0x40, 0x40 }, // _ { 0x00, 0x01, 0x02, 0x04, 0x00 }, // ` (0x60) { 0x20, 0x54, 0x54, 0x54, 0x78 }, // a { 0x7F, 0x50, 0x48, 0x48, 0x30 }, // b { 0x38, 0x44, 0x44, 0x44, 0x20 }, // c { 0x38, 0x44, 0x44, 0x48, 0x7F }, // d { 0x38, 0x54, 0x54, 0x54, 0x18 }, // e { 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f { 0x0C, 0x52, 0x52, 0x52, 0x3E }, // g { 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h { 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i { 0x20, 0x40, 0x44, 0x3D, 0x00 }, // j { 0x7F, 0x10, 0x28, 0x44, 0x00 }, // k { 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l { 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m { 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n { 0x38, 0x44, 0x44, 0x44, 0x38 }, // o { 0x7C, 0x14, 0x14, 0x14, 0x08 }, // p (0x70) { 0x08, 0x14, 0x14, 0x08, 0x7C }, // q { 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r { 0x48, 0x54, 0x54, 0x54, 0x20 }, // s { 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t { 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u { 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v { 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w { 0x44, 0x28, 0x10, 0x28, 0x44 }, // x { 0x0C, 0x50, 0x50, 0x50, 0x3C }, // y { 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z { 0x00, 0x08, 0x36, 0x41, 0x00 }, //sol süslü parantez { 0x00, 0x00, 0x7F, 0x00, 0x00 }, // | { 0x00, 0x41, 0x36, 0x08, 0x00 }, //sag süslü parantez { 0x30, 0x08, 0x10, 0x20, 0x18 } // ~ }; int main(void){ DATA_OUT; CMD_OUT; glcd_basla(); glcd_sil(); glcd_cizgi(0,0,127,63); glcd_cizgi(127,0,0,63); _delay_ms(1000); for (uint8_t i=0;i<64;i++){ for (uint8_t j=0;j<128;j++){ glcd_piksel(j,i); //_delay_ms(1); } } _delay_ms(1000); glcd_sil(); glcd_git(0,0); glcd_dizi("GLCD deneme "); _delay_ms(100); glcd_dizi("ABCDEFGHIJKLMNO abcdefghijklmno "); _delay_ms(100); glcd_dizi("0123456789 +-* /_()[]{}"); while (1){ } } void glcd_basla(){ DATA_OUT; // data portu çıkış yapıldı CMD_OUT; // cmd portu çıkış yapıldı RST_L; //reset _delay_ms(1000); RST_H;// reset bitti glcd_chip_sec(cift);// iki ekran seçildi glcd_cmd(X_ADR);// x 0 sütünda glcd_cmd(Y_ADR);// y 0 numaralı sayfada glcd_cmd(Z_ADR);// 0 numaralı sayfanın 0 numaralı pikseli en üstte glcd_cmd(DISP_ON);// ekran açıldı } void glcd_cmd(char _cmd){//glcd komut yazan fonksiyon glcd_durum();// busy flag kontrolü RS_L;//rs low yapıldı RW_L;//rw low yapıldı DATA_P=_cmd;//data portuna fonksiyon parametresi _cmd yazıldı EN_H;//en high saat darbesi _delay_us(1);//twh en az 450ns EN_L;//en low saat darbesi } void glcd_data(char _data){//glcd ram veri yazan fonksiyon glcd_durum();// busy flag kontrolü RS_H; //rs high yapıldı RW_L; //rw low yapıldı DATA_P=_data; //data portuna fonksiyon parametresi _data yazıldı EN_H; //en high saat darbesi _delay_us(1);//twh en az 450ns EN_L;//en low saat darbesi } void glcd_durum(){// busy flag kontrolü uint8_t durum; //8bit değişken oluşturuldu RS_L; //rs low yapıldı RW_H; //rw high yapıldı DATA_IN; //data portu giriş yapıldı do { EN_H; //en high saat darbesi _delay_us(1);// tWH ve tD süresi için bekleme durum=DATA_PIN; // data pini okunuyor değişkene atanıyor EN_L; //en low saat darbesi } while (durum&0x80); //busy flag D7 pini high ise işlem yapılıyordur, döngüden çıkılmaz okuma tekrarlanır DATA_OUT; //data portu çıkış yapıldı } uint8_t glcd_oku(){// veri okuma fonksiyonu glcd_durum();// busy flag kontrolü uint8_t _rdata; //8bit değişken oluşturuldu DATA_IN; //data portu giriş yapıldı RS_H; //rs high yapıldı RW_H; //rw high yapıldı EN_H; //en high saat darbesi _delay_us(1);// tWH ve tD süresi için bekleme _rdata=DATA_PIN;// data pini okunuyor değişkene atanıyor EN_L; //en low saat darbesi DATA_OUT;//data portu çıkış yapıldı return _rdata;// değişken veri olarak dönüş yapıyor } void glcd_chip_sec(uint8_t _chip){ if (_chip==sol){ CS1_L; CS2_H; chip=1; } if(_chip==sag){ CS1_H; CS2_L; chip=2; } if(_chip==cift){ CS1_L; CS2_L; } } void glcd_sil(){ glcd_chip_sec(cift); for (uint8_t j=0;j<8;j++){ glcd_cmd(Y_ADR+j); for (uint8_t i=0;i<64;i++) { glcd_data(0x00); } } } void glcd_git(uint8_t x, uint8_t y){ sutun=x;// ekrana yazarken sütun değişikliğini global bir değişkende tutuyoruz satir=y;// ekrana yazarken satır değişikliğini global bir değişkende tutuyoruz x&=127; // eğer girilen x değeri 127den büyükse başa alıyoruz. y&=63; // eğer girilen y değeri 63den büyükse başa alıyoruz. if (x>=64){// x değeri 64-127 arasındaysa sağ ekran glcd_chip_sec(sag); x&=63;//x değeri sağ ekranda da 0-63 arası olmalıdır. }else { glcd_chip_sec(sol);////0-63 arasındaysa sol ekran seçilir } glcd_cmd(X_ADR|x);// komut gönderiliyor sütun seçimi glcd_cmd(Y_ADR|(y>>3));// komut gönderiliyor satır seçimi (y>>3) 3 bit ağa kaydırma 8e bölmek demektir (y/8) aynı şeydir. } void glcd_yaz(uint8_t _chr){//girilen parametre ascii table karakter kodu _chr-=0x20; // ilk karkater space ascii 0x20 (32) for (uint8_t i=0;i<5;i++){// her arakter 5 sütundan yani 5 adet veriden oluşuyor. if (sutun>=64){ // sol ekran sütun sınırı glcd_git(sutun,satir);//sağ ekrana geçiş } if ((chip==2)&&(sutun>=125)){// her karakter 5 sütundan oluşuyor. glcd_git(0,satir+8);// ekrana sığma şansı kalmayan karakterler alt satıra geçer } glcd_data(pgm_read_byte_near(&font_5x8[_chr][i]));// for ile her seferinde bir veri yazılıyor sutun++;// glcd sütun otomatik artar, bizim bunu takip etmemiz gerektiğinden değişkeni artırıyoruz } glcd_data(0x00);// karakterler arası boşluk sutun++; } void glcd_dizi(char *str){ uint8_t i=0; while (str[i]){ glcd_yaz(str[i]); i++; } } void glcd_piksel(uint8_t _x, uint8_t _y){//istenen pikselin adresi (koordinatları) uint8_t _data=0; // geçici bir değişken oluşturuldu glcd_git(_x,_y);// parametre koordinatlarına gidildi glcd_oku(); // boş okuma yapıldı _data=glcd_oku();// ikinci okuma değişkene yazıldı _data|=(1<<(_y%8)); //düşeyde 8 bit 1 sayfadır. istenen bit için 8'e bölerek kalan bulunuyor ve o kadar bit kaydırma yapılıyor. "OR" işlemi yapılıyor glcd_git(_x,_y);// okuma yapınca sütun kaydırıldı bu nedenle tekrar istenen adrese gidiliyor glcd_data(_data);// veri yazıldı } void glcd_cizgi(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2){ int8_t x=0,y=0,xadim=0, yadim=0; int16_t _hata; uint8_t dx,dy; if (x2>x1){ xadim=1; dx=(x2-x1); }else{ xadim=-1; dx=(x1-x2); } if (y2>y1){ yadim=1; dy=(y2-y1); }else{ yadim=-1; dy=(y1-y2); } x=x1; y=y1; if (dx>=dy){ _hata=2*dy-dx; for (uint8_t i=0;i<dx;++i){ glcd_piksel(x,y); if (_hata<0){ _hata+=2*dy; x+=xadim; }else{ _hata+=2*dy-2*dx; x+=xadim; y+=yadim; } } }else{ _hata=2*dx-dy; for (uint8_t i=0;i<dy;++i){ glcd_piksel(x,y); if (_hata<0){ _hata+=2*dx; y+=yadim; }else{ _hata+=2*dx-2*dy; x+=xadim; y+=yadim; } } } } void glcd_resim(const unsigned char *img){ uint16_t sira=0; glcd_git(0,0); for(uint8_t _sayfa=0;_sayfa<8;_sayfa++){ glcd_chip_sec(sol);//sol ekran ile başlar glcd_cmd(Y_ADR+_sayfa); glcd_cmd(X_ADR); for(uint8_t _sutun=0;_sutun<128;_sutun++){ if (_sutun==64){// sütun değeri 64 sağ ekrana geçer glcd_chip_sec(sag); glcd_cmd(Y_ADR+_sayfa); glcd_cmd(X_ADR); } glcd_data(pgm_read_byte_near(&img[sira])); sira++; } } } |
Son Yorumlar