Bölüm-3 Mikrodenetleyici Başlangıç Uygulamaları

7-Segment Gösterge (Seven Segment Display) Kullanarak Sayaç Oluşturmak

Bu içerikte 7-Segment gösterge kullanarak 0-9 arası bir sayaç yapılacak. Bir butona her basıldığında ekrandaki değer bir artacak. Program yazılmadan önce 7 segment göstergede karakter oluşturmak için gerekli işlemler şu şekildedir:

Şekil-3.1

Bu değerler harflere göre verilmelidir. Tablo 3.1’dek harflere göre kodlar gösterilmiştir.

Tablo-3.1

Göstergeler ortak katotlu ya da ortak anotlu olarak iki çeşittir. Biz programlarımızda yaygın olarak ortak katotlu olanı kullanacağız. Bu göstergeleri çıkış uçlarından 330Ω’luk dirençler kullanarak sırası ile PIC portlarına bağlarsak, PIC portlarına da hangi sayıyı oluşturmak istiyorsak ona uygun kodu göndermemiz gerekmektedir.

Tablo-3.2


unsigned char sayac=0;/*değişkenlerin ilk değeri burada verilebilir*/
void main() {
 TRISB=0x00;
 PORTB=0;
 CMCON =0x07;/*Comparator(karşılaştırıcı) kapatıldı, dijital I/O */
 for(;;){ /*Program sonsuz döngü içinde çalışacak */
  if (sayac==0)PORTB=63;
  if (sayac==1)PORTB=6;
  if (sayac==2)PORTB=91;
  if (sayac==3)PORTB=79;
  if (sayac==4)PORTB=102;
  if (sayac==5)PORTB=109;
  if (sayac==6)PORTB=125;

  if (sayac==7)PORTB=7;
  if (sayac==8)PORTB=127;
  if (sayac==9)PORTB=111; /*buraya kadar sayıların Display
          görüntü karşılıkları gönderiliyor.*/
  sayac=sayac+1;/*sayac değişkeni bir artırılıp geri yazılır.*/
  if(sayac==10)sayac=0;/*sayac değeri kontrol ediliyor ve 9'a
    kadar saymasını istediğimiz için sayac 10 olur olmaz sıfırlıyoruz*/
  delay_ms(1000);/*Sayma işlemi 1saniye ara ile yapılıyor*/
 }
}
  

İlgili kodun uygulanabileceği devre Şekil 3.2'de gösterilmiştir.

Şekil-3.2

Eğer “if” – “else if” – “else” yapısı kullanılacaksa kod aşağıdaki şekilde düzenlenir. Böylece bir koşul sağlandığında artık diğer satırlara bakılmasına gerek kalmaz. Bu durum programın daha hızlı ve kararlı çalışmasını sağlar.

Tablo-3.3


if (sayac==0)PORTB=63;
else if (sayac==1)PORTB=6;
else if (sayac==2)PORTB=91;
else if (sayac==3)PORTB=79;
else if (sayac==4)PORTB=102;
else if (sayac==5)PORTB=109;
else if (sayac==6)PORTB=125;
else if (sayac==7)PORTB=7;
else if (sayac==8)PORTB=127;
else PORTB=111;

C dilinin güçlü özelliklerinden biri olan “switch-case” yapısı da kullanılabilir. Bu durumda karşılaştırma işleminin yapıldığı kısım aşağıdaki şekilde düzenlenir. Burada dikkat edilmesi gereken, her şart için gerçekleşen işlem satırının ardından “break” kodunun eklenmesidir.

Tablo-3.4


switch(sayac){
 case 0: PORTB=63;break;
 case 1: PORTB=6;break;
 case 2: PORTB=91;break;
 case 3: PORTB=79;break;
 case 4: PORTB=102;break;
 case 5: PORTB=109;break;
 case 6: PORTB=125;break;
 case 7: PORTB=7;break;
 case 8: PORTB=127;break;
 case 9: PORTB=111;
}

Devrenin butona basılması durumuna göre çalışması için mikrodenetleyicinin uygun bir girişine buton eklenir. Uygulamada B portunun boşta bulunan B7 pini saydırma girişi olarak kullanılmıştır. B7 butonunun girişi 10K’lık bir direnç üzerinden +5V VCC kaynağına bağlanmıştır. Butonun diğer ayağı şase hattına bağlanmıştır. Butona basıldığında B7 girişi 1 seviyesinden 0 seviyesine gitmektedir. Program kodunda bu durum kontrol ettirilir (Bkz. Tablo 3.5).

Tablo-3.5


unsigned char sayac=0;
void main() {
 TRISB=0x80;
 PORTB=0;
 CMCON =0x07;
 for(;;){
  switch(sayac){
   case 0: PORTB=63;break;
   case 1: PORTB=6;break;
   case 2: PORTB=91;break;
   case 3: PORTB=79;break;
   case 4: PORTB=102;break;
   case 5: PORTB=109;break;
   case 6: PORTB=125;break;
   case 7: PORTB=7;break;
   case 8: PORTB=127;break;
   case 9: PORTB=111;
  }
  if(PORTB.B7 == 0){
   sayac=sayac+1;
   if(sayac==10)sayac=0;
   delay_ms(1000);
  }
 }
}

TRISB : 0x80; “(10000000)” koduyla B portunun B7 pini giriş, diğer pinleri çıkış olarak ayarlanmıştır. Butona sürekli olarak basıldığı sürece sayaç değeri artar.

Şekil-3.3

Eğer yalnızca girişin 1 seviyesinden 0 seviyesine gittiği durumda sayaç değerinin artması isteniyorsa mikroC programının buton kütüphanesi kullanılabilir.

Dizi Değişken Tipiyle Tarama Tekniği

Diziler iyi bir programcı olmayı düşünüyorsak bilmemiz gereken en önemli kullanımlardan biridir. Hemen hemen tüm programlarda diziler oldukça yaygın kullanılmaktadır. Örneğin bir fabrikada on ayrı yerin sıcaklığını ekrana belirli süre aralıklarıyla yazdırmamızı sağlayan bir program gerekli olsun. Bu fonksiyonun kabaca yapılışı aşağıdaki gibi olacaktır:
Yazdır_1.sıcaklık;
Bekle
Yazdır_2.sıcaklık;
Bekle
Yazdır_3.sıcaklık;
:
:
Yazdır_10.sıcaklık;

Şeklinde 10 satır yazmamız gerekir. Tüm programlarımızı bu şekilde yazarsak bize sayfalar yetmeyecektir. Bu durum iyi bir programcıda olması istenmeyen bir özelliktir. İyi programcı bir işi kabaca ve uzun uzadıya yapmak yerine fonksiyonları kullanarak en kısa satırda yapmalıdır.

Bunu dizilerle yapabilmemiz için öncelikle programımıza dizi tanımlaması yapmamız gereklidir. Şimdi sıcaklık dizisini tanımlayalım. Dizimiz kaç değeri tutacak bunu belirlemeliyiz. Üstteki tanımlamalar için 10 değer tutacak.
sicaklik[10]={0,1,2,3,4,5,6,7,8,9}; /*buraya on değer yazılabilir */

1.ortamın sıcaklığı = sicaklik[0];
Bekle
2.ortamın sıcaklığı = sicaklik[1];
Bekle
3.ortamın sıcaklığı = sicaklik[2];
:
:
10.ortamın sıcaklığı = sicaklik[9];

Bu örnek için on ayrı satırda kod yazmak yerine dizi değişkenini bir döngü komutu içine sokarsak her bir sıcaklı değeri için ayrı bir kod yazmaya gerek kalmayacaktır. Bu durum aşağıdaki örnekte gösterilmiştir:


int sayac;
for(sayac=0; sayac<10; sayac++){
 (sayac+1).ortamınsıcaklıgı=sicaklik[sayac]
 Bekle;
}

Burada yazdığımız programı incelersek,
İlk değeri 0 olan sayac,
sayac + 1 =1 olur.
1.ortamınsıcaklığı=sicaklık[0]; sıcaklık dizisinin 0. Değeri 1 ortamla eşleştirildi.
Bir süre bekledik for döngüsü içinde sayac 1 oldu.
sayac+1=2 olur.

2.ortamınsıcaklığı=sicaklık[1]; :
:
şeklinde döngü 10 defa gerçekleşir ve on ayrı ortamın sıcaklığı 4 satırda yazılmış olur.
Şimdi konuyu pekiştirmek adına üstteki sayaç örneğini dizi ile yapalım (bkz. Tablo 3.6).

Tablo-3.6


unsigned char sayac=0;/*değişkenlerin ilk değeri burada verilebilir*/
unsigned int display[10]={63,6,91,79,102,109,125,7,127,111};
void main() {
    /*PORT AYARLAMALARI YAPILACAK GİRİŞ ÇIKIŞ AYARLARI*/
 TRISB=0x80;
 PORTB=0;
 CMCON =0x07;/* Karşılaştırıcı kapatıldı. Pinler dijitale ayarlandı. */
 for(;;){ /* Sonsuz döngü */
  PORTB=display[sayac];/*sayaci display içine yazdık */
  if(PORTB.B7==0){ /* eğer butona basılmışsa artırma işlemi yapılır. */
   sayac=sayac+1; /* sayac değişkeni bir artırılır. */
   if(sayac==10)sayac=0; /*sayac değeri kontrol ediliyor */
   delay_ms(1000);
  }
 }
}

Tarama Yöntemiyle 2 Basamaklı 7-Segment Gösterge Uygulaması

Birden fazla 7-Segment göstergenin her biri için ayrı hat kullanılacak olsa mikrodenetleyicinin port sayısı buna yetmez. Bu nedenle tüm göstergeler ortak hat üzerinden sürülür. Bu işlem için hızlı bir tarama tekniği kullanılır. İnsan gözünün maksimum algılama süresinden yararlanılarak bu işlem gerçekleştirilir. Kaç basamaklı bir sistem yapılacaksa söz konusu sayının birler, onlar, yüzler… basamakları sırayla 7 Segment gösterge hattına gönderilir. Kontrol ucu gözün algılayamayacağı bir tarama hızında aktifleştirilerek, tek bir gösterge hattı üzerinden birden fazla gösterge sürülür.

Tablo 3.7’de 2 basamaklı bir sistemin kod örneği gösterilmiştir. Birler basamağının elde edilmesi için “sayac” değişkeninin 10’a göre mod’u alınmıştır. Onlar basamağının elde edilmesi için “sayac” değişkeni 10’a bölünmüş ve ardından 10’a göre mod’u alınmıştır.

Bu kod örneğinde ayrıca mikroC programının buton kütüphanesinden yararlanılmıştır. Böylece butona uzun süre basılsa ya da buton basılı kalsa bile “sayac” değerinin artması engellenmiştir. Buton kütüphanesinin etkinleştirilmesi için mikroC “Library Manager” penceresinde kütüphane listesinde yer alan “Button” tercihinin tıklanması gereklidir.

Tablo-3.7


unsigned short eski_durum;
unsigned char sayac=0,birler,onlar; /*değişkenler tanımlanıyor*/
unsigned int display[10]={63,6,91,79,102,109,125,7,127,111};
void main() {
 TRISB=0x80; /* PORT B7 pini giriş diğer pinler çıkış yapıldı */
 PORTB=0;
 TRISA=0x00; /*PORTA çıkış yapıldı */
 PORTA=0;
 CMCON =0x07; /*Comparator kapatıldı pinler dijitale ayarlandı*/
 do {
  if (Button(&PORTB, 7, 1, 0)) eski_durum = 1;
  if (eski_durum && Button(&PORTB, 7, 1, 1)) {
   sayac++;
   eski_durum = 0;
  }
  birler=sayac%10; /*sayaç değerinin birler basamağı alınıyor.*/
  onlar=(sayac/10)%10; /*sayaç değerinin onlar basamağı alınıyor. */
  PORTA=0x01; /* PORTA.B0'a bağlı transistör aktif yapılıyor*/
  PORTB=display[onlar]; /*Onlar basamağı bilgisi displaylere gönderiliyor*/
  delay_ms(5);
  PORTA=0x02; /* PORTA.B1'e bağlı transistör aktif yapılıyor*/
  PORTB=display[birler];
  delay_ms(5);
 }while(1);
}

NOT: Button(&PORTB, 7, 1, 0)) komutunda PORTB, butonun bağlanacağı portu, 7 rakamının bulunduğu hane portun hangi pinine butonun bağlandığını, 1 sayısının olduğu hane buton tarama hızını (bu örnek için 1 ms) ve 0 rakamının olduğu hane, butonun bağlı olduğu pinin hangi değeri görmesi durumunda algılamanın gerçekleşeceğini (bu örnek için 0) gösterir. “eski_durum” adlı değişkenle buton girişinin o an için hangi durumda olduğu izlenir.

Şekil-3.4

Flaşör Uygulaması

Flaşör uygulamasında öncelikle kullanılacak LED sayısına karar verilir. LED’lerin paralel bağlanması durumunda çekecekleri akım hesaplanır ve ona uygun bir direnç üzerinden sürülürler. LED sayısının fazla olması durumunda mikrodenetleyicinin çıkışları için transistör kullanılır. Örnek devrede Kırmızı ve Mavi ışıkların her biri için 8’er LED kullanılmıştır. Işıkların pır pır edecek şekilde yanıp sönmesi için kırmızı ve mavi ışık gruplarının her birine bir “for” döngüsü kullanılmıştır. Yanıp sönme değerleri 50ms ile ayarlanmıştır. Bu iki döngü sonsuz “do{.. }while(1);” döngü yapısı içinde kullanılmıştır (Bkz. Tablo 3.8).

Mavi LED grubunu kontrol edecek transistör PORTB.B1 pinine, kırmızı LED grubunu kontrol edecek transistör PORTB.B2 pinine bağlanmıştır. Baz akımı 1k’luk direnç üzerinden, kolektör akımı 100 Ω’luk direnç üzerinden sürülmüştür (Bkz. Şekil 3.5).

Tablo-3.8


unsigned int i;
void main() {
 TRISB=0x00;
 PORTB=0;
 TRISA=0x00;
 PORTA=0;
 CMCON =0x07;
 i=0;
 do{
  for(i=0;i<5;i++){
   PORTB=0X02;
   delay_ms(50);
   PORTB=0X00;
   delay_ms(50);
  }
  for(i=0;i<5;i++){
   PORTB=0X04;
   delay_ms(50);
   PORTB=0X00;
   delay_ms(50);
  }
 }while(1);
}

NOT: Sürme transistörlerinin yerine daha güçlü bir transistör bağlanarak LED sayısı yükseltilebilir. Ayrıca LED gruplarına seri bağlı dirençler kaldırılarak LED’lerin daha parlak yanması da sağlanabilir. Burada dikkat edilmesi gereken her bir LED’in içinden geçen akımın elemanın doyum akımından fazla olmamasıdır.

Şekil-3.5

2 Satır LCD Ekrana Yazı Yazdırmak

Olayların, sonuçların ve çeşitli bilgilerin son kullanıcıya ve müşteriye sunulması noktasında en önemli görsel elemanlardan biridir LCD ekranlar. 2 satır ve 16 sütun özellikli karakter tabanlı LCD ekranlar bilgilendirme ve yönlendirme işlemleri gerektiren durumlarda oldukça pratik bir çözüm sunar. Uygulamada Proteus kütüphanesinde LM16L olarak bulunan 16x2 LCD ekran kullanılacaktır. Şekil 3.6’da 16 sütun ve 2 satır LCD ekranın bacak isimleri gösterilmiştir.

Şekil-3.6

LCD Ekranın Teknik Özellikleri

LM16L 16x2 alfanümerik LCD’nin 14 pini vardır. Ayrıca (+) – (-) ışıklandırma bağlantısı vardır. Bazı modellerde ışıklandırma bağlantısı 14 pinin yanında ek 2 pin olarak yer alır. Bacak isimleri ve özellikleri Tablo 3.9’da gösterilmiştir.

Tablo-3.9

mikroC programının LCD kütüphanesi vardır. LCD uygulamalarında “View --> Library Manager” komutuyla açılan kütüphane penceresinde LCD kütüphanesinin seçilmesi gerekmektedir. Bu kütüphane kodları ve işlevleri şu şekildedir:

Lcd_Init():LCD başlatma (initialize) komutudur. Bu komuttan önce aşağıdaki evrensel değişkenlerin tanımlanması gerekmektedir.
Evrensel Değişkenler
LCD_D7: 7 numaralı veri pini
LCD_D6: 6 numaralı veri pini
LCD_D5: 7 numaralı veri pini
LCD_D4: 4 numaralı veri pini
LCD_RS: Kütük seçme sinyal pini
LCD_EN: Etkinleştirme sinyal pini
LCD_D7_Direction: 7 numaralı veri pininin yönü
LCD_D6_Direction: 6 numaralı veri pininin yönü
LCD_D5_Direction: 5 numaralı veri pininin yönü
LCD_D4_Direction: 4 numaralı veri pininin yönü
LCD_RS_Direction: Kütük seçme pininin yönü LCD_EN_Direction: Etkinleştirme pininin yönü

Lcd_Out: Bu komut LCD ekrana, göndermek istediğimiz bilgiyi yollar.
                Lcd_Out(1, 3, “HOSGELDIN”); Dersek 1. satır ve 3. sütundan itibaren HOSGELDIN Yazısını göstermiş oluruz.
Lcd_Out_Cp: Kursörün bulunduğu yerden itibaren yazıyı ekrana basar.
                Lcd_Out_Cp(“Burada”); Kursör o anda neredeyse oradan itibaren “Burada” yazısını görürüz.

Lcd_Chr: İstediğimiz satır ve sütuna tek bir karakter yazdırmak içindir.
Lcd_Chr(2, 3, ‘X’); Komutu 2.satır 3.haneye bir X harfi LCD data hafızasına gönderilecektir. Lcd_Chr_Cp: Kursörün olduğu noktaya bir karakter basar.
Lcd_Chr_Cp(‘k’); dediğimizde o anda işaretçi nerdeyse oraya bir ‘k’ basacaktır.
Lcd_Cmd: Lcd ekrana ait bazı özellikler bulunmaktadır. Bu komutları LCD’ye anlatmak istediğimizde bunu kullanırız. Örnek verirsek ekranı sildirmek için, Lcd_clear komutunu parantez içinde yazarız:
Lcd_Cmd(_Lcd_Clear);
Parantez içine yazabileceğimiz diğer komutlar ve anlamları:

_LCD_SECOND_ROW: İşaretçiyi 2.satıra taşır.
_LCD_CURSOR_OFF: İşaretçiyi kapatır.
_LCD_BLINK_CURSOR_ON: İşaretçinin yanıp sönmesini sağlar.
_LCD_MOVE_CURSOR_LEFT: Display data RAM değişmeden kursor sola taşınır.
_LCD_MOVE_CURSOR_RIGHT: Display data RAM değişmeden kursor sağa taşınır.
_LCD_TURN_ON: LCD açılır.
_LCD_TURN_OFF: LCD kapanır.
_LCD_SHIFT_LEFT: Display data RAM değişmeden ekran sola kayar.
_LCD_SHIFT_RIGHT: Display data RAM değişmeden ekran sağa kayar.
_LCD_UNDERLINE_ON: İşaretçiyi _ haline getirir.
_LCD_CLEAR: Ekranı temizler.
_LCD_FIRST_ROW: İşaretçiyi 1.satıra taşır.
_LCD_RETURN_HOME: İşaretçiyi varsayılan yerine getirir.

LCD’ler 8 pin ya da 4 pin ile kontrol edilebilir. PIC pinlerinden en fazla yararlanmak için 4 pin kullanılacaktır. Burada bahsi geçen 4 pin LCD’ye bilginin gönderildiği pinlerdir. Haricen görüntüleme işlemi için tarama, data clock sinyalleri içinde haricen 3 pin kullanılacaktır. Bu durumda toplamda 7 pin yeterli olmaktadır. Böylece tek bir port LCD için kullanılabilir. Bu durumda bir pin boş kalmaktadır. Bu pin LCD arka plan ışığını otomatik yakıp söndürmede kullanılabilir. Böylece bütün bir PORT LCD için kullanılmış olur.

NOT: LCD’nin bacaklarını PIC’in hangi portunun hangi bacaklarıyla ilişkilendirmeniz, baskı devre hazırlayacak olmanız durumunda göz önünde bulundurmanız gereken bir durumdur. Olabildiğince aynı sırada giden bacaklarla eşleştirme yapmanız, atlamasız ya da en az atlamayla baskı devrenizi yapmanızı sağlar.

Yapılacak uygulamada PIC16F628A’nın B portu LCD için kullanılmıştır ve pin atamaları Tablo 3.10’daki kodla gerçekleştirilmiştir:

Tablo-3.10


sbit LCD_RS at RB2_bit;/*LCD bağlantıları ayarlanıyor*/
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;


Bu uygulamada LCD ekran üzerinde kayan yazı uygulaması yapılacaktır. Burada iki farklı metin kullanılmıştır. 1 numaralı mesaj ekranın 1’nci satırına sağdan sola kaydırılarak yazdırılmaktadır. 2’nci mesaj ise ekranın ortasına ortalanarak sabit olarak yazdırılmaktadır. Bu programda 1’nci mesajın yazdırılması için ‘pointer’ değişken yapısı kullanılmıştır. Pointer değişkenler bir başka bilginin veri belleğindeki adresini tutan değişken türüdür. Hangi değişkenin adresine atıfta bulunulacaksa o değişkenin başına ‘&’ (ampersand) işareti konur.

Tablo-3.11


unsigned int i;
char msj01[] = "SEVGILI ELEKTRONIK SEVERLER ";
char msj02[] = "MERHABA";

char *pointer_msj01, *pointer_msj02;   //pointer türünde değişken
unsigned int uzunluk_msj01, uzunluk_msj02, konum;

sbit LCD_RS at RB2_bit; //LCD bağlantıları ayarlanıyor
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

/**************************************************************/
/**************************************************************/
void ayarlar(){
 TRISB=0x00;
 PORTB=0;
 TRISA=0X00;
 PORTA=0;
 INTCON = 0; //Tüm kesmeler iptal edildi
 Lcd_Init(); //Lcd_Init PORTB LCD için hazırlandı
 Lcd_Cmd(_LCD_CURSOR_OFF);  // LCD kursör kapatıldı
 Lcd_Cmd(_LCD_CLEAR);//LCD'de rastgele karekter oluşmaması için silindi
 CMCON=0x07; //Karşılaştırıcı modülleri dijitale ayarlandı
}
 /**************************************************************/
void main() {
 ayarlar();
 uzunluk_msj01 = strlen(msj01);
 uzunluk_msj02 = strlen(msj02);
 konum = (16-uzunluk_msj02)/2;
 for(;;){ //sonsuz döngü
  pointer_msj01 = &msj01;
  pointer_msj02 = &msj02;
  for(i=0;i<16;i++){
   Lcd_Out(1,16-i,msj01);
   Lcd_Out(2,konum,pointer_msj02);
   delay_ms(100);
  }
  for(i=0;i<uzunluk_msj01;i++){
   Lcd_Out(1,1,pointer_msj01);
   Lcd_Out(2,konum,pointer_msj02);
   delay_ms(100);
   Lcd_Cmd(_LCD_CLEAR);
   pointer_msj01++;
  }
 }
}

LCD ekranda kayma efektinin oluşturulması amacıyla, sonsuz ‘for’ döngüsü açıldıktan sonra “pointer_msj01 = &msj01; pointer_msj02 = &msj02;” eşleştirmeleriyle ekrana yazdırılacak mesajların bellek adresleri ‘pointer’ değişkenlerine aktarılmıştır. Ardından LCD ekranın sütun sayısı olan 16 değerince bir ‘for’ döngüsü açılmıştır. Bu döngüde 1 numaralı mesaj, sütun konumu ‘16-i’ tekniğiyle geriye doğru eksiltilerek ekrana yazdırılmıştır. Döngü değeri son bulduğunda mesajın geri kalanını kayma efektini devam ettirerek yazdırmak için ikinci bir ‘for’ döngüsü açılmıştır. Bu döngüde ‘pointer’ değişkeninin değeri artırılarak (pointer_msj01++) her defasında ilgili mesajın bir sonraki adresinden itibaren ekrana yazdırılması sağlanmıştır. Böylece mesaj kısalarak 1’nci sütundan itibaren yazdırılmış, mesaj kayıyormuş etkisi oluşturulmuştur. Ancak ekran temizlenmediği takdirde mesajın son karakteri ekranı doldurur. Bunu engellemek için “Lcd_Cmd(_LCD_CLEAR);” komutuyla her defasında ekran temizlenir. Şekil 3.7’de 16x2 LCD’nin PIC16F628A ile bağlantısı ve programın uygulaması gösterilmiştir.

Şekil-3.7

mikroC'de Tuş Takımı Tasarımı

Mikrodenetleyicilerin yaygın kullanımlarından biri tuş takımları üzerinden veri girişi kontrolüdür. Bu başlık altında kendi tuş takımınızı tasarlama mantığını göreceksiniz. Anlatılacak yöntem aslında günümüzde mikroişlemcili bilgisayarlarda da kullanılan klavyelerin çalışma ilkesiyle benzerlik göstermektedir. mikroC’de 4x1, 4x2, 4x3 ve 4x4 olmak üzere dört farklı boyutlu tuş takımı kullanmanızı sağlayan “Keypad” kütüphanesi vardır. Ancak ilk önce hazır kütüphanelerden yararlanmadan butonları arzu ettiğimiz sıralamada dizerek kendimize özgü tuş takımı tasarımını göreceğiz.

Öncelikle tasarımı yapılacak tuş takımının kaç boyutlu olacağına karar verilir. Bu durum tuş takımını hangi amaçla kullanacağımıza bağlı olarak değişir. Eğer 0-9 arası rakam girişi yapılacak bir şifreli kapı tasarımı yapılacaksa rakamlar için 10, şifre giriş işlemi için ‘*’ ve Enter görevini görecek giriş onayı için ‘#‘ olmak üzere toplam 12 tuşluk bir tuş takımına ihtiyaç olacaktır. Bu durumda 4x3 olmak üzere 4 satır ve 3 sütun hattından meydana gelen bir tasarım yapmamız gerekir. 4 satır ve 3 sütun olmak üzere toplamda 7 adet port hattına ihtiyacımız olur. 8 bitlik bir mikrodenetleyicide tek seferde 8 bitlik port kontrolü yapılabildiğinden en fazla 4x4=16 tuşluk bir tuş takımına kadar tek bir mikrodenetleyici portu işimizi görecektir. 4x3=12’lik bir tuş takımı için mikrodenetleyicimizin mevcut 8 bitlik portlarından herhangi birini bu iş için ayırabiliriz. Bu durumda portun bir hattı boşa çıkacaktır.

Mikrodenetleyicilerin portlarını tuş takımı için ayırırken dikkat edilmesi gerekenlerden biri kullanılacak portun fiziksel özelliklerini bilmektir. Şekil 3.8’de CMOS çıkışlı iki farklı mikrodenetleyici portu gösterilmiştir. İlk resimde mikrodenetleyicinin dâhili çekme dirençleri aktifken, ikinci resimde açık kanallı bir port çıkışı gösterilmiştir. Bu durumda porta harici bir çekme direncinin bağlanması gerekir (Bkz. C-4 ).

Şekil-3.8

Tuş takımı ve buton uygulamalarında yaygın olarak B portunu kullanmaya çalışacağız. Bunun nedeni PIC 16F serisi mikrodenetleyicilerin B portunun tüm hatlarının dâhili çekme direnci özellikli olmasıdır. Böylece harici olarak çekme direnci bağlanması gerekmez. Dâhili çekme dirençleri PIC16F628A’da OPTION kütüğünün programlanmasıyla yapılır (Bkz. 4.2).

4x3 boyutlu tuş takımında hangi tuşa basıldığını izlemek için 2x16 LCD ekran kullanılacaktır. Şekil 3.9’da bu işlemi gerçekleştirecek devrenin PROTEUS® şeması gösterilmiştir. Yazılan programın mikrodenetleyicili devreye enerji verildiğinde çalışıp çalışmadığını anlamak için kullanacağımız basit ve pratik bir yöntem bulunmaktadır. Bunun için mikrodenetleyicinin bir portu fasılalı olarak çıkış verecek şekilde programlanır ve bir LED yardımıyla bu işlem izlenir. Eğer devrenize enerji verdiğinizde bu izleme portuna bağladığınız LED programladığınız üzere fasılalı olarak yanıyorsa mikrodenetleyici çalışıyordur. Diğer işlemlerin çalışmaması durumunda program kodunuzu ya da devre üzerinde diğer bağlantılarınızı kontrol etmeniz gerekecektir.

NOT: Bu uygulamada mikrodenetleyicinin dâhili osilatörü kullanılmıştır (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN). Dâhili osilatör seçeneğinin nasıl kullanılacağı EK-D3’te ayrıntılı olarak açıklanmıştır.

Şekil-3.9

Uygulamada 4x3 tuş takımı için B portunun 7 pini, 2 satır karakter LCD için A portunun 6 pini olmak üzere mikrodenetleyicinin toplam 13 pini aktif olarak kullanılmaktadır. Geriye giriş ve çıkış olarak kullanılabilecek RA4 ve RB0 pinleri kalmıştır. Mikrodenetleyicinin açılışta çalışıp çalışmadığını izlemek için RA4 hattı fasılalı LED yakma çıkışı olarak kullanılmıştır. PIC16F628A’nın A portunun RA4 hattı açık kanal (open drain) özelliklidir. Bu hattın çıkış olarak kullanılabilmesi için harici çekme (pull-up) direnci bağlanması gerekir. Açık kanal hatlı porta bağlanan harici çekme direncinin diğer ucu +5V’luk kaynak gerilimine bağlanır. RB0 pini bir sonraki bölümde göreceğiniz harici kesme girişi olarak özellikle boş bırakılmıştır. Devrede görüleceği üzere, RESET girişi olan RA5 pini ve mikrodenetleyicinin VDD/VSS besleme hatları dışında kalan 15 pini aktif olarak kullanılabilmektedir.

NOT: Mikrodenetleyicinin portlarına doğrudan LED gibi yüklerin bağlanması tavsiye edilmez. Bunun için daha zayıf akımla tetiklenebilecek bir transistör üzerinden LED’in sürülmesi mikrodenetleyicinin kararlı çalışmasını sağlayacaktır. Devre tasarımlarınızda bu duruma dikkat etmeniz tavsiye edilir.

Tuş takımı kullanmanın temel mantığı sütun ya da satır hatlarının sıralı olarak taratılması ilkesine dayanmaktadır. Bu uygulamada 3 sütun hattının sıralı olarak taratılması ilkesi kullanılacaktır. Satır hatlarının bağlandığı port pinleri giriş, sütun hattının bağlandığı port pinleri çıkış olarak programlanır. Dolayısıyla;

TRISB=0xF1;

Kodu ile B portunun hangi pinlerinin giriş ve çıkış olacağı TRISB kütüğüne bildirilir. Ardından sütun hatlarının bağlandığı RB1, RB2 ve RB3 portları sırayla sonsuz döngü içinde lojik-0 yapılarak sürekli olarak taranır. Bu portların sıralı taranması için en küçük işaretsiz tamsayı değişkeni formatında bir dizi değişken tanımlanmıştır.

unsigned short sutun[3]={0xFD, 0xFB, 0XF7};

Bu değişken Tablo 3.12’de gösterilen koda benzer şekilde kullanılırsa tarama işlemi yapılmış olur.

Tablo-3.12


void main(){
 ...
 while(1){ 
  keyscan(); 
 ... 
 }
 ...
}

void keyscan(){
 for (i = 0; i <= 2; i++)
   {
    PORTB = sutun[i];   //Sütun taraması
    tus_kontrol();
    delay_ms(50); //Tarama süresi
  }   
}


Ana program içinden çağırarak tarama için kullanacağımız “keyscan()” isimli değer döndürmeyen (void) bir fonksiyon oluşturulur. Ana program içinde sürekli “while(1)” şeklinde işletilen sonsuz bir döngü bulunur. Bu döngü ile tarama fonksiyonu sürekli olarak çağrılır ve tarama fonksiyonunun içindeki 3 adımlı “for” döngüsü ile “sutun[]” dizi değişkeninin daha önceden kendisine atanmış “hex” değerleri B portuna aktarılır. İlgili port hatlarının lojik-0 ile taratılması için B portuna gönderilen hex kodu Tablo 3.13’te gösterilmiştir.

Tablo-3.13

Tuşa bastığınız anda o sütuna karşılık gelen hat ile satır hattı kısa devre olur ve giriş olarak ayarlanmış satır hattında lojik-0 seviyesi oluşur. Program içinde, aynı anda lojik-0 seviyesine çekilen sütun ve satır hattını karşılaştırdığınızda hangi tuşa basıldığı tespit edilir. Tuşa basılma durumunu ve hangi tuşa basıldığına karar verecek program yapısı Tablo 3.14’te gösterildiği gibidir. “keyscan()” fonksiyonu içinden çağrılan “tus_kontrol()” isimli fonksiyon ile bu işlem gerçekleştirilir.

Tablo-3.14


void tus_kontrol(){
   if((sutun1==0) && (satir1==0)){
    tus_id=1;
    tus_adres=1;
   }
   if((sutun2==0) && (satir1==0)){
    tus_id=1;
    tus_adres=2;

...
...
...

   if((sutun1==0) && (satir4==0)){
    tus_id=1;
    tus_adres=10;  //* tuşu
   }
   if((sutun2==0) && (satir4==0)){
    tus_id=1;
    tus_adres=0;
   }
   if((sutun3==0) && (satir4==0)){
    tus_id=1;
    tus_adres=11;  //Enter tuşu (#)
   }
}


“tus_id” değişkeni ile tuşa basılma durumu takip edilir. “tus_adres” değişkeni ile tuşun onlu (desimal) değeri tutulur. Ana program içinde bu değişkenler kullanılarak hangi işlemlerin yapılacağına karar verilir. Hangi tuşa bastığımızı ve toplam kaç kez tuşa bastığımızı gösteren program kodunun eksiksiz hali Tablo 3.15’te verilmiştir. Bu programda farklı olarak “sayac” isimli bir değişken vardır ve bu değişken yardımıyla kaç kez tuşa basma işlemini gerçekleştirdiğimiz takip edilmektedir.

Tablo-3.15


#define LED_kontrol PORTA.RA4
#define sutun1 PORTB.RB1
#define sutun2 PORTB.RB2
#define sutun3 PORTB.RB3
#define satir1 PORTB.RB4
#define satir2 PORTB.RB5
#define satir3 PORTB.RB6
#define satir4 PORTB.RB7
unsigned short tus, i, tus_id, tus_adres, sayac;
unsigned short sutun[3]={0xFD, 0xFB, 0XF7};
char msj01[]="TUSA BASIN";
char msj02[]="BASILAN TUS:";
char sayac_txt[5];
sbit LCD_RS at RA7_bit;
sbit LCD_EN at RA6_bit;
sbit LCD_D4 at RA0_bit;
sbit LCD_D5 at RA1_bit;
sbit LCD_D6 at RA2_bit;
sbit LCD_D7 at RA3_bit;
sbit LCD_RS_Direction at TRISA7_bit;
sbit LCD_EN_Direction at TRISA6_bit;
sbit LCD_D4_Direction at TRISA0_bit;
sbit LCD_D5_Direction at TRISA1_bit;
sbit LCD_D6_Direction at TRISA2_bit;
sbit LCD_D7_Direction at TRISA3_bit;

void ayarlar(){
  sayac=0;
  Lcd_Init();//LCD başlatılıyor
  Lcd_Cmd(_LCD_CLEAR);
  Lcd_Cmd(_LCD_CURSOR_OFF);
  INTCON=0; //kesmeler kapatıldı
  CMCON = 0X07;//analog karşılaştırıcılar kapatıldı
  OPTION_REG = 0X07;//Dâhili pull-up'lar etkin ve düşen kenar tetiklemesi
  TRISA=0X00;
  PORTA=0;
  TRISB=0XF1;
  PORTB=0;
  for(i=0;i<5;i++){      //Açılışta mikrodenetleyicinin çalışıp
   LED_kontrol=~LED_kontrol;//çalışmadığını anlamamızı sağlayan
   Delay_ms(250);        //kontrol döngüsü
  }
  LED_kontrol=0;
}

void tus_kontrol(){
   if((sutun1==0) && (satir1==0)){
    tus_id=1;
    tus_adres=1;    // 1 tuşu
    sayac++;
   }
   if((sutun2==0) && (satir1==0)){
    tus_id=1;
    tus_adres=2;   // 2 tuşu
    sayac++;
   }
   if((sutun3==0) && (satir1==0)){
    tus_id=1;
    tus_adres=3;  // 3 tuşu
    sayac++;
   }
   if((sutun1==0) && (satir2==0)){
    tus_id=1;
    tus_adres=4;   // 4 tuşu
    sayac++;
   }
   if((sutun2==0) && (satir2==0)){
    tus_id=1;
    tus_adres=5;   // 5 tuşu
    sayac++;
   }
   if((sutun3==0) && (satir2==0)){
    tus_id=1;
    tus_adres=6;   // 6 tuşu
    sayac++;
   }
   if((sutun1==0) && (satir3==0)){
    tus_id=1;
    tus_adres=7;   // 7 tuşu
    sayac++;
   }
   if((sutun2==0) && (satir3==0)){
    tus_id=1;
    tus_adres=8;   // 8 tuşu
    sayac++;
   }
   if((sutun3==0) && (satir3==0)){
    tus_id=1;
    tus_adres=9;   // 9 tuşu
    sayac++;
   }
   if((sutun1==0) && (satir4==0)){
    tus_id=1;
    tus_adres=10;  //* tuşu
    sayac++;
   }
   if((sutun2==0) && (satir4==0)){
    tus_id=1;
    tus_adres=0;   //0 tuşu
    sayac++;
   }
   if((sutun3==0) && (satir4==0)){
    tus_id=1;
    tus_adres=11;  //Enter tuşu (#)
    sayac++;
   }
}

void keyscan()// Basılan bir tuş için Tuş takımını tarayan rutin
{
   for (i = 0; i <= 2; i++)
   {
    PORTB = sutun[i];   //Sütun taraması
     tus_kontrol();
     delay_ms(50);     //Tarama süresi
   }
}// Tuştakımını Tarayan rutin sonlandırılıyor
 /**************************************************************/

void main(){
 ayarlar();
 Lcd_Out(1,1,msj01);
 while(1){
  keyscan();
  if(tus_id==1){
   ShortToStr(sayac, sayac_txt);
   Lcd_Out(2,1,msj02);
   if(tus_adres<10){
    Lcd_Chr(2,14,tus_adres+48);
   }
   else if(tus_adres==10){
     Lcd_Chr(2,14,'*');
   }
   else{
     Lcd_Chr(2,14,'#');
   }
   Lcd_Out(1,11,sayac_txt);
   tus_id=0;
  }
 }
}


Short sayı türündeki “sayac” değişkeninin LCD ekranda yazdırılması için string türüne çevrilmesi gerekmektedir (Bkz. A-4.1). Short sayı türü metin türüne çevrildiğinde hafızada 5 karakterli yer tutar. O nedenle 5 indisli char türünde “sayac_txt[]” değişkeni oluşturulmuştur. Sayı-metin dönüşümlerinin yapılmasında kullanılan komutları aktif kılmanız için mikroC kütüphane yöneticisinde (Library Manager) “Conversions” başlıklı kütüphanenin etkinleştirilmesi gerekmektedir.

Program çalıştırıldığında her tuşa bastığınızda sayaç değerinin çoğu zaman birden fazla saydığı görülecektir. Bu durum tarama hızından kaynaklanmaktadır. Tarama süresini değiştirerek farklı hızlar için sonucu gözlemleyiniz.

Program koduna dikkat edilirse basılan tuşun sayısal değeri ekrana “Lcd_Chr” karakter yazma koduyla yazılmaktadır. mikroC’de ekrana karakter yazdırmanın birkaç yolu vardır. Yazdırılacak karakterlerimiz sayısal değerler olduğu için pratik ve hızlı şekilde dönüşüm işlemi yapmak zorunda kalmadan doğrudan bu rakamları yazdırmak için ASCII kodlarına başvurmaktayız. Böylece 0-9 arası rakamları rahatlıkla short-char dönüşümü yapmak zorunda kalmadan karakter olarak yazdırabilmekteyiz. ASCII koduna göre 0-9 rakamları 48-57 desimal değer aralığındadır. “Lcd_Chr” komutu ile doğrudan ASCII desimal kodunu kullanarak o koda karşılık gelen değeri yazdırabiliriz. Diğer ASCII kodları ve karşılıklarını görmek için mikroC programı ana menüsünden Tools-->Ascii Chart komutunu verdiğinizde çıkacak ASCII tablosunu kullanabilirsiniz.

Kullanıcı Tanımlı Harici Pull-up Dirençli Tuş Takımı Uygulama Devresi

Bu uygulamada 4x4 boyutlu bir tuş takımının farklı bir tarama tekniği ile gerçekleştirilmesi gösterilmiştir. Tuş takımının bağlı olduğu B portunun dâhili pull-up dirençleri devre dışı bırakılmış olup bunun yerine harici dirençler kullanılmıştır. Mikrodenetleyicilerde her portun dâhili çekme dirençleri olmadığından bu tür tuş takımı tasarımları daha yaygındır.

Tasarlanan tuş takımı 16 karakterli olup A, B, C ve D karakterleri de eklenmiştir. Harici çekme dirençleri birkaç yüz ohm dolaylarında seçilmelidir. Devrede 470 ohm’luk dirençler kullanılmıştır. Birkaç kilo ohm dolaylarında dirençler giriş olarak tanımlanan portların şaseye çekilmesinde yeterli olmayacaktır.

NOT: Mikrodenetleyicili devrelerde uygun direnç değerlerinin seçimi çok önemlidir. Aksi takdirde hatlarda kararsızlık durumu oluşacaktır. Kararsızlık mikrodenetleyicilerin olağan dışı çalışmasına neden olur.

Şekil-3.10

Tablo-3.16


#define LED_kontrol PORTA.RA4
#define satir1 PORTB.RB4
#define satir2 PORTB.RB5
#define satir3 PORTB.RB6
#define satir4 PORTB.RB7
unsigned short i, sayac; //evrensel değişken tanımlamaları
char msj01[]="TUSA BASIN";
char msj02[]="BASILAN TUS:";
char sayac_txt[5];
sbit LCD_RS at RA7_bit;
sbit LCD_EN at RA6_bit;
sbit LCD_D4 at RA0_bit;
sbit LCD_D5 at RA1_bit;
sbit LCD_D6 at RA2_bit;
sbit LCD_D7 at RA3_bit;
sbit LCD_RS_Direction at TRISA7_bit;
sbit LCD_EN_Direction at TRISA6_bit;
sbit LCD_D4_Direction at TRISA0_bit;
sbit LCD_D5_Direction at TRISA1_bit;
sbit LCD_D6_Direction at TRISA2_bit;
sbit LCD_D7_Direction at TRISA3_bit;
void ayarlar(){
  sayac=0;
  Lcd_Init();    // LCD başlatılıyor
  Lcd_Cmd(_LCD_CLEAR);
  Lcd_Cmd(_LCD_CURSOR_OFF);
  INTCON=0; //kesmeler kapatıldı
  CMCON = 0X07; // analog karşılaştırıcılar kapatıldı
  TRISA=0X00;
  PORTA=0;
  TRISB=0XF0; //B portunun ilk dört biti çıkış, son dört biti giriş
  PORTB=0;
  for(i=0;i<5;i++){      //Açılışta mikrodenetleyicinin çalışıp
   LED_kontrol=~LED_kontrol; //çalışmadığını anlamamızı sağlayan
   Delay_ms(250);      //kontrol döngüsü
  }
  LED_kontrol=0;
}
char findKey(unsigned short a, unsigned short b) 
{
   if(a == 0)    //Birinci tarama sütunu RB0 portu
   {
      if(b == 5)
        return '1';
      else if(b == 6)
        return '4';
      else if(b == 7)
        return '7';
      else if(b == 8)
        return '*';
   }
   else if(a == 1)  //İkinci tarama sütunu RB1 portu
   {
      if(b == 5)
         return '2';
      else if(b == 6)
         return '5';
      else if(b == 7)
         return '8';
      else if(b == 8)
         return '0';
   }
   else if(a == 2)  //Üçüncü tarama sütunu RB2 portu
   {
      if(b == 5)
         return '3';
      else if(b == 6)
         return '6';
      else if(b == 7)
         return '9';
      else if(b == 8)
         return '#';
   }
   else if(a == 3)   //Dördüncü tarama sütunu RB3 portu
   {
      if(b == 5)
         return 'A';
      else if(b == 6)
         return 'B';
      else if(b == 7)
         return 'C';
      else if(b == 8)
         return 'D';
   }
}
char readKeyboard()
{
 for(i=0;i<4;i++) //B portunun ilk dört pini için tarama
 {                //çıkışı elde ediliyor
  if(i == 0)
   PORTB = 1;
  else if(i == 1)
   PORTB = 2;
  else if(i == 2)
   PORTB = 4;
  else if(i == 3)
   PORTB = 8;

  if(satir1)  //Butona basıldığında sütuna karşılık gelen
    return findKey(i,5);  //satır hattı kısa devre olduğundan
  if(satir2)           //satır hattının girişi lojik-1 olur
   return findKey(i,6);   //Bu bölümde lojik-1 olan satır hattı
  if(satir3)           //kontrol ediliyor
   return findKey(i,7);
  if(satir4)
   return findKey(i,8);
 }
 return ' ';
}
void main(){
 char tus;  /*Tuşa basma durumunu takip etmek için*/
 ayarlar();
 Lcd_Out(1,1,msj01);
 while(1){
  tus = readKeyboard();
  if(tus != ' '){
   sayac++;
   ShortToStr(sayac, sayac_txt);
   Lcd_Out(1,11,sayac_txt);
   Lcd_Out(2,1,msj02);
   Lcd_Chr(2,14,tus);
  }
  Delay_ms(50);
 }
}


EEPROM Veri Belleği İle Şifreli Geçiş Uygulaması

PIC mikrodenetleyicilerinde EEPROM Veri Belleği ve Program Belleği ile haberleşmeyi sağlayan 6 adet özel amaçlı kaydedici bulunur. Bilindiği üzere EEPROM veri belleğidir ve 8 bitliktir. Bu nedenle veri belleğinin kapasitesi Byte birimi üzerinden tanımlanır. Örneğin PIC16F887 denetleyicisi 256 Byte’lık EEPROM’a sahiptir ve adres aralığı 0x00 - 0xFF dir. Program Belleği ise 14 bitliktir ve kapasitesi Word birimi üzerinden tanımlanır. Örneğin PIC16F887 8 KWord (8192 Word) program belleğine sahiptir ve adres aralığı 0x0000 – 0x1FFF dir. Bu kaydediciler ile her iki bellek türü de kontrol edilebilir. Bunlar aşağıdaki gibidir:

  • EECON1 Program ya da veri belleği erişimini kontrol eden kaydedicidir. EECON1 kaydedicisinin EEPGD biti ile hangi bellek türüne erişim yapılacağı belirlenir. Varsayılan olarak ve cihaz her resetlendiğinde veri belleği olarak ayarlanır. Bu bit lojik-1 yapıldığında program belleğine erişim yapılır. Program belleği yalnızca okunabilir, veri belleği ise hem okunabilir hem de yazılabilirdir.
  • EECON2 Fiziksel bir kaydedici değildir. Yalnızca veri EEPROM’una bilgi yazılırken geçici olarak kullanılır. Normal durumda içeriği 0’dır.
  • EEDAT Program belleğinden okunacak 14 bitlik bilginin 8 bitlik LSB kısmını tutan kaydedicidir. Veri belleğine yazılacak ya da bellekten okunacak 8 bitlik veriyi tutan kaydedicidir.
  • EEDATH Program belleğinden okunacak 14 bitlik bilginin 6 bitlik MSB kısmını tutan kaydedicidir.
  • EEADR Program belleği adresinden okuma işlemleri için program belleği adresinin 8 bitlik LSB kısmını tutar. Veri belleğine yazma ve bellekten okuma işlemleri için veri belleğinin 8 bitlik adresini tutar.
  • EEADRH PIC16F887 gibi 8 KWord’lük program belleğine sahip denetleyicilerin program belleğinden okuma işlemleri için adres bilgisinin MSB kısmını tutar.

MikroC programının EEPROM veri belleğine veri yazılması ve bellekten veri okunmasını sağlayan kütüphanesi vardır. Bu komutlar aşağıdaki gibidir:

EEPROM_Read : Belirtilen adresten 1 byte’lık veri alır.
unsigned short take;
Kullanılışı: take = EEPROM_Read(0x3F);
Açıklama: 0x3F adresinden “take” isimli değişkene 1 byte’lık veri alınır.
EEPROM_Write: Belirtilen adrese 1 byte’lık veri yazar.
Kullanılışı: EEPROM_Write(0x32, 19);
Açıklama: EEPROM’un 0x32 adresine 19 desimal bilgisini yazar.


Şifreli Geçiş Uygulaması

Bu devrede PIC16F887 kullanılarak bir şifreli geçiş uygulaması gerçekleştirilmiştir. Devre uygulaması PROTEUS-ISIS® programında yapılmış ve Şekil 3.11’de gösterilmiştir. Devrede 4x3 boyutlu 12 butonluk bir tuş takımı bulunmaktadır. Bu uygulamada mikroC programının “Keypad” kütüphanesinin kullanımı gösterilmiştir. Keypad kütüphanesi ile 4x1, 4x2, 4x3 ve 4x4 boyutlu tuş takımlarını kullanmak mümkündür. Bu uygulamada 4. sütun kullanılmadığından Keypad ile ilişkilendirilen B portunun RB3 hattı şaselenmiştir. Eğer 4x4 boyutlu bir tuş takımı kullanmak isterseniz tuş takımının dördüncü sütunu bu hatta bağlanmalıdır. Önceki iki uygulamadan farklı olarak bu uygulamada satır taraması yapılmıştır ve tüm hatlara 10k’lık dirençler bağlanmıştır.

Tuş takımı ile şifre giriş ve şifre değiştirme işlemleri yapılmaktadır. Giriş ve izleme işlemleri 2x16 LCD ekran üzerinden yapılmaktadır. Giriş işlemi başarılı şekilde gerçekleştiğinde 7 girişli ULN2003A entegresinin iki girişi tetiklenmektedir.

ULN2003A entegresi genellikle röle gibi endüktif yüklerin tetiklenmesinde ya da endüktif sargı içeren DC ve adım motorlarının sürülmesinde kullanılan, açık kollektör darlington çıkışlı bir entegredir. 7 giriş ve 7 çıkışı bulunmakta olup her bir çıkışından 500mA’e kadar akım çekilebilmektedir. Mikrodenetleyiciler ile endüktif yüklerin kontrolünde yaygın olarak kullanılır. Port ve yük arasında iyi bir tampon (buffer) işlevi gördüğünden yüksek akımlı yüklerin kontrolünde mikrodenetleyicinin kararlı çalışmasını sağlar. 8 numaralı bacağı DC şase hattına, 9 numaralı bacağı ise sürülecek rölenin besleme hattına bağlanır.

Mikrodenetleyiciler ile gerçek uygulamalarda röle gibi endüktif ve elektromekanik yüklerin sürülmesi sırasında program akışınızın olağan işleyişi dışında rahatsız edici sorunlarla karşılaşabilirsiniz. Bu durum genellikle kullanılmayan portların dışarıdan etkilerle yüklenmesi, mikrodenetleyicinin kullanılmayacak özelliklerinin devre dışı bırakılmaması, vb. sorunlardan kaynaklanır. Bu nedenle gerçek uygulamalarda mikrodenetleyicilerin boşta kalan port hatlarının şaselenmesi tavsiye edilir. Uygulamada görüleceği üzere tüm boş hatlar şaselenmiştir.

Şifreli giriş uygulamasında sistemin ilk enerjilendirilmesi sonrası yeni şifre girişi için “master” şifreyi girmemiz istenir. “Master” şifre program kodunun içinde “12345” şeklinde 5 rakamlı olarak verilmiştir. İsteğinize göre “master” kodu değiştirilebilir. İlk girişte “master” şifreyi doğru şekilde girip ‘#’ tuşuyla onayladığınızda sizden 4 haneli yeni bir şifre girmeniz istenecektir. Bu dört haneli şifre mikrodenetleyicinin EEPROM’un kalıcı veri hafızası alanında belirtilen adres bölgesine kaydedilecektir.

Şifre giriş ve kontrol işlemleri çok basit bir programlama mantığı ile yürütülmektedir. İlk “master” kodunu girdikten sonra şifre değiştirdiğiniz zaman, daha sonrasında tekrar şifre değiştirmek istediğinizde şifre giriş ekranında iken “master” kodu girmeniz yeterli olacaktır. Bu durumda program sizi “change_code()” fonksiyonuna yönlendirecektir. Bu fonksiyonda dört defa “code_enter()” fonksiyonu çağrılmaktadır. Kullanıcı tarafından basılan tuş değerleri “code1[]” dizi değişkenine kaydedilmektedir. Kod değiştirme fonksiyonu çağrıldığında bu fonksiyon içinden “code_write()” ve “code_read()” fonksiyonları çağrılarak yeni oluşturulan kod hafızaya yazılır ve tekrar hafızadan okunarak “user1[]” dizi değişkenine kopyalanır. Artık giriş işlemlerinde karşılaştırma amaçlı olarak “user1[]” değişkeni kullanılacaktır.

Şifre giriş ya da okuma/yazma işlemlerinde her bir tuş değeri için tek tek ilgili fonksiyonun çağrılması yerine döngü içinde taramayla daha profesyonel ve yapısal bir programlama yapılabilirdi. Ancak programın dikkatli incelenmesi sonucu aynı anda pek çok işlemin çok basit ve pratik bir mantıkla gerçekleştirildiği görülecektir.

Devrede iki ayrı DC besleme vardır. Birinci besleme MCU’nun ve rölelerin beslemesini yapan 5V’luk VCC/VDD kaynağıdır. İkinci besleme ise KAPI olarak adlandırılmış olan DC motorun beslemesini yapan kaynaktır. Her iki DC kaynağın şaseleri VSS olarak ortak bir hatta birleştirilmiştir. PROTEUS-ISIS® programında elektromekanik kapıyı canlandırmak için 12V’luk DC motor kullanılmıştır. VCC/VDD beslemeleri PROTEUS-ISIS® programının “DesignConfigure Power Rails” komutuyla 5V’luk besleme kaynağına ilişkilendirilmiştir (Ayrıntılı bilgi için bkz. Ek C-2). Etiket oluşturma yoluyla da 12V’luk ayrı bir batarya kaynağı kurulmuştur. Kapıyı besleyecek olan bu kaynak rölenin ortak (common) ucuna bağlanmıştır. Aynı şekilde “Design-->Configure Power Rails” komutuyla açılan pencereden 12V’luk bir besleme tanımlaması yapılmıştır.

Şekil-3.11

Devre simülasyonu yapılmadan önce “Edit Component” penceresinden, rölenin çalışma gerilimi 5V, DC motorun ise 12V olarak ayarlanmalıdır.


#define LED_kontrol PORTA.RA0
#define LED PORTA.RA1
#define KAPI PORTA.RA2
unsigned short tus;
char code1[15] ,user1[4];
int i = 0, j, cnt;
char msj01[]="SIFRELI KILIT";
char msj02[]="KAPI ACIK";
char msj03[]="KAPI KAPALI";
char msj04[]="YANLIS SIFRE";
char msj05[]="YENI SIFRE";
char keypadPort at PORTB;// Tuş takımı PORTB için tanımlanıyor
sbit LCD_RS at RD2_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISD2_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
void code_enter(int kon){
 tus = 0; //Tuş kodu resetleniyor
 while(!tus){
  tus = Keypad_Key_Click();// basılan tuş tus değikeninde tutuluyor
 }
 // Basılan tuş değerinin ekran çıktısı için ASCII değeri üretiliyor
 switch (tus){
  case  1: tus = 49; break; // 1
  case  2: tus = 50; break; // 2
  case  3: tus = 51; break; // 3
  //case  4: tus = 65; break; // A 4x4 pad için
  case  5: tus = 52; break; // 4
  case  6: tus = 53; break; // 5
  case  7: tus = 54; break; // 6
  //case  8: tus = 66; break; // B 4x4 pad için
  case  9: tus = 55; break; // 7
  case 10: tus = 56; break; // 8
  case 11: tus = 57; break; // 9
  //case 12: tus = 67; break; // C 4x4 pad için
  case 13: tus = 42; break; // *
  case 14: tus = 48; break; // 0
  case 15: tus = 35; break; // #
  //case 16: tus = 67; break; // D 4x4 pad için
 }
 code1[i] = tus;
 if(kon==0) Lcd_Chr(2, i+1, '*'); // LCD ekranda * değeri basılıyor
 if(kon==1) Lcd_Chr(2, i+1, code1[i]);
 i++;
}
void code_read(){      //EEPROM'dan veri oku
 Delay_ms(20);
 user1[0] = EEPROM_Read(0x00);    // adres 0'daki veriyi oku
 Delay_ms(20);
 user1[1] = EEPROM_Read(0x01);    // adres 1'deki veriyi oku
 Delay_ms(20);
 user1[2] = EEPROM_Read(0x02);    // adres 2'deki veriyi oku
 Delay_ms(20);
 user1[3] = EEPROM_Read(0x03);    // adres 3'deki veriyi oku
 Delay_ms(20);
}
void code_write(){       // Veriyi EEPROM'a yaz
 Delay_ms(20);
 EEPROM_Write(0x00,code1[0]);     // veriyi adres 0'a yaz
 Delay_ms(20);
 EEPROM_Write(0x01,code1[1]);     // veriyi adres 1'e yaz
 Delay_ms(20);
 EEPROM_Write(0x02,code1[2]);     // veriyi adres 2'ye yaz
 Delay_ms(20);
 EEPROM_Write(0x03,code1[3]);     // veriyi adres 3'e yaz
}
void change_code(){
 Lcd_Cmd(_LCD_CLEAR);
 Lcd_Out(1,1,msj05);
 i = 0;
 code_enter(1);
 code_enter(1);
 code_enter(1);
 code_enter(1);
 code_write();
 delay_ms(20);
 code_read();
 delay_ms(20);
 Lcd_Cmd(_LCD_CLEAR);
 Lcd_Out(1,1,msj05);
 Lcd_Out(2,1, "KURULDU");
 Delay_ms(5000);
}

void ayarlar(){
  Keypad_Init(); // Tuş takımı başlatılıyor
  Lcd_Init();    // LCD başlatılıyor
  Lcd_Cmd(_LCD_CLEAR);
  Lcd_Cmd(_LCD_CURSOR_OFF);
  INTCON=0;
  OPTION_REG = 0X87;//Dâhili Pull-up’lar kapalı,düşen kenar
  TRISA=0X00;
  PORTA=0;
  TRISC = 0x00;
  PORTC = 0;
  TRISE=0X00;
  PORTE=0;
  ANSEL  = 0;   // AN pinlerini dijital I/O olarak ayarla
  ANSELH = 0;
  CM1CON0.C1ON=0;   // karşılaştırıcıları kapat
  CM2CON0.C2ON=0;
  ADCON0.ADON=0; //ADC kapatıldı
  for(i=0;i<5;i++){        //Açılışta mikrodenetleyicinin çalışıp
   LED_kontrol=~LED_kontrol; //çalışmadığını anlamamızı sağlayan
   Delay_ms(250);         //kontrol döngüsü
  }
  LED_kontrol=0;
}

void main(){
 ayarlar();
 code_read();
L1:   
 cnt = 0;
 do{
  Lcd_Cmd(_LCD_CLEAR);
  Lcd_Out(1,1,msj01);
  Lcd_Out(2,3,"*'a BASIN");
  i = 0;
  code_enter(1);
  if(code1[0] == 42){      // *
   Lcd_Cmd(_LCD_CLEAR);     
   Lcd_Out(1, 1, "SIFREYI GIRIN");
   i = 0;
   code_enter(0);
   code_enter(0);
   code_enter(0);
   code_enter(0);
   code_enter(0);
   if(code1[0] == '1' && code1[1] == '2'  && code1[2] == '3'
   && code1[3] == '4' && code1[4] == '5' ){      // master kod
    code_enter(0);
    if(code1[5] == 35){      // #
      change_code();
    }
   }
   else if(cnt<3 && code1[0] == user1[0] && code1[1] == user1[1]
   && code1[2] == user1[2] && code1[3] == user1[3] && code1[4] == 35){
// şifre onayını kaydet
    Lcd_Cmd(_LCD_CLEAR);
    Lcd_out(1,4,msj02);  //Kapı açık
    LED=1;
    KAPI=1;
    Delay_ms(5000);
    
    LCD_Cmd(_LCD_CLEAR);
    Lcd_out(1,4,msj03); //Kapı kapalı
    LED=0;
    KAPI=0;
    Delay_ms(2000);
   }
   else{
    cnt++;
    Lcd_Cmd(_LCD_CLEAR);
    Lcd_Out(1,1,msj04);
    Delay_ms(2000);
   }
   if(cnt>=3)
   goto L1;
  }
 }while(1);
}


mikroC'de Motor Kumandası

Mikrodenetleyici ile motor sürme uygulamaları endüstriyel ve ticari olarak çok yaygındır. Çizgi izleyen robotlarda olduğu gibi devirli olarak hareket eyleminin gerçekleştirilmek istendiği yerlerde DC motorlar, rotasyonel mekanik sistemlerin belli bir açıyla dönmesinin istendiği yerlerde adım motorlar, düşük akımlarla yüksek tork üretilmesi istenen yerlerde servo motorlar tercih edilir. Parayla çalışan otomatlarda, vurmalı yazıcılarda, tarayıcılarda, tıbbi cihazlarda, mekanik zoom özellikli görüntüleme aygıtlarında, sıvı pompalarında, 3D yazıcılarda ve mini CNC tezgâhlarında farklı amaçlar doğrultusunda bu motorlardan yararlanılmaktadır.

Adım Motor Kumandası

Fırçasız doğru akım (DA) motorları sınıfına giren adım motorları, bünyesinde barındırdığı sargılara sinyal uygulandığında belli değerde hareket eden, dolayısıyla elektrik sinyallerini mekanik güce çeviren elektromekanik elektrik makinalarıdır. Ayrıca, adım motoru fırçasız, tam bir turunu eşit adımlara bölen senkronize elektrik motorudur. Sisteme uygun güçte seçildiği takdirde motor pozisyonu geri besleme mekanizmasına gerek duymadan tam kararlılıkla kontrol edilebilir.

Normalde adım motorları DC motorlara göre daha karmaşık bir yapıya sahiptirler. Bu nedenle hız ve adım kontrolü istenmeyen yerlerde DA motorlar kullanılabilir. Yapacağımız uygulama motor seçiminde çok önemlidir. Çünkü kullanacağımız motoru seçerken bazı kriterleri bilmek durumundayız. Bir adım motoru için her sinyal uygulanışında dönme açısının kaç olduğu veya toplam kaç sinyalde turunu tamamlayacağı önemli parametrelerdir.

Dönme açıları motorların üzerinde yazılıdır. Üzerinde açı değerleri yazılı olmayan motorların bir tam turu kaç adımda tamamladığı el ile sayılır. 360 derecelik açı, bir tam turdaki adım sayısına bölünür ve her bir adımın kaç derece olduğu tespit edilir. Bu değer küçüldükçe motorun hassasiyeti artar. Adım açısı motorun yapısına bağlı olarak 90o, 45o, 18o, 7.5o, 1.8o ya da daha farklı açılarda olabilir.

Şekil-3.12 Adım motor ve iç yapısı

Adım motorlar Şekil 3.12’de gösterildiği üzere 8 stator (endüktör) sargısından oluşur. Ancak şekildeki adım motorun stator kutup sayısı 4’tür. Adım motorların stator ve rotorları elektromagnet özelliklidir. Şekil 3.12’deki adım motorun rotor kutup sayısı 6 olup dönme hareketini nasıl kazandığı Şekil 3.13’te gösterilmiştir. Stator sargılarına uygulanan elektrik yönüne göre sargıların N (North - Kuzey) ve S (South-Güney) yönleri değiştirilir.

Şekil-3.13 Unipolar adım motor rotorunun manyetiklenmesi ve 15o’lik açılı hareketin elde edilmesi

Adım motorlarının çok farklı türleri vardır. Kitap kapsamında iki fazlı adım motoru kullanılacaktır. İki fazlı adım motorları da kendi içinde Unipolar (Evrensel kutuplu) ve Bipolar (Çift kutuplu) olarak ikiye ayrılır. Unipolar adım motorları faz başına iki sargıdan oluşur. Akım yönü değiştirilmeden polarizasyon yönleri değiştirilebilir. Bu tip adım motorların komütasyonu daha kolaydır. Genelde her sarım için bir uç ortaktır. 2 fazlı unipolar adım motorlarında 5 ya da 6 kablo bulunur. 5. ve 6. kablolar ortak uçtur. 5 kablo bulunanlarda Şekil 3.13’te gösterildiği gibi ortak uçlar içerden birleştirilir. Ortak uç ölçü aletinin direnç kademesi kullanılarak tespit edilebilir. Bunun için ölçü aletinin problarından biri ortak ucabağlıyken diğer prob kalan uçlar arasında değiştirildiğinde aynı direncin okunması gerekir. Ortak uçlara beslemenin (+) kutbu bağlanır. Her iki çeşit kablolamada da çalışma sistemi aynıdır. Ortak kablonun dışında kalan diğer kablo uçlarına uygun sıralamada lojik-0 uygulayarak çalıştırılırlar. Sıralama hatalı yapıldığında motorda titreme meydana gelir. Şekil 3.13’te çalışma ilkesi gösterilen adım motorda her bir adım açısı 15o’dir. φ1, φ2, φ3 ve φ4 sargı uçlarına şekilde gösterilen darbelerin uygulama sırasına göre motor saat yönü ya da saat yönünün tersine döndürülür.

Bipolar adım motorlarında faz başına tek sarım vardır. Manyetik polarizasyonu ters çevirmek için akım yönünü terslemek gerektiğinden H-köprüsü denen devreler kullanılır. Bipolar adım motorların komütasyonu daha karmaşık olmakla birlikte torkları daha kuvvetlidir. Şekil 3.14’te 2 fazlı adım motorlarına örnek gösterilmiştir. 8 kablolu unipolar adım motorları da vardır. Adım motorları piyasada farklı renk kodlarıyla bulunur. Devre bağlantısı öncesinde renk kodlarına göre tetikleme sıralaması tespit edilmeli bağlantı ya da program kodlaması ona göre yapılmalıdır.

Şekil-3.14 2 Fazlı adım motorları

Şekil-3.15 Adım motor rotorunda kutuplar ve manyetik yönler

Dönme açıları motorların üzerinde yazılıdır. Üzerinde açı değerleri yazılı olmayan motorların bir tam turu kaç adımda tamamladığı el ile sayılır. 360 derecelik açı, bir tam turdaki adım sayısına bölünür ve her bir adımın kaç derece olduğu tespit edilir. Bu değer küçüldükçe motorun hassasiyeti artar. 360o’lik dönme hareketi için gerekli adım miktarı statora sarılan sargıların faz sayısına ve motorun çıkıntılı rotor kutup sayısına bağlıdır. Rotor kutuplarının nasıl göründüğü ve nasıl manyetize edildiği Şekil 3.15 ve Şekil 3.16’da gösterilmiştir.

Şekil-3.16 Adım motor rotorunda S (mavi renkli olan) ve N (kırmızı renkli olan) kutuplarının pozisyonları

Adım motorun dönme açısı Denklem 3.1’de gösterildiği şekilde hesaplanır.

Denklem 3.1

Burada; θs = Adım motorun hareket açısı
Ns = Statorun faz sayısı
Nr = Rotorun çıkıntılı kutup sayısını ifade eder.

Adım motorların dönme açısı üzerlerinde yazmaktadır ve dolayısıyla Denklem 3.1 pratikte kullanılmaz. Tam bir turu dönme açısına böldüğümüzde adım motorun toplam adım sayısını hesaplamış oluruz.

Denklem 3.2

Burada S toplam adım sayısını ifade etmektedir. Step motorlar, yarım adım modunda çalıştıkları zaman hassaslıkları daha fazla artar. Örneğin; 600 adım/tur değerindeki bir adım motor, yarım adım modunda çalışırken her turda 1200 adım yapar. Bu da 0.60’a göre daha hassas olan 0.30’luk bir adım açısı anlamına gelir.

Adım motorlarının etiket değerlerine dikkat ederek çalışma özellikleri belirlenebilir. Etiketler üzerinde genellikle her bir tetiklemeden çektikleri akım, hareket açısı, çalışma voltajı gibi dikkat edilmesi gereken değerler bulunmaktadır. Şekil 3.17-a’da 0.48 Amperlik ve 1.8o’lik bir bipolar adım motorun değerleri etiketi üzerinde gösterilmiştir.

Şekil-3.17 (a) 0.48A'lik ve 1.8o'lik 2 fazlı bipolar adım motoru, (b) Unipolar 6 kablolu adım motoru

Adım motorlar pratikte tek başına besleme kaynağına bağlanarak kullanılamazlar ve sargı uçlarının sıralı şekilde tetiklenerek dönme hareketinin sürekli hale getirilmesi gerekir. Bunun için adım motor sürücü (step motor driver) devreleri kullanılır. Darlington transistör çiftleriyle yapılmış yüksek akımlı sürücü devreleri tasarlayabileceğiniz gibi hazır sürücü entegreleri de kullanabilirsiniz.

Adım motorları çoğunlukla 1 fazlı, 2 fazlı ya da 1-2 fazlı olarak sürülürler. Şekil 3.14’te gösterilen 6 kablolu unipolar adım motorunun 1 fazlı ve 2 fazlı olarak sürülmesi Tablo 3.18’de gösterilmiştir. Şekilde gösterilen adım motorunun kablo renkleri Turuncu (T) , Mavi (M), Sarı (S) ve Kırmızı (K) şeklindedir. Ancak tablodan görüldüğü üzere renk sıraları T-K-M-S şeklindedir. Motorun Siyah ve Beyaz kabloları ortak uçlardır. Tabloda dikkat edilirse motor sargılarının tetiklenmesi için lojik-1 uygulandığı görülmektedir. Aslında lojik-0 uygulanması gerektiği söylenmişti. Burda lojik-1 uygulanıyor gözükmesinin sebebi sürücü devrelerinin çalışma ilkesindendir. Diğer bir ifadeyle tabloda gözüken lojik-1 değerleri transistör baz tetikleme gerilimlerini ifade etmektedir.

Tablo-3.18 Adım motorun hareket yönüne göre 1 ve 2 fazlı olarak sürülmesi

Kullandığınız adım motoruna bağlı olarak besleme sıralamaları değişiklik gösterebilir. Bunun için ürün etiket bilgisine başvurmakta fayda vardır. Şekil 3.18’de, 6 kablolu unipolar bir adım motorun transistörlü bir sürücü devresi yardımıyla mikrodenetleyici ile kontrol devresi gösterilmiştir. Devre Labcenter Electronic™ firmasının Proteus-ISIS® elektronik devre çizimi ve simülasyonu programında gerçekleştirilmiştir. Devrede dikkat edildiği üzere adım motorların transistör ile sürülmesinde kollektör ve emetör ayakları arasına ters yönlü diyot bağlanmaktadır. Adım motorların sargısı birer bobin olarak davranır ve DC gerilimin ilk uygulanmasında ters EMK’dan dolayı transistörün zarar görmesi durumu söz konusudur. Bunu engellemek için transistörün C-E arasına ters diyot bağlanır. Ayrıca elektrik kesildiğinde oluşan zıt yönlü akım bu diyot üzerinden dolaştırılır.

DİKKAT: Adım motorların bobin sargılarının iç direnci düşük olduğundan tetikleme anında transistörden fazla akım geçer. Devrede BDX53 transistörü kullanılmıştır. Bu transistörler sırtlarına soğutma levhası bağlanarak soğutulabilir. Daha yüksek akımlı adım motorlarının komütasyonu için darlington çifti transistörler kullanılabilir. Adım motorun yeterli kuvvet ile dönebilmesi için, ortak bağlantı ucundan verilecek DC besleme voltajının mikrodenetleyiciyi besleyen +5V’luk VCC/VDD beslemesinden farklı olması daha uygun olur. 5A/12V’luk bir anahtarlamalı tip (Switch Mode) DC güç kaynağı adım motorunun beslenmesi için uygun olacaktır.

Devre şemasına dikkat edilirse kullanılmayan boşta kalmış pinler şaselenmiştir. Özellikle elektromekanik bileşenlerin kontrolünde bu önemli bir işlemdir. Boşta kalan bacaklar şaselenmek suretiyle MCU’nun kararlı çalışması sağlanır. Dış ortamdan kaynaklı parazitlerden etkilenme olasılığı azaltılır.

Şekil-3.18 Adım motorun transistörlü olarak mikrodenetleyici ile sürülmesi

Şekil 3.19’da gösterilen devrede ise sürücü entegresi kullanılmıştır. ULN2003A yüksek akımlı darlington bağlı transistör dizisinden oluşan 7 kanallı bir sürücü entegresidir (Bkz. Ek-I1). Özellikle 2 fazlı unipolar adım motorların sürülmesinde yaygın olarak tercih edilir. Şekildeki devrede entegre çıkışlarına farklı olarak LED diyotlar bağlanmıştır. Böylece aktif olan çıkış anlık olarak izlenebilmektedir.

Şekil-3.19 Adım motorun transistörlü olarak mikrodenetleyici ile sürülmesi

Piyasada ULN2003A entegresi kullanılarak gerçekleştirilmiş çok sayıda hazır sürücü modülü vardır. Şekil 3.20’de PIC mikrodenetleyicileri tarafından da rahatlıkla kullanılabilecek ULN2003A entegreli bir sürücü modülü görülmektedir.

Şekil-3.20 ULN2003A hazır motor sürücü kartı

Şekil 3.18 ve Şekil 3.19’daki devrelerin mikrodenetleyici ile kontrol edilmesi için mikroC’de yazılmış olan program kodları sırasıyla Tablo 3.19 ve Tablo 3.20’de gösterilmiştir. Tablo 3.19’da gösterilen program yardımıyla adım motor, PIC16F628A’nın RB0 harici kesme girişindeki buton yardımıyla tetiklenmekte ve kendi etrafında 360 derecelik tam tur hareket yaptıktan sonra saat yönünün tersinde dönerek başlangıç noktasına geri gelmektedir.

Program kodunda görüldüğü üzere başlangıçta adım motoru kontrol etmek için kullanılan PORTA’nın tüm pinleri 0xFF yapılmak suretiyle set edilmektedir (Lojik 1 yapılmaktadır). Adım motorların tüm girişlerine lojik-1 ya da lojik-0 uygulandığında adım motoru hareketsiz durur.

Tablo 3.19 Adım motoru ileri-geri tam tur döndüren program kodu

unsigned short sure=50;
unsigned short sayac=0;
unsigned short jeton=0;
unsigned short bitti=1;
void main()
{
  TRISA = 0X00; // PORT A çıkış
  TRISB = 0X01; // RB0 giriş diğerleri çıkış
  PORTA = 0X00;
  PORTB = 0X00;
  CMCON = 0X07; // Karşılaştırıcılar kapatıldı
  OPTION_REG = 0X47;/* Yükselen kenar tetiklemesi ve dâhili pull-up'lar*/
  INTCON.GIE=1; // Evrensel kesme aktif
  INTCON.INTE=1; // Harici kesme aktif
}
void mendil(){
if(jeton){
     do
     {      // Adım motoru saat yönünde döndüren döngü
       sayac++;
       PORTA = 0b00001000;
       VDelay_ms(sure);
       PORTA = 0b00000100;
       VDelay_ms(sure);
       PORTA = 0b00000010;
       VDelay_ms(sure);
       PORTA = 0b00000001;
       VDelay_ms(sure);
     }while(sayac<=50);
     sayac=0;
     do
     {       // Adım motoru ters yön döndüren döngü
       sayac++;
       PORTA = 0b00000010;
       VDelay_ms(sure);
       PORTA = 0b00000100;
       VDelay_ms(sure);
       PORTA = 0b00001000;
       VDelay_ms(sure);
       PORTA = 0b00000001;
       VDelay_ms(sure);
     }while(sayac<=50);
     sayac=0;
     bitti=1;
     jeton=0;
    }
}
void interrupt(){ //kesmenin kontrol edildiği fonksiyon
 if(bitti){
  jeton=1;
  bitti=0;
  mendil();
 }
 INTCON.INTF=0;
 Delay_ms(20);
}


Şekil 3.21’de transistörlü adım motor devresinin PROTEUS-ARES®’de gerçekleştirilmiş baskı devresi gösterilmiştir. Baskı şemadan anlaşılacağı üzere transistör sırtları soğutucu bağlanabilmesi için dışarı yöne gelecek şekilde yerleştirilmişlerdir. Devre tam olarak 5x5 cm ebatlarına sığacak şekilde tek katmanlı olarak tasarlanmıştır. Şekil 3.21’de devre elemanlarının montajlanmış görüntüsü gösterilmiştir. Baskı devre tasarımının en az malzemeyle yapılması için Şekil 3.18’deki devrenin “Başlat” butonu dirençsiz bağlanmıştır. Bu işlem mikrodenetleyicinin programlanması sırasında dâhili çekme direncinin aktif kılınmasıyla sağlanmıştır. Ayrıca PIC16F628A’nın dâhili 4 MHz osilatör seçeneğinde çalışabilme özelliği mikroC programında aktifleştirilerek (Bkz. Ek-D) harici osilatör devresi elemanlarından tasarruf sağlanmıştır.

Şekil-3.21 Transistörlü adım motor kumanda ve sürücü devresinin baskı şeması

Reset girişine bağlanan R1 direnci PCB üzerine mikrodenetleyicinin altına denk gelecek şekilde yerleştirilmiştir. Uygulanan bu yöntemler ile daha sade görünümlü ve küçük bir alana sığabilen kart tasarımı sağlanmıştır. PROTEUS-ISIS® programı çalışma alanında +5V CONN-SIL2 konnektörü simülasyondan hariç tutulmuştur (Eleman üzerine sağ tıklanır Edit Properties-->Exclude from Simulation seçeneği işaretlenir). Ayrıca Mikrodenetleyici entegrelerinin VDD +5V besleme bacağı PROTEUS-ISIS® programında gizli tutulmaktadır. PCB tasarım uygulaması ARES’te bu bağlantının 5V’luk besleme girişine bağlanması için ISIS çalışma alanındaki “Power” konnektörleri VCC/VDD olarak ayarlanmış ve Design-->Configure Power Rails komutuyla açılan pencerede VCC/VDD hatları 5V’luk besleme sınıfına dâhil edilmiştir.

Şekil-3.22 Transistörlü adım motor kumanda ve sürücü devresinin malzeme yerleşim düzeni

DİKKAT: Bu tür yüksek akımlı devrelerin PCB tasarımlarında dikkat edilmesi gereken bir diğer önemli nokta kartın elektriksel şaselenmesidir. Eğer PCB’nizi PROTEUS-ARES® gibi bir PCB hazırlama programında hazırlıyorsanız, “Zone Mode” özelliğini kullanarak yolların dışında kalan alanları bakır kaplama şeklinde oluşturabilirsiniz. Bu kaplamayı devrenin elektriksel şasesine bağladığınızda baskı devre şeklinde geniş ve yekpare bir şase alanı oluşturmuş olursunuz. Böylece transistörler ya da entegre üzerindeki ısının dağılması kolaylaşır.

Yüksek torklu bipolar adım motorlarının kontrolü için H-köprüsü denen transistörlü ya da MOSFET’li karmaşık motor akım yönü kontrol devreleri kullanılır. Bu devrelerin karmaşıklığını ortadan kaldıran L293D entegeresi daha basit bir çözüm sunar (Bkz. Ek-I2). Şekil 3.23’te bipolar adım motorlarının komütasyonu için tercih edilen L293D entegresinin MCU ve motor arasına nasıl bağlanacağı gösterilmiştir.

Şekil-3.23 L293D ile bipolar adım motor kontrolü

DC Motor Kumandası

Diğer endüktif yükler gibi DC motorlar da doğrudan MCU’ya bağlanamazlar. Hem yüksek akım gerektirmeleri hem de zıt EMK’nın MCU üzerindeki olumsuz etkisi nedeniyle yardımcı devre elemanları kullanılması gerekir. DC motorları kontrol etmede yaygın olarak kullanılan elektronik devrelerin en önemlisi H-köprüsüdür. Şekil 3.24’te H-köprüsünün prensip şeması gösterilmiştir.

Şekil-3.24 H-köprüsü prensip şeması

Transistörler kullanılarak hazırlanan bu devrelerin komütasyonu MCU’lar ile yapılabilir. H-köprüleri çok sayıda transistör (BJT) ya da MOSFET yarıiletken elemanlarının özel olarak bağlantısıyla elde edilir. Karmaşık yapıları kullanımlarını zorlaştırır. Bu amaçla son kullanıcıya daha esnek kullanım olanağı sunan entegre devre sistemleri geliştirilmiştir. Piyasada en bilinen motor sürücüsü dört kanallı yarım-H sürücüsü L293 ya da L293D entegreleridir (Bkz. Ek-I2). Prensip şemada gösterilen anahtarlama mantığından anlaşılacağı üzere, anahtarlar yardımıyla DC motorun dönme yönü değiştirilebilir. Ancak H-köprüsü gibi elektronik devrelerle ya da L293 gibi yarım-H sürücüleriyle bu işlem elektronik olarak hızlı şekilde gerçekleştirilir. Ayrıca yüksek frekanslı tetiklenme özelliği sayesinde motor devri kolaylıkla ayarlanabilir.

L293D entegresi içinde dört adet yarım-H sürücüsü içerir. İki yarım-H sürücüsü bir tam H-köprüsünü meydana getirir. Sonuç olarak L293D entegresiyle 2 adet DC motorun ileri-geri tam kumandası sağlanabilir. Şekil 3.25’te L293D entegresinin PIC16F628A MCU’ya bağlantısı gösterilmiştir. Daha önceki endüktif malzeme kumandası uygulamalarında yapıldığı üzere MCU’nun kullanılmadığı bacaklar şaselenmiştir. Ayrıca çıkış bacaklarına motor gibi selenoid yükleri doğrudan bağlayabilirsiniz. Çıkış diyotları entegre devre paketi içinde tümleşik olarak yer almaktadır.

Şekil-3.25 DC motorun L293D entegresi yardımıyla sürülmesi

Piyasada, farklı üreticiler tarafından tasarlanmış hazır L293D sürücüsü kartlarını bulabilirsiniz. Kartın üzerinde 2 adet DC motor bağlantısı, MCU için kumanda bağlantıları ve VCC/Motor besleme girişleri bulunmaktadır.

Şekil-3.26 L293D hazır DC motor sürücü kartı

Şekil 3.25’teki devre için mikroC’de yazılmış program kodu Tablo 3.20’de gösterilmiştir. Devrede tek bir DC motorun ileri-geri kumandası sağlanmaktadır. MCU’nun RB0/INT girişine bağlı butona basılıp bırakıldıktan sonra ‘interrupt()’ rutini devreye girer. Önce 2 saniyelik bir bekleme yapılır ardından DC motor hareket ettirilir. Her butona basıldığında bir önceki dönme hareketinin tersi yapılır. Motor saat yönünde dönüyorsa ve butona basılıp bırakılırsa 2 saniyelik beklemenin ardından motor saat yönünün tersine döndürülür. Çıkışların terslenmesi sırasında bekleme yapılması, motor gibi endüktif elemanların akım yönünün voltaj değişimine hemen tepki verememesindendir. Motor kutuplarındaki voltaj yönü değiştirilse de akım yönü hemen değişmez. Ayrıca motor rotorunun atalet momentinden dolayı hareket yönünü anında değiştirmesi de mümkün değildir.

Tablo 3.20 DC motor kumandası program kodu

unsigned short ters=0; // DC motor yön değişimini takip eden değişken
void main()
{
 TRISA = 0X00; // PORT A çıkış
 TRISB = 0X01; // B0 hariç PORTB çıkış
 PORTA = 0X00; 
 PORTB = 0X00;
 CMCON = 0X07;//Analog karşılaştırıcılar kapatıldı
 OPTION_REG = 0X47; //Dâhili pull-up aktif, yükselen kenar tetiklemesi
 INTCON.GIE=1;
 INTCON.INTE=1;

}
void interrupt(){
 ters=~ters; //Butona her basılıp bırakıldığında yön değiştiriliyor
 INTCON.INTF=0;
 PORTA=0; // L293D çıkışları kapatılıyor
 Delay_ms(2000); //Geçişler arası bekleme
 if(ters) PORTA = 0b00000100;//CW saat yönü
 else if(!ters) PORTA = 0b00001000; //CCW saat yönünün tersi
}


MCU’nun kontrol portunun bit değerlerine ve motor sürücüsü L293D entegresinin girişlerine göre motorun davranışı Tablo 3.21’de gösterilmiştir.

Tablo-3.21 Kontrol sinyalleri ve motor durumu

Tablodan görüldüğü üzere L293D entegresinin IN1 ve IN2 girişlerine Lojik-0 ya da Lojik-1 uygulandığında motor durmaktadır.

Şekil 3.27’de DC motor kumanda devresinin PROTEUS-ARES®’de gerçekleştirilmiş baskı devresi gösterilmiştir. Baskı devreye dikkat edilirse, L293D entegresinin baskı devre üzerinden soğutulmasını kolaylaştırmak için şase alanının geniş tutulduğu görülmektedir (Bkz. Ek-I2). Ayrıca L293D entegresinin 5V VCC ve 12V motor besleme girişlerine elektrolitik kondansatörlerin yakın olarak yerleştirildiği görülmektedir. Baskı devre tek katmanlı olacak şekilde tasarlanmıştır.

Şekil-3.27 DC motor kumanda kartının baskı şeması

Şekil 3.28’de malzeme yerleşim düzeni gösterilmiştir. Kart 5cm X 5cm boyutlarında olacak şekilde tasarlanmıştır. Baskı devrenin dolgu şase yoluyla yapılması nedeniyle kart boyutu normalde olabileceğinden biraz daha büyük tasarlanmıştır.

Şekil-3.28 DC motor kumanda devresinin malzeme yerleşim düzeni

Servo Motor Kumandası

Servo motorlar temel olarak içinde vites sistemi kurulu bir DC motor türüdür. Servo motorların içinde DC motorun dönme yönünü ve konumunu kontrol eden elektronik devre bulunur. Servo motorlarda DC motorun komütasyonu, darbe genişliğinin istenen oranda ayarlanabildiği PWM (Pulse Width Modulation – Darbe Genişliği Modülasyonu) sinyaliyle gerçekleştirilir. PWM sinyalinin kullanımı diğer sıradan DC motorlarda gerçekleştirilen devir kontrolünden farklıdır. Servo motorlarda PWM sinyaliyle motorun dönme yönü ve konumu ayarlanır. Çoğu servo motor 50 Hz’lik PWM frekansında etkili çalışır. Bu 20 msn’lik periyot anlamına gelir. Servo motorun içindeki elektronik devre PWM sinyalinin genişliğine tepki verir. 0,7 ms’den 1 ms’ye PWM genişliği servo motorun saat yönünde (CW), 1,7 ms’den 2 ms’ye PWM genişliği servo motorun saat yönünün tersine (CCW) dönmesini sağlar.

Standart servo motor için 1,5 ms PWM genişliği motoru merkez noktasına döndürür (Bkz. Şekil 3.29). Motorun ne kadar döneceği uygulanan sinyalin süresine bağlıdır. Proteus-ISIS® ortamında tam CW yönünde hareket için 2 msn’lik, tam CCW yönünde hareket için 0,7 msn’lik darbe gerekmektedir. 1,7 – 2 ms arasındaki diğer değerlerde motor açılı olarak CW yönünde, 0,7 – 1 ms arasındaki değerlerde ise açılı olarak CCW yönünde döner.

Şekil-3.29 Servo motora uygulanan PWM sinyaline göre motorun tam hareket yönü

Anlatılan bu teknik bilgiler 180o’lik açı hareketi yapan servo motorlar içindir. Ayrıca 360o hareket edebilen tam tur servo motorlar da vardır. Servo motorların en önemli özelliği istenilen açı değerine tam ve kesin olarak ayarlanabilmesidir. Bu işlemi sıradan bir DC motorda ya da yalnızca belli açılar arasında hareket edebilen adım motorlarında yapmak mümkün değildir. Robot uygulamalarında, radyo kumandalı araçlarda, endüstriyel makinelerde ve daha pek çok alanda yaygın olarak kullanılırlar. Kumandalarının kolaylığı, düşük enerji gerektirmeleri, yüksek tork, TTL seviyesinde voltajla kumanda edilebilmeleri, küçük boyutları ve hafiflikleri servo motorların tercih sebebidir.

Şekil-3.30 Solda plastik dişli, sağda metal dişli iki faklı servo motor

Servo motorların üzerinde genellikle 3 kablo bulunur (Bkz. Şekil 3.30). Kablolardan biri GND, diğeri VCC, üçüncüsü ise kontrol içindir. Genellikle kahverengi ya da siyah olan negatif besleme, kırmızı olan pozitif besleme ve turuncu ya da beyaz olan kumanda sinyali içindir. Besleme bağlantıları yapıldıktan sonra kumanda hattına sinyal uygulandığında motor dönmeye başlar.

Değişen genişlikli sabit genlikli bir sinyali MCU ile üretmek için PWM (Pulse Width Modulation – Darbe Genişliği Modülasyonu ) özelliği bulunan MCU’lar kullanılabilir. MikroC PRO for PIC® yazılımı, CCP (Capture/Compare/PWM) hattına sahip PIC mikrodenetleyicileri için kullanılabilen PWM kütüphanesi sunmaktadır. İlgili MCU için PWM kütüphanesi kullanıldığında üretilecek PWM sinyali için bir periyot/frekans belirlenmesi gerekir. MCU’nun çalışma frekansına bağlı olarak seçilebilecek frekans belli bir sınır aralıkta olur. Örneğin MCU’nuz için osilatör frekansı 4 MHz ise çalışma frekansı bunun 4’te 1’i kadardır ve dolayısıyla 1 MHz’lik çalışma frekansı elde edilir. Bu durumda MikroC’de ilgili MCU için en az 245Hz’lik sinyal elde edebilirsiniz. MCU’nuz 1 MHz OSC sinyaliyle çalışıyorsa çalışma frekansı 250kHz olacaktır. Bu durumda elde edilebilecek minimum PWM frekansı 62Hz olacaktır. Yüksek frekansta çalışan MCU uygulamalarınızda, MCU’nun PWM çıkışı 50Hz seviyesinde sinyal üretimi sağlayamaz. 50Hz’lik periyoda tepki veren servo motorlar için gerekli sinyalin üretiminde temel olarak iki yol kullanılmaktadır.

Birinci yöntemde yapay gecikme üretilerek MCU’nun gerekli olan periyot süresince bekletilmesi sağlanır. Bunun için MikroC’de “Delay_ms(x)” komutu kullanılır. “x” yerine elde etmek istediğiniz bekleme süresi milisaniye cinsinden yazılır. İkinci yöntemde ise MCU’nun Timer0 ya da varsa Timer1/Timer2 zamanlayıcıları kullanılarak istenen bekleme süresi elde edilir. Zamanlayıcı kullanılarak yapılan uygulama yapay gecikme ile yapılan uygulamadan daha sağlıklıdır. Ancak zamanlayıcıların kullanımı kesmeler konusunda anlatılacağından bu kısımda yapay bekleme ile servo motorun kullanımına örnek verilecektir. Bunun için kurulacak örnek uygulama devresi Şekil 3.31’de gösterilmiştir.

Şekil-3.31 Servo motorun kumandası

Tablo 3.22 Servo motor için yön kontrol program kodu

#define SERVO PORTB.RB1 //B portunun B1 pini motor için tanımlanıyor
void servoRotate0() //Servo motoru merkeze konumlandıran program bloğu
{
  unsigned int i;
  for(i=0;i<5;i++)//Motorun istenen noktaya konumlanmasını
  {                 //garanti etmek için yeterli sayıda darbe uygulanıyor
    SERVO = 1;
    Delay_us(1500);
    SERVO = 0;
    Delay_us(18500);
  }
}
void servoRotate90() //Saat yönünün tersine döndüren blok
{
  unsigned int i;
  for(i=0;i<5;i++)
  {
    SERVO = 1;
    Delay_us(700);
    SERVO = 0;
    Delay_us(19300);
  }
}
void servoRotate180() //Saat yönünde döndüren blok
{
  unsigned int i;
  for(i=0;i<5;i++)
  {
    SERVO = 1;
    Delay_us(2000);
    SERVO = 0;
    Delay_us(18000);
  }
}
void main()
{
  TRISA = 0; // PORTA çıkış olarak ayarlanıyor
  TRISB = 0; // PORTB çıkış olarak ayarlanıyor
  PORTA = 0;
  PORTB = 0;
  CMCON = 0X07; // Analog girişler Dijital I/O olarak ayarlanıyor
  OPTION_REG = 0X07;
  while(1)
  {
    servoRotate90(); //CCW
    Delay_ms(1000);
    servoRotate0(); //Merkeze
    Delay_ms(1000);
    servoRotate180(); //CW
    Delay_ms(1000);
    servoRotate0(); //Merkeze
    Delay_ms(1000);
  }
}


Örnek program kodu derlenip MCU devresinde çalıştırıldığında servo motor ilk enerjiyi almasıyla birlikte merkeze konumlanır. Ardından sırasıyla CCW-Merkez-CW-Merkez şeklinde sürekli olarak hareket eder. Program koduna dikkat edileceği üzere, uygulanan darbe süresiyle bekleme süresinin toplamı 20.000 (20 ms) yapmaktadır.

Gerçek uygulamada kullandığınız servo motora göre, tam CW ve tam CCW yönündeki hareketleri elde etmek için uygulamanız gereken süre değişkenlik gösterebilir. Deneme yaparak ya da varsa ürün kataloğundan ideal tepki süresini tespit edebilirsiniz. Ara değerler uygulayarak motorunuzun belli bir açıda dönmesini de sağlayabilirsiniz.

mikroC'de Grafik LCD Kumandası

MikroC IDE programının Tools → GLCD Bitmap Editör menüsünden ulaşılabilen bir grafik LCD karakter üretme aracı vardır. Bu ekran yardımıyla tek renk (monochrome) formatta ve bitmap (.bmp) uzantısıyla oluşturulmuş bir resmin seçilen grafik LCD türü için kod dönüşümü yapılır. Elde edilen bu kod programlama editöründe kullanılarak, mikrodenetleyiciniz tarafından grafik LCD ekran üzerinde kontrollü şekilde dönüştürülmüş resmi göstermeniz sağlanır.

Grafik LCD’ler yardımıyla çözünürlük sınırları içinde kendi karakter stilinizi de tasarlayabilirsiniz. Üreteceğiniz karakterlerin kodlarını kolay yoldan tespit edebilmek için programın Tools → LCD Custom Character aracını kullanabilirsiniz. Bu aracın kullanımıyla ilgili ayrıntılı bilgi için EK-I5’e bakmanız tavsiye edilir. Karakter kümesi için bir look-up tablosu oluşturarak bu tabloyu kullanmak suretiyle istediğiniz yazıları Türkçe karakter sorunu yaşamadan yazdırmanız mümkündür.

Editörün bize sunduğu dönüştürme aracında KS0108, T6963 ve Nokia 3310 grafik LCD türleri için dönüşüm yapılmaktadır. Piyasada yaygın olarak Samsung firmasınca geliştirilmiş olan KS0108 sürücüsü kullanılmaktadır. Uygulamamızda KS0108 grafik LCD sürücüsü kullanılarak PIC18F4550 yardımıyla örnek bir grafik oluşturulması gösterilecektir. Kitap içinde kullanılan diğer mikrodenetleyiciler GLCD uygulamalarında yavaş kalmaktadır. 18F serisi mikrodenetleyiciler bu konuda daha yüksek performans gösterir. Mikroc IDE programının gelişmiş kütüphane arşivi içinde ayrıntılı bir grafik LCD kütüphanesi de yer alır. Ancak biz bu uygulamada kütüphane kullanmadan KS0108 sürücüsünün kaydedicilerini kullanmak suretiyle grafik kontolünü göstereceğiz.

Şekil 3.32’de mevcut bir resmin kullanılmasıyla KS0108 GLCD için uygun karakter kodunun elde edilmiş ekran çıktısı gösterilmiştir. KS0108 grafik LCD donanımı gerçekte 64x64 + 64x64 olmak üzere iki ayrı segment sürücüsünden meydana gelmiştir ve toplamda 128x64’lük en ve boy çözünürlüğüne sahiptir. Bu sürücüleri kontrol etmek için kullanılan KS0107 64 kanal ortak sürücüsü bulunmaktadır.

Karakter kodunu üretmek istediğiniz resmin öncelikle tek renk siyah (monchrome siyah) formatında ve bitmap (*.bmp) dosyası olarak kaydedilmesi gerekir. Resminizin piksel olarak çözünürlüğünü uygun bir resim düzenleme programı yardımıyla 128x64 sınırları içinde olacak şekilde ayarlamalısınız.

Şekil-3.32 mikroC editöründe GLCD kodu üretme aracı

Elde edilen karakter kümesi tasarladığımız yazılımdan ötürü tek seferde kullanılmayacaktır. KS0108 sürücüsünün çalışma ilkesine göre ekran düşeyde her biri 8 piksel olan 8 sayfadan oluşur. Dolayısıyla örnek ekranda gösterilen resim 45x64 boyutundadır ve 64 bitlik yükseklik 8’e bölünerek 8 parçalı bir karakter kodu tasarlanır. Böylece her bir karakter kodu dizisi bir sayfaya yazdırılır.

Şekil 3.33’te grafik LCD’nin blok şeması gösterilmiştir. Eğer şekil dikkatlice incelenirse her bir sayfanın (Page 0 – Page 7) yukardan aşağı (LSB’den MSB’ye) yönde 0-255 arası ikilik kodla kodlanabileceği görülür. Oluşturmak istediğiniz karakterlerden bunu göz önünde bulundurabilirsiniz. Karakter üretme aracında 5x7’lik düzenleyiciyi esas almanız doğru olacaktır.

Görüldüğü gibi bir takım kontrol sinyali hatları vardır. CS1 ve CS2 pinleri iki adet KS0108 sürücüsünden birini seçmek için kullanılır. CS1 sol taraftakini, CS2 ise sağ taraftakini seçer. (RST) Reset pinidir ve ekran RAM’ini temizlemek suretiyle tüm ekran içeriğini resetlemek için kullanılır. R/W pini GLCD tarafından gerçekleştirilecek işlem türünü seçmede kullanılır. Lojik-1 (High) olması durumunda okuma işlemi, Lojik-0 (Low) durumunda yazma işlemi gerçekleştirilir. Veri/Komut (D/I) pini ise veri ya da komut iletimi gerçekleştirmede kullanılır.

Şekil-3.33 KS0108 sürücülü Grafik LCD blok şeması (Kaynak: https://openlabpro.com)

KS0108 grafik ekran sürücüsü için temel 7 komut bulunur. Bu komutlar açıklamalarıyla ve hat sinyallerinin lojik durumlarıyla birlikte Tablo 3.23’te verilmiştir.

Tablo 3.23 KS0108 Grafik LCD sürücüsü için kontrol sinyalleri

E pini GLCD için bir bakıma saat darbesi işlevi görür. Nasıl ki tüm dijital sistemlerde ilgili bitin işleme alınması için high-to-low ya da low-to-high geçişi sağlayan bir saat darbesi giriş varsa, LCD ekran sürücüleri de benzer ilkeyle çalışır. Komut ya da verinin ekran belleğinden okunması ya da ekran belleğine yazdırılması için öncelikle her bir pin karşılık gelen veriye ayarlandıktan sonra, E pini üzerindeki sinyal seviyesi yüksekten alçağa geçirilir. E’deki sinyalin düşen kenarında pinlerdeki karşılık gelen veri tutulur (latch). Ardından, E yüksek seviyeye çekilir ve komut ya da veri bilgisi işleme alınır. Komutlar verilirken mutlaka geçişler arasında bir gecikme bırakılmalıdır. Bu değerlere ilgili sürücünün veri kılavuzlarından ulaşılabilir.

Şekil 3.34’te ve Şekil 3.35’te veri ya da komut yazma ve okuma sinyalizasyonları sırasıyla gösterilmiştir. Burada, veri kılavuzunda da belirtilen geçiş zamanlarına ait bazı kısaltmalar vardır. TC (E çevrim süresi), TWL (E alçak seviye genişliği süresi), TWH (E yüksek seviye genişliği süresi), TASU (adres kurma süresi), TDSU (veri kurma süresi) ve TDHW (yazma işleminin gerçekleşmesi için gerekli veri tutma süresi) kısaltmalarının yanında bahsetmeye gerek görmediğimiz başka süre parametreleri de vardır. Ayrıntılı bilgiye ilgili sürücünün veri kılavuzundan ulaşılabilir.

Şekil-3.34 GLCD yazma komutu sinyalizasyonu (Kaynak: https://openlabpro.com)

Bu sinyalizasyona göre bir yaz komutu fonksiyonu oluşturmak için Tablo 3.24’teki kod yazılabilir.

Tablo 3.24 GLCD yazma komutu mikroC komutu

// r: 0 komut için ve 1 veri için
void GLCD_Write (unsigned char r, unsigned char datax)
{
 GLCD_Data_Direction = 0x00; // Çıkış olarak kurulur
 GLCD_E = 0; // E pini lojik-0
 GLCD_DI = r; // RS(DI) komut için 0, veri için 1
 GLCD_RW = 0; // RW yazma modu için lojik-0 yapılır
 delay_us(1); // Twl > 450ns ve Tasu > 140ns
 GLCD_Data = datax; // Veri portuna veri gönderilir
 GLCD_E = 1; // E pini lojik-1
 delay_us(1); // Twh > 450ns ve Tdsu > 520ns
 GLCD_E = 0; // E pini lojik-0
 delay_us(1); // Tdhw > 10ns
 GLCD_E = 1; // E pini lojik-1
}

Şekil-3.35 GLCD okuma komutu sinyalizasyonu (Kaynak: https://openlabpro.com)

Okuma sinyalizasyonuna göre yazılacak kod Tablo 3.25’deki gibi olur.

Tablo 3.25 GLCD okuma komutu mikroC kodu

unsigned char GLCD_Read (unsigned char r)
{
 unsigned char datax;
 GLCD_Data_Direction = 0xFF; // Giriş olarak kurulur
 GLCD_E = 0; // E pini lojik-0
 GLCD_DI = r; // RS(DI) komut için 0, veri için 1
 GLCD_RW = 1; // RW okuma modu için lojik-1 yapılır
 delay_us(1); // Twl > 450ns ve Tasu > 140ns
 GLCD_E = 1; // Veri Td gecikmesi sonrası gözükür
 datax = GLCD_Data; // Veri portundaki veri alınır
 GLCD_E = 0; // E pini lojik-0
 delay_us(1); // Tdhr > 20ns verinin hatta kaybolmasından önce
}

GLCD’nin etkin şekilde kullanımı için tasarlanmış diğer fonksiyonlar, Şekil 3.36’da uygulaması gösterilmiş devre için Tablo 3.26’da açıklamalarıyla birlikte verilmiştir. Öncelikle gerçekleştirilen uygulama mikrodenetleyicinin dâhili osilatörü kullanılarak tasarlanmıştır. Bunun için OSCCON kaydedicisi kullanılarak yapılandırma sözcüklerinin osilatör tercihi ne olursa olsun MCU, INTOSC (ölçeklenebilir dahili RC osilatörü) üzerinden çalışacak şekilde yapılandırılmıştır. PIC18F4550 ve türevlerinin gelişmiş osilatör ve güç yönetimi üzerine ayrıntılı bilgi almanız için EK-F4’e bakmanız tavsiye edilir.

Şekil-3.36 PIC18F4550 ile GLCD kontrolü

Tablo 3.26 GLCD uygulaması mikroC kodu

/* GLCD pinleri için MCU portları tanımlanıyor */
#define GLCD_Data PORTD
#define GLCD_Data_Direction TRISD
#define GLCD_E PORTB.RB5
#define GLCD_DI PORTB.RB4
#define GLCD_RW PORTB.RB3
#define GLCD_Reset PORTB.RB2
#define GLCD_CS2 PORTB.RB1
#define GLCD_CS1 PORTB.RB0
/* Bu kısımda program içinde kullanılan tüm fonksiyonlar
derleyiciye tanıtılıyor. Böylece programın istenen yerinden
ilgili fonksiyon çağrılabilir */
void GLCD_Write (unsigned char r, unsigned char datax);
unsigned char GLCD_Read_Data (unsigned char r);
void GLCD_Init();
void Set_Column(unsigned char col);
void Set_Row(unsigned char row);
void Set_XY(unsigned char row, unsigned col);
void Display_Set_Start_Line(unsigned char line);
unsigned char GLCD_Busy();
void Check_GLCD_Busy();
char GLCD_Status();
void GLCD_Write_Char(unsigned char datax);
void GLCD_Write_Str(unsigned char *str, unsigned char count);
char GLCD_Read();
void GLCD_Clear_Display();
void GLCD_Clear_Display2();
void GLCD_Clear_Line(unsigned char datax);
void GLCD_Clear_Line2(unsigned char datax);
void GLCD_Dot(unsigned char x, unsigned char y);
/* kodu çıkartılmış resimler yüksekliklerine uygun olarak satırlara
bölünüyor */
char bayrak1[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
55, 255, 255};
char bayrak2[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 127, 63, 31, 15, 15, 7, 135, 199, 227, 227, 227, 243, 247, 231, 231, 239,
223, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
55, 255, 255};
char bayrak3[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1
, 0, 0, 0, 0, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 239, 207, 207, 1, 3, 135, 131, 51, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
55, 255, 255};
char bayrak4[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254
, 248, 240, 224, 192, 192, 131, 135, 143, 31, 31, 31, 159, 159, 159, 223, 223,
239, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
55, 255, 255};
char bayrak5[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
55, 255, 255};
char bayrak6[] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3};
char mustafa_kemal1[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 192, 224, 224,
240, 248, 248, 252, 252, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 252,
252, 240, 224, 192, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0};
char mustafa_kemal2[] = {0, 0, 240, 252, 252, 254, 254, 254, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 252, 232, 0, 0, 0, 0};
char mustafa_kemal3[] = {0, 0, 0, 15, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0};
char mustafa_kemal4[] = {0, 0, 0, 0, 1, 31, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 127, 127, 127, 127, 255, 223, 223, 223, 159, 159, 31,
31, 31, 159, 159, 159, 159, 63, 63, 127, 255, 255, 31, 0, 0, 0, 0};
char mustafa_kemal5[] = {0, 0, 0, 0, 0, 0, 0, 31, 15, 255, 63, 31, 207, 195,
131, 1, 3, 5, 6, 6, 6, 6, 2, 5, 7, 7, 1, 1, 1, 0, 3, 7, 3, 7, 2, 4, 0, 0, 7, 1,
0, 0, 0, 0, 0};
char mustafa_kemal6[] = {0, 0, 0, 0, 0, 0, 0, 16, 64, 96, 242, 249, 95, 187,
207, 239, 111, 39, 78, 64, 0, 128, 192, 192, 192, 148, 204, 204, 204, 188, 200,
228, 192, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char mustafa_kemal7[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 193, 115, 253, 255, 254,
255, 255, 253, 252, 255, 254, 254, 252, 225, 225, 193, 192, 198, 199, 198, 198,
128, 192, 192, 160, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char mustafa_kemal8[] = {0, 0, 0, 0, 0, 0, 0, 8, 28, 28, 253, 255, 255, 127,
255, 255, 255, 127, 127, 215, 187, 255, 255, 127, 95, 127, 159, 191, 127, 247,
127, 255, 79, 51, 50, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char C_[] = {0,4,10,17,17,0};
char h_[] = {0,31,4,28,0,0};
char m_[] = {0,24,4,28,4,24};
char tt_[] = {0,2,31,2,0,0};
char T_[] = {0,1,1,31,1,1};
char u_[] = {0,29,16,29,0,0};
char uu_[] = {0,28,16,28,0,0};
char r_[] = {0,30,2,2,0,0};
char k_[] = {0,31,8,20,0,0};
char i_[] = {0,0,29,0,0,0};
char y_[] = {0,18,12,2,0,0};
char e_[] = {0,12,26,22,0,0};
char bos_[] = {0,0,0,0,0,0,0};
char *turkiye[]={T_, u_, r_, k_, i_, y_, e_};
char *cumhuriyeti[]={C_, uu_, m_, h_, uu_, r_, i_, y_, e_, tt_, i_};
int i, k, j, carpan=54;
unsigned char deneme;
void main(){
 OSCCON = 0X72;
 CMCON=0X07; // Analog karşılaştırıcılar kapalı
 ADCON0=0; // A/D modülü kapalı
 ADCON1=0X0F; // Tüm AN kanallari dijital I/O olarak ayarlı
 TRISA = 0;
 TRISB = 0;
 TRISC = 0;
 TRISD = 0;
 TRISE = 0;
 PORTA = 0;
 PORTB = 0;
 PORTC = 0;
 PORTD = 0;
 PORTE = 0;
 GLCD_Init(); // GLCD açılıyor
 Check_GLCD_Busy(); // Meşgulse bekle
 GLCD_Clear_Display();
 /* Türkiye Bayrağı yazdırılıyor */
 Set_XY(0,1);
 GLCD_Write_Str(bayrak1, 64);
 Set_XY(1,1);
 GLCD_Write_Str(bayrak2, 64);
 Set_XY(2,1);
 GLCD_Write_Str(bayrak3, 64);
 Set_XY(3,1);
 GLCD_Write_Str(bayrak4, 64);
 Set_XY(4,1);
 GLCD_Write_Str(bayrak5, 64);
 Set_XY(5,1);
 GLCD_Write_Str(bayrak6, 64);
 /* Mustafa Kemal Atatürk yazdırılıyor */
 Set_XY(0,65);
 GLCD_Write_Str(mustafa_kemal1, 45);
 Set_XY(1,65);
 GLCD_Write_Str(mustafa_kemal2, 45);
 Set_XY(2,65);
 GLCD_Write_Str(mustafa_kemal3, 45);
 Set_XY(3,65);
 GLCD_Write_Str(mustafa_kemal4, 45);
 Set_XY(4,65);
 GLCD_Write_Str(mustafa_kemal5, 45);
 Set_XY(5,65);
 GLCD_Write_Str(mustafa_kemal6, 45);
 Set_XY(6,65);
 GLCD_Write_Str(mustafa_kemal7, 45);
 Set_XY(7,65);
 GLCD_Write_Str(mustafa_kemal8, 45);
 /* Türkiye Cumhuriyeti yazdırılıyor */
 for (i = 0; i < 7; i++){
  for (k = 0; k < 6; k++){
   Set_XY(6,i*6+k);
    GLCD_Write(1, *turkiye[i]);
     turkiye[i]++;
   }
  }
  for (i = 0; i < i++){
   for (k = 0; k < k++){
    Set_XY(7,i*6+k);
     GLCD_Write(1, *cumhuriyeti[i]);
     cumhuriyeti[i]++;
   }
  }
}

void GLCD_Write (unsigned char r, unsigned char datax){
 GLCD_Data_Direction = 0x00; // Çıkış olarak kurulur
 GLCD_E = 0; // E pini lojik-0
 GLCD_DI = r; // RS(DI) komut için 0, veri için 1
 GLCD_RW = 0; // RW yazma modu için lojik-0 yapılır
 delay_us(1); // Twl > 450ns ve Tasu > 140ns
 GLCD_Data = datax; // Veri portuna veri gönderilir
 GLCD_E = 1; // E pini lojik-1
 delay_us(1); // Twh > 450ns ve Tdsu > 520ns
 GLCD_E = 0; // E pini lojik-0
 delay_us(1); // Tdhw > 10ns
 GLCD_E = 1; // E pini lojik-1
}

unsigned char GLCD_Read_Data (unsigned char r){
 unsigned char datax;
 GLCD_Data_Direction = 0xFF; // Giriş olarak kurulur
 GLCD_E = 0; // E pini lojik-0
 GLCD_DI = r; // RS(DI) komut için 0, veri için 1
 GLCD_RW = 1; // RW okuma modu için lojik-1 yapılır
 delay_us(1); // Twl > 450ns ve Tasu > 140ns
 GLCD_E = 1; // Veri Td gecikmesi sonrası gözükür
 datax = GLCD_Data; // Veri portundaki veri alınır
 GLCD_E = 0; // E pini lojik-0
 delay_us(1); // Tdhr > 20ns verinin hatta kaybolmasından önce
}

/* GLD'yi çalışmaya hazır hale getiren fonksiyon */
void GLCD_Init(){
 GLCD_Reset = 1; // Göstergeyi resetle
 GLCD_CS1 = 1; // Sol kontrolcüyü seç
 GLCD_CS2 = 1; // Sağ kontrolcüyü seç
 GLCD_Write(0, 0x3F); // AÇMA için yazma komutu
}

/* Sütunu seçmek için kullanılan fonksiyon */
void Set_Column(unsigned char col){
 unsigned char datax;
 if (col<64){
  GLCD_CS1 = 1;
  GLCD_CS2 = 0;
  // Komut formatı -> 01XXXXXX
  // XXXXXX -> 0-63 aralığında sütun adresi
  datax = (col | 0x40) & 0x7F;
  GLCD_Write(0,datax);
 }
 else{
  GLCD_CS1 = 0;
  GLCD_CS2 = 1;
  // Komut formatı -> 01XXXXXX
  // XXXXXX -> 0-63 aralığında sütun adresi
  datax = ((col-64) | 0x40) & 0x7F ;
  GLCD_Write(0,datax);
 }
}

/* Satır ayarlanıyor */
void Set_Row(unsigned char row){
 unsigned char datax;
 // Komut formatı :10111XXX, XXX -> satır
 datax= (row | 0xB8) & 0xBF;
 GLCD_Write(0, datax);
}

void Set_XY(unsigned char row, unsigned col){
 Set_Row(row);
 Set_Column(col);
}

/* Bu fonksiyon isteğe bağlı olarak kullanılır.
Ekran belleğindeki verinin gösterildiği satır hattını
değiştirmeyi sağlar. Böylece animasyonlu içerik tasarlanabilir */
void Display_Set_Start_Line(unsigned char line){
 unsigned char datax;
 GLCD_CS1 = 1;
 GLCD_CS2 = 1;
 datax = 0xC0 | line; // Başlangıç satırı komudunu kur
 GLCD_Write(0, datax);
}

unsigned char GLCD_Busy(){
 unsigned char datax;
 datax = GLCD_Read_Data(0);
 return (datax && 0x80); // Durum bayrağı döndürülüyor,
 // DB7 biti 1 ise meşgul, 0 ise hazırdır
}

/* Meşgul sinyali temizlenene kadar beklemeyi sağlayan fonksiyon
Bir komut çağrıldıktan sonra meşgul bayrağı temizlenene kadar beklemek
için kullanılabilir. */
void Check_GLCD_Busy(){
 while(GLCD_Busy());
}
char GLCD_Status(){
 unsigned char datax;
 datax = GLCD_Read_Data(0); // Komut modunda oku
 return (datax && 0x30); // durum bayrağını döndür,
 // Ekran kapalıysa 1, reset için 2 ve diğer durumlarda 0
}
void GLCD_Write_Char(unsigned char datax){
 GLCD_Write(1, datax); // Veri modunda yazma
}

// Çok sayıda baytın RAM'e gönderilmesi için kullanılan fonksiyon
// Gönderilen verinin sayfa ve sütun limitlerini aşmadığından emin olun
void GLCD_Write_Str(unsigned char *str, unsigned char count){
 unsigned char c=0;
 do{
  GLCD_Write(1, *str); // Veri modunda yazma
  c++;
  str++;
 } while(c < count);
}

char GLCD_Read(){
 unsigned char datax;
 datax = GLCD_Read_Data(1); // Veri modunda okuma
 return datax;
}

void GLCD_Clear_Display(){
 unsigned char i=0;
 GLCD_Clear_Line(i); // Her satırı temizle
}

void GLCD_Clear_Line(unsigned char datax){
 unsigned char i, j;
 for(i=0;i<8;i++){
  for(j=0;j<128;j++){
   Set_XY(i, j);
   GLCD_Write_Char(datax);
  }
 }
}

void GLCD_Dot(unsigned char x, unsigned char y){
 Set_XY(x, y); // Pozisyon kurulur
 GLCD_Write(1, 0x70); // Veri modunda yazma
}