Bu bölümü okumadan
önce LCD
Ekran Kullanımı (Dot Matrix) Bölüm 1
'i okumanızı tavsiye ederim. Modül pin isimleri, görevleri, komutlar ve adres
bilgilerine değindiğim ilk bölümden farklı olarak uygulamaya yönelik daha
detaylı bilgiler vermeye çalışacağım. Burada arduino kodlarını
paylaşmayacağım bunu yapan çok sayıda blog ve site var. C dilinde yazılmış kod parçaları
paylaşacağım. İlk olarak modül ve mikro denetleyicinin bağlantılarını
gerçekleştirelim. 4 Bit veri aktarımı kullanarak anlatım yapacağımı
belirtmiştim.
Modülün D4,D5,D6,D7 (D4-D7) pinleri veri alış-verişi yapan
pinleridir. Paralel veri alma verme işleminde program kısmında kolaylık olması
açısından aynı porta bağlamak uygun olacaktır. Arduino uno D4-D7 ya da
atmega328p olarak PORTD4-PORTD7 pinlerine bağlanacak. RS saklayıcı seçici D8 ya
da PORTB0, EN saat darbesi D9 ya da PORTB1 pinine bağlanacak. RW pini genelde
Gnd bağlantılı eğer değilse D11 ya da PORTB3 bağlayabilirsiniz. D10 bende
bulunan ve piyasadaki birçok modelde kontrast ve arka aydınlatma bağlı
durumdadır. LED+ D10 ya da PORTB2 ye bağlıdır. Bu şekilde bir modül değilse
kontrast (VSS,VEE,VCC) ve arka aydınlatma (VCC,LED+, LED-) yazdığım sırayla 10k
potansiyometre ile bağlantı yapılacak. Tüm bağlantılar yapıldıktan sonra port
ve makro tanımlarını yapacağız ve fonksiyonları oluşturacağız. Öncesinde tekrar
gibi olsa da alttaki grafik dikkatle incelenmeli. kontrolcüye bilgilerin nasıl
gönderilip alınacağını kavramak için önemli.
Grafikte görülen veri ve komut yazma işlemlerinde "EN"
saat darbesi ve "DATA" kısmı ortak ikisi arasında sadece
"RS" farkı var. Ortak olan kısım için fonksiyon tanımlarsak, data
portuna veriyi göndereceğiz EN "1" ve "0" saat darbesiyle
modül kontrolcüsüne veriyi aktaracağız. Data portu PORTD D4-D7 çıkışlarıydı,
örnek olarak "0011" gibi bir veri
göndermek istesek PORTD=0b0011xxxx şeklinde data portu çıkışlarını "1" ya da
"0" yapmamız gerekir. Bunu yapmak için göndereceğimiz verinin her bir
bit verisini çıkışa tek tek yazabileceğimiz gibi PORTD|= 0b0011xxxx şeklinde de yapabiliriz. Gördüğünüz gibi sadece
bit 7 ve bit 4 arası yüksek bitleri kullanıyoruz. Kontrolcüyü 4bit olarak
ayarlayacağımız için bit0 -bit3 işleme alınmayacak. Tanımlayacağımız fonksiyon
parametresinin düşük bitlerini silip data portuna yazmalıyız.
void lcd_gonder(uint8_t gelen){
PORTD=(gelen & 0xF0); |
Görüldüğü gibi "gelen" parametresinin 0xF0 yani 0b11110000 ile "and" işlemine tabi tuttuk. Örnek:
PORTD=0b00001010 olsun ve gelen=0b00110101olsun, gelen veriyi "and" ile 0b00110000 haline getirdik ve PORTD=0b00110000 olarak data portuna
yazdık. Bu şekilde data portuna yazdığımızda farklı bir amaç için kullanabileceğimiz
"D3-D0" 4 adet çıkışı ya da girişi de değiştirmiş olduk. Bu
istenmeyen durumu çözmek için PORTD ye de 0x0F yani 0b00001111 ile and işlemi uygulamalıyız. PORTD=(PORTD&0x0f)|(gelen & 0xF0); olarak düzeltirsek
yukarıdaki örneğimiz değişir. PORTD=0b00001010, gelen=0b00110101 "and"
işlemi sonunda parantez içleri (0b00001010), (0b00110000) olur "or"
işlemiyle iki parantezin sonucu (0b00111010) olur. Bu sayede veriyi yazarken düşük bitleri
değiştirmemiş oluruz. Porta bilgiyi çıkış olarak ilettik "1" ve
"0" durumuna göre çıkışlar tetiklendi. Bu bilginin kontrolcünün
ilgili saklayıcısı tarafından alınması için "EN" pinin "1"
sonrasında "0" olması gerekiyor. 4Bit veri alacak şekilde
ayarladığımız kontrolcüye 8 Bit olan veriyi ikiye bölerek göndereceğiz demiştim.
Önce yüksek bit sonra düşük bit gönderilecek. Bunu komut ve veri saklayıcısını
seçen ilgili saklayıcıya gönderim yapan iki ayrı fonksiyon ile tanımlarız.
Fonksiyonlar arasındaki tek fark "RS" pininin "1" ya da
"0" olmasıdır. Komut gönderirken RS "0" olacak, veri
gönderirken "1" olacaktır. Bunun dışında ister komut ister veri olsun
yazma işlemi aynıdır ve veri 8bitten oluşmaktadır. Örneğin "A" harfi
yazmak için (0x41,0b01000001) lcd_yaz("A");
ya da lcd_yaz(0b01000001); şeklinde kod yazmamız gerekir. lcd_yaz
fonksiyonu parametre verisini (0b01000001) iki defa lcd_gonder() fonksiyonuna göndermeli.
Yüksek bit yazılacağı için (0100) yazıldıktan sonra (0001) düşük bitleri 4 bit sola kaydırılmalı. lcd_gonder(0b01000001) sonrasında lcd_gonder(0b00010000) şeklinde olmalıdır. Bunun için
void lcd_yaz(uint8_t data){
RS_HIGH;
//RW_LOW;
lcd_gonder(data);
lcd_gonder(data<<4);
}
|
şeklinde fonksiyon oluşturabiliriz.
void lcd_cmd(uint8_t cmd){
|
Lcd_cmd fonksiyonu
aynı sadece RS_HIGH; değil RS_LOW; olacaktır.
Modüle komut ya da veri yazma işlemini tanımladıktan sonra modülü
başlatma fonksiyonunu tanımlayabiliriz. Datasheet' lerde farklı sıralama ve
zamanlamalar olsa da mantık aynı. Enerji verildikten sonra sabit besleme için
beklenecek sonrasında veri uzunluk seçimi, giriş yöntemi, ekran satır sayısı ve
yazım yönü gibi komutları gönderip başlatma fonksiyonunu tamamlamış olacağız.
Datasheet' den alıntı resimde görüldüğü gibi gerekli komutlar
yazılmış. Kontrolcülerde bekleme zamanları farklı, bazı kontrolcülerde "Function
Set" komutuyla "0011" "0x03" 8bit verisi gönderilmemiş
4bit veri seçilerek işleme devam edilmiş. Adım adım gidecek olursak önce veri
göndereceğimiz için data portunu ve kontrolcünün yönetimi için gerekli
(RS,RW,EN) pinleri çıkış yapmamız gerekiyor. Enerji verildikten sonra 15-30 milisaniye
bekliyoruz. “0x03” bilgisini 3 kere gönderiyoruz. Bu veriyi “cmd” ya da “yaz”
fonksiyonuyla değil “lcd_gonder” fonksiyonuyla yapıyoruz. Veri 4bittir diğer
fonksiyonlar 8bit veriyi ikiye bölerek göndermektedir. İlk olarak 4.1ms sonra
100µs daha bekleyip “0x02” bilgisini
gönderip komutlara geçiyoruz. Gerekli ayarlamaları yapan komutları da
gönderdikten sonra modül kullanıma hazır hale geliyor. Artık gerekli komutlarla
ekranı kullanmaya başlayabiliriz.
Önceki bölümde modülün, kontrolcünün komutlarından bahsetmiştim.
Bu komutların detayına inmeden kolay kullanım için komutları tanımlayalım.
Clear display: LCD_CL
Return Home: LCD_HOME
Entry Mode Set: SCR= Ekran kayma açık, NSCR=Ekran kayma kapalı,
L=Sol, R=Sağ
LCD_NSCR_RL -LCD_NSCR_LR - LCD_SCR_RL - LCD_SCR_LR
Display ON/OFF: D= Ekran on-off, C= imleç on-off, B= kutu
on-off
LCD_DOFF - LCD_DON - LCD_DBON - LCD_DCON - LCD_DCBON
Cursor or Display Shift: (Bu komut ekrana yazarken ya da döngü
halinde verilmelidir.) CRL=İmleç sol, CRR=İmleç Sağ, SCL= Ekran sol, SCR=Ekran
sağ kaydırma
LCD_CR_L - LCD_CR_R - LCD_SC_L - LCD_SC_R
Function Set: 4L1= 4 bit tek satır, 4L2= 4 bit iki satır.
LCD_4L1 - LCD_4L2
Modülü kullanırken gerekli komutları tanımladıktan sonra yine
kullanıma dönük komutlardan Set CGRAM Address, Set DDRAM Address
komutlarını doğru ve düzgün kullanım için fonksiyon olarak tanımlamalıyız.
CGRAM: Daha önce anlattığım özel karakter hafıza birimiydi. Oluşturduğumuz
karakterleri hangi adrese atacağımızı belirlemek için kullanıyorduk.
void lcd_krk(uint8_t adres, uint8_t karakter[]){
|
Fonksiyon başlığını incelediğimizde iki parametre bulunmakta bunlardan
biri adres CGRAM komutu için adres, diğeri adrese gönderilecek veri. Daha önce bu
konuyu açıkladığım için burada anlatmayacağım.
void lcd_krk(uint8_t adres, uint8_t karakter[]){
lcd_cmd(0x40+(adres*8));
for (uint8_t i=0;i<8;i++)
{
lcd_yaz(karakter[i]);
}
}
|
Fonksiyonun gövdesini incelediğimizde komut gönderimi ile adres
seçiliyor sonrasında döngü ile adrese dizi içindeki veri kaydediliyor.
DDRAM: Ekranda gördüğümüz karakterlerin adreslerini belirleyen
hafıza birimiydi. Karakterlerin ya da imlecin yerini belirlemek için bu komutu kullanıyoruz.
void lcd_go(uint8_t x, uint8_t y){
|
X” ve”Y” olarak satır ve
sütun numaraları girilerek kullanılmaktadır.
Karakterleri ayrı ayrı yazabilirken bir kelime ya da cümle yazmak
istediğimizde bunu başaramayacağız. Bunu gerçekleştirmemiz için yine bir
fonksiyon tanımlamamız gerekecek. Bu fonksiyon girdiğimiz metni tek tek
karakter olarak yazdıracak.
void lcd_dizi (char dizi[]){
|
Kısaca değindiğim bu fonksiyonların kullanıldığı örnek programı
aşağıda inceleyebilirsiniz.
/*
* lcd_ornek.c
*
* Created: 19.01.2019 21:18:22
* Author : haluk
*/
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
//port tanımları
#define DATA_PORT PORTD
#define CMD_PORT PORTB
//#define RW_ PB3
#define EN_ PB1
#define RS_ PB0
//
#define DATA_OUT DDRD|=0xF0;
#define CMD_OUT DDRB|=0x03;//RW Gndye bağlı ise
//#define DATA_IN
DDRD&=~0xF0;
//RW Gndye bağlı değilse
//#define CMD_OUT
DDRB|=0x0B;
//#define RW_HIGH
CMD_PORT|=(1<<RW_)
//#define RW_LOW
CMD_PORT&=~(1<<RW_)
#define EN_HIGH CMD_PORT|=(1<<EN_)
#define EN_LOW CMD_PORT&=~(1<<EN_)
#define RS_HIGH CMD_PORT|=(1<<RS_)
#define RS_LOW CMD_PORT&=~(1<<RS_)
//lcd komutlar
#define LCD_CL lcd_cmd(0x01)
#define LCD_HOME lcd_cmd(0x02)
#define LCD_NSCR_RL lcd_cmd(0x04)
#define LCD_SCR_RL lcd_cmd(0x05)
#define LCD_NSCR_LR lcd_cmd(0x06)
#define LCD_SCR_LR lcd_cmd(0x07)
#define LCD_DOFF lcd_cmd(0x08)
#define LCD_DON lcd_cmd(0x0C)
#define LCD_DBON lcd_cmd(0x0D)
#define LCD_DCON lcd_cmd(0x0E)
#define LCD_DCBON lcd_cmd(0x0F)
#define LCD_CR_L lcd_cmd(0x10)
#define LCD_CR_R lcd_cmd(0x14)
#define LCD_SC_L lcd_cmd(0x18)
#define LCD_SC_R lcd_cmd(0x1C)
#define LCD_4L1 lcd_cmd(0x20)
#define LCD_4L2 lcd_cmd(0x28)
void lcd_gonder(uint8_t gelen){
DATA_PORT=(DATA_PORT&0x0f)|(gelen & 0xF0);
EN_HIGH;
_delay_us(1);
EN_LOW;
_delay_us(100);
}
void lcd_cmd(uint8_t cmd){
RS_LOW;
//RW_LOW;
lcd_gonder(cmd);
lcd_gonder(cmd<<4);
}
void lcd_yaz(uint8_t data){
RS_HIGH;
//RW_LOW;
lcd_gonder(data);
lcd_gonder(data<<4);
}
void lcd_go(uint8_t x, uint8_t y){
if (y==0)
lcd_cmd(0x80+x);
if (y==1)
lcd_cmd(0xC0+x);
if (y==2)
lcd_cmd(0x94+x);
if (y>=3)
lcd_cmd(0xD4+x);
}
void lcd_krk(uint8_t adres, uint8_t karakter[]){
lcd_cmd(0x40+(adres*8));
for (uint8_t i=0;i<8;i++)
{
lcd_yaz(karakter[i]);
}
}
void lcd_basla(){
CMD_OUT
DATA_OUT
_delay_ms(15);
lcd_gonder(0x03);
_delay_us(4100);
lcd_gonder(0x03);
_delay_us(100);
lcd_gonder(0x03);
lcd_gonder(0x02);
LCD_4L2; LCD_DOFF;
LCD_DON;
LCD_NSCR_LR;
LCD_HOME;
_delay_ms(2);
LCD_CL;
}
/*
void lcd_kontrol(){
uint8_t okdata;
DATA_IN//portd7-4 data pinleri giriş yapıldı
RW_HIGH;// RW pini "1" yapıldı okumaya hazır
do
{ EN_HIGH;
okdata=(PIND<<4);// yuksek bit okundu
_delay_us(1);
EN_LOW;
_delay_us(1);
EN_HIGH;
okdata|=(PIND&0x0F);// düşük bit okundu
_delay_us(1);
EN_LOW;
_delay_us(4);
} while (okdata&0x80); //Bit 7 "1" ise döngüde
kalır
DATA_OUT//portd7-4 data pinleri çıkış yapıldı
RW_LOW;//RW pini "0" yapıldı yazmaya hazır
}*/
void lcd_dizi (char dizi[]){
uint8_t i=0;
while(dizi[i])
{
lcd_yaz (dizi[i]);
i++;
}
}
int main(void)
{
lcd_basla();
_delay_ms(2000);
while (1) {
lcd_go(1,0);
lcd_yaz('A');
_delay_ms(500);
lcd_yaz('B');
_delay_ms(500);
lcd_yaz('C');
_delay_ms(500);
lcd_go(12,0);
lcd_yaz('1');
_delay_ms(500);
lcd_yaz('2');
_delay_ms(500);
lcd_yaz('3');
_delay_ms(500);
lcd_go(4,1);
lcd_dizi("16x2
LCD");
_delay_ms(500);
LCD_SC_L;
_delay_ms(500);
LCD_SC_R;
_delay_ms(500);
LCD_SC_R;
_delay_ms(500);
LCD_SC_L;
_delay_ms(500);
LCD_CL;
_delay_ms(500);
}
}
|