Grafik LCD Kullanımı
Biçerdöver
tablasını ayarlanan yükseklikte sabit tutmak için 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 128x64 piksel bir ekranda iki tane KS0108 bulunmaktadır. 128x64
aslında 64x64 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.
//////////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.
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.
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.
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.
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.
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ütunda
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: 64x64 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.
#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.
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.
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.
void glcd_yaz(uint8_t _chr){//girilen parametre ascii
table karakter kodu
_chr-=0x20; // ilk karakter space ascii 0x20 (32)
for (uint8_t i=0;i<5;i++){// her karakter 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.
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.
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ızlı 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.
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. Tüm fonksiyonları aşağıda bulabilirsiniz.
/*
* 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 sağ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 karakter space ascii 0x20 (32)
for (uint8_t i=0;i<5;i++){// her karakter 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++;
}
}
}
|