23 Aralık 2013 Pazartesi

Port Denetimi - Ders18

Ders18: Port Denetimi



Port Kavramı

Bu kısıma başlamadan önce port kavramları konusunda giriş seviyesinde bilgi sahibi olmak gerekir. Bunun için burayı tıklayın. Eğer port kavramı konusunda yeterli bilgiye sahip olduğunuzu düşünüyorsanız bu kısmı atlayabilirsiniz.
Portların Kontrolü

Geliştirilen bir program içerisinden donanımsal öğelere erişmek veya onları kullanmak için birçok yol vardır. En yalını, ki sistem mimarisi buna izin veriyorsa, bu gibi birimlere aynı bellek gözüne erişilmiyormuş gibi işaretçi değişkenler kullanılmıştır; ancak bu durum sistem mimarisinden dolayı her zaman mümkün olmayabilir. Bu durumda, ilgili birimlere erişmek için derleyicilerin sahip olduğu hazır kütüphane fonksiyonları kullanılır.
Port Giriş/Çıkış Fonksiyonları

Bir bilgisayarın portlarına erişmek için (outport(), intport() gibi) birçok fonksiyon vardır. Bunlar, sistemin sahip olduğu donanımsal öğelere port üzerinden erişilmesi imkanını sunar. Sistemin donanımsal öğelerine erişmek için derleyiciler birçok fonksiyona sahiptir; tüm listesi için kullanılan derleyicinin başvuru kitabına bakılmalıdır. Tablo 18.1 de, Turbo C derleyicisinde bululunan ve bu konu ile ilgili birkaç fonksiyon tanıtılmıştır (bunlar dışında birçok fonksiyon da vardır!).
Tablo 18.1 : Turbo C derleyicisine ait bazı port fonksiyonları
Port Fonksiyonu Açıklama
void outp(int port_adresi,int bayt_degeri); Porta bir baytlık veri yazar
void outport(int port_adresi,int deger); Porta bir kelime* yazar
void outportb(int port_adresi,unsigned char deger); Porta bir baytlık veri yazar
int inp(int port_adresi); Porttan bir baytlık veri okur
int inport(int port_adresi); Porttan bir kelime okur
char inportb(int port_adresi); Porttan bir baytlık veri okur
int peek(unsigned segment, unsigned offset); segment:offset ile belirlenen bellek alanındaki kelimeyi çağırır
char peekb(unsigned segment, unsigned offset); segment:offset ile belirlenen bellek alanından bir baytlık veriyi çağırır
void poke(unsigned segment, unsigned offset,int deger); segment:offset ile belirlenen bellek alanına bir tamsayı değeri yazar
void pokeb(unsigned segment,unsigned offset, char deger); segment:offset ile belirlenen bellek alanına bir baytlık veri yazar
(*) kelime(word) : Porta yazılacak veya porttan okunacak, bir tamsayının bellekte kaplayacağı alanı temsil eder. (Bu alan sizeof() operatörü ile öğrenilebilir)
Port foksiyonlarının kullanımı, örnek programlar üzerinde, bir sonraki bölümde incelenmiştir. Örnekler, programlaması kolay olduğu için, PC paralel portu üzerinde yoğunlaştırılmıştır. Programların çıktıları hemen ilgili progam kodunun altında verilmiştir.
Porta Veri Yazma ve Porttan Veri Okuma

Bir önceki bölümde verilen port fonksiyonları, bir PC nin bağlantı noktalarına erişmek veya bellek gözündeki port adreslerine erişmek için kullanılır. Aşağıda bu fonksiyonları daha iyi anlamak için 10 tane örnek program sunulmuştur. (Bütün programlar Turbo C derleyicisinde denemiştir. Eger bu derleyiciye sahip degilseniz, buradan inderbilirsiniz).
Not : Turbo C derleyicisinde port fonksiyonları kullanılırken dos.h başlık dosyası programın başına ilave edilmelidir.
Program 18.1 : outp fonksiyonunun kulanımı
1:  /* outp örneği */
2:  #include <stdio.h>
3:  #include <dos.h.h> /* port fonksiyonları için */
4:
5:  #define DATA  0x0378
6:
7:  int main(void)
8:  {
9:     int deger = 25;
10:
11:    outp(DATA,deger);
12:    printf("\n%X nolu adrese %d değeri yazıldı.",DATA,deger);
13:    return 0;
14: }
378 adresine 25 değeri yazıldı.

Program 18.1 de 5. satırda tanımlanan porta, 11.satırda 25 değeri yazılmaktadır. Bu değer PC paralel portunun DATA uçlarına yazılır. Bu sebeple 25 değeri binary olarak 8 e bölünür, yani 25 = 00011001 şekinde DATA portuna yazılır.
Porta yazılmak veya porttan okunmak istenen veriyi binay olarak görüntülemek mümkündür. PortPrg02 bu amaçla yazılımıştır. Bu işlemin gerçekleşmesi için Programa ayrıca CevFonks.c programı da ilave edilmelidir.
Program 18.2 : outportb fonksiyonun kullanımı
1:  #include <stdio.h>
2:  #include <dos.h>
3:  #include "CvrFonks.c"
4:
5:  #define DATA 0x0378
6:
7:  main()
8:  {
9:     int deger;
10:
11:    deger = 0x19; /* deger = 25 */
12:
13:     outportb(DATA,deger);
14:
15:     printf("\nDATA portuna gönderilen değer %Xh ",deger);
16:     cevir(deger);
17:
18:   return 0;
19: }
Porta gönderilen değer 19h 00011001

3. satıda CvrFonks.c programa eklenmiştir. 11. satırda tanımlanan değer, 5. satırdaki porta, 13. satırda elirtilen outportb fonksiyonu ile yazılmıştır. 16. satırdaki cevir fonksiyonu, porta yazılan değeri sadece 8 bite çevirir ve ekrana yazar. cevir fonksiyonu CevFonks.c fonksiyonunda daha önce tanımlanmıştır. outp ve outportb fonksiyonlarının kullanımının aynı olduğuna dikkat ediniz.
inp ve inportb fonksiyonları, PC bağlantı noktalarından bir baytlık veri okumak mümkündür. PortPrg03 bu fonksiyonlar ile nasıl veri okunacağına dair iyi bir fikir verir. Ayrıca bu iki fonksiyonun kullanımının aynı olduğuna dikkat ediniz.
Program 18.3 : inp ve inportb fonksiyonlarının kulanımı
1:  /* inp ve inportb fonksiyonlarıyla ile paralel porta atanan varsayılan değerleri öğrenme */
2:  #include <dos.h>
3:  #include <stdio.h>
4:
5:  #define DATA    0x0378
6:  #define STATUS  DATA+1
7:  #define CONTROL DATA+2
8:
9:  main()
10: {
11:    int  veri;
12:
13:    puts("Paralel porta atanan varsayılan değerler (Hex):");
14:
15:    veri = inp(DATA);
16:    printf( "Data portu    : %X\n",veri );
17:
18:    veri = inp(STATUS);
19:    printf( "Status portu  : %X\n",veri );
20:
21:    veri = inportb(CONTROL);
22:    printf( "Kontrol portu : %X\n",veri );
23:
24:    return 0;
25: }
Paralel porta atanan varsayılan değerler (Hex):
Data portu    : 4
Status portu  : 7F
Kontrol portu : CC

Port adresleri 5. 6. ve 7. satırlarda tanımlanmıştır. inp ve inportb foksiyonları ile okunanan değerle (sırasıyla DATA,STATUS ve CONTROL) veri değişkenine aktarılmış ve ekrana yazıldırılmıştır. Bu değerler porta hiç bir müdehale olmadan elde edilmiştir ve her bilgisayarda başka bir sonuç verebilir. Bu fonksiyonların tek parameteresi olduğuna dikkat ediniz.
Bir porta her hangi bir veri yazıldıktan sonra, bu veri o portun saklayıcısına (register) yazılır ve yeni bilgi yazılmadıkça orada veri kalır. PortPrg05 CONTROL portuna ouportb ile yazılan bir verinin inportb fonksiyonu ile okunması gösterilmiştir.
Program 18.4 : inportb ve outportb fonksiyonlarının kullanımı
1:  /* inportb ve outportb örneği */
2:  #include <stdio.h>
3:  #include <dos.h>
4:  #define PORT 0x037A  /* kontrol portu */
5:
6:  main()
7:  {
8:     int deger;
9:
10:    deger = inportb(PORT);  /* varsayılan deger */
11:    printf("\nPorta veri yazılmadan önceki değer : %X",deger);
12:
13:    deger = 0x0A;   /* deger = 10 */
14:    outportb(PORT,deger);
15:
16:    deger = inportb(PORT);
17:    printf("\nPorta veri yazdıktan sonraki değer : %X",deger);
18: }
Porta veri yazılmadan önceki değer : CC
Porta veri yazdıktan sonraki değer : CA

4. satırda belirtilen porta, A değeri 14. satırdaki outportb fonksiyonu ile yazılmıştır. Yazılan değer CONTROL portunun saklayıcısında saklanmaktadır. Daha sonra bu saklayıcıdan aynı veri 16. satırdaki inport fonksiyonu ile okunmaktadır. Program çıktısı incelendiğinde, portta varsayılan değerin CC, veri yazıldıktan sonraki değerin CA olduğu görülmektedir.
Not: CONTROL portunun ilk ilk dört bitine müdehale edilebilir. Yani birinci C değeri değiştirilemez.
Bazen paralel porttan istenen çıkış 8 bitten fazla olabilir. Bu durumda DATA portuna ek olarak CONTROL portunun kullanılması gerekir. DATA portunun 8 bitlik çıkışına, CONTROL portunun 4 bitlik çıkışları da ilave edilirse toplam 12 bitlik çıkış almak mümkün olur. PortPrg05 12 bitlik veri çıkışının nasıl yapılabilceğini göstermektedir. Kullanılan yöntem özetle şöyledir:
  • Veri 3 basamaklı Hex olarak ifade edilir. Çünkü 3 basamaklı Hex veri 12 bitlik binary veriye denktir.
    Örneğin A2F = 101000101111
  • Sağdan ilk 8 bit DATA portuna son 4 bit CONTROL portuna yazılır. Yani veri, veriC ve veriD olarak iki kısma bölünür.
    örneğin A2F = 1010 00101111
  • Soldan ilk 4 bit CONTROL portuna yazılması için, veri bitleri 8 basamak sağa kaydırılır.
    veriC = A2f >> 8 = 000000001010 = A
  • Daha sonra veri bitleri 4 sola ardından 4 sağa kaydırılır.
    A2F << 4 = 001011110000 = 2F0
    2F0 >> 4 = 000000101111 = 2F
    veriD = 2F
  • Böylece veri iki kısma ayrılmış olur.
Program 18.5 : PC Paralel portundan 12 bitlik veri çıkışı
1:  /*
2:     PC Paralel portundan 12 bitlik veri çıkışı:
3:     veri değişkeni bit düzeyinde işlem yapan operatörlerle
4:     iki kısma ayrılır.
5:          veri = veriC + veriD
6:  */
7:
8:  #include <stdio.h>
9:  #include <dos.h>
10:
11: #define DATA    0x0378
12: #define CONTROL DATA+2
13:
14: main()
15: {
16:
17:    int veri,veriC,veriD;
18:
19:    veri = 0xF48;  /* 3-dijit Hex sayı = 12 bit binary sayı */
20:
21:    veriC = veri >> 8; /* CONTROL portuna yazılack veri */
22:
23:    veri <<= 4;
24:    veri >>= 4;
25:
26:    veriD = veri;        /* DATA Portuna yazılacak veri */
27:
28:    outportb(DATA,veriD);
29:    outportb(CONTROL,veriC);
30:
31:    printf("\n12 bitlik veri              : %X%X",veriC,veriD);
32:    printf("\nCONTROL portuna yazılan veri: %X",veriC);
33:    printf("\nDATA    portuna vazılan veri: %X",veriD);
34:
35: return 0;
36: }
12 bitlik veri              : F48
CONTROL portuna yazılan veri: F
DATA    portuna vazılan veri: 48

19. satırdaki veri 21. satırdaki işlemle veriC değişkenine aktarılmıştır. 23. ve 24. satırdaki işlemlerle veri bitlerinden F değeri silinmiş olur. Elde eldilen yeni veri, 26. satırda veriD değişkenine aktarılmıştır. 28. ve 29. satıda veriC ve veriD değerleri sırasıyla CONTROL ve DATA portlarına yazılmıştır.
Paralel porttan belli bir formatla veri çıkış almak mümkündür. Örneğin format DATA uçlarına belli zaman aralıklarıyla sırasıyla 1 değeri göndemek olsun. Bu işlem bit düzeyinde işlem yapan operatörler ve ve basit bir döngü yardımıyla PortPrg06 programında incelenmiştir. Programda kullanılan delay fonksiyonu ile zaman aralıkları seçilmiştir. dos.h kütüphanesinde bulunan bu fonksiyon, kendisine parametre olarak gelen değeri mili saniye olarak kabul eder ve bekletir.
Program 18.6 : DATA-port uclarına sırasıyla 1 değerini gönderme
1:  /* DATA-port uclarına sırasıyla 1 değerini gönderir */
2:  #include <stdio.h>
3:  #include <dos.h>
4:
5:  #define DATA 0x0378
6:
7:  main()
8:  {
9:     int veri,sayac;
10:
11:    sayac = 0;  /* döngü sayacı */
12:    veri  = 1;
13:
14:    while( sayac<8 )
15:    {
16:        outportb(DATA,veri);  /* DATA portuna int veri yaz */
17:        printf("\nPorta yazılan veri : %d",veri);
18:
19:        veri <<= 1; /* veriyi binary olarak birer-birer sola kaydır */
20:
21:        delay(500); /* 500 ms bekle */
22:        sayac++;
23:    }
24: }
Porta yazılan veri : 1
Porta yazılan veri : 2
Porta yazılan veri : 4
Porta yazılan veri : 8
Porta yazılan veri : 16
Porta yazılan veri : 32
Porta yazılan veri : 64
Porta yazılan veri : 128

12. satırda veri değişkenine dögüye girmeden önce 1 (00000001) değeri atanmıştır. Dögü koşulu sınandıktan sonra 16. satırda veri DATA portuna yazılmıştır. 19. satırda verinin içeriği 1 bit sola kaydırılrak DATA portunun sadece bir sonraki ucuna 1 değeri gönderilmiştir. 21. satırda bu verinin DATA portunun saklayıcısında 500 ms saklanmış ve ardından bir sonraki çevrime girilmiştir. Bütün verilerin ekran çıktısı incelendiğinde 2 saysının kuvvetleri olduğu görülür.
Şimdiye kadar paralel portun taban adresini 378h olarak kabul edildi. Bu adres bazı makinalarda farklı olabilir. Fakat şu bir gerçektir ki taban adresleri PCde herzaman belli bir alanlarda(register) yazılıdır. Bu alanlar segment:offset olarak adlandırılan bölgede saklıdır. segment ve offset değerleri bir değişkeninin (veya bir çevre biriminin) bellekteki (genellikle RAM) adreslerini gösterir. Bu adresler dört çift heksadesimal değerden oluşur. Örneğin segment:offset => 23FB:0017 gibi. segment ve offset adres çiftinin belirli bir kombinasyonu ana bellekteki kesin adresleri belirler. Bu belirlemenin hesaplanışı şu şekildedir:
   segmet:offset => 2083:0100
segment adresinin sağına 0 rakamı ilave edilip offset adresi ile toplanır:
   20830h + 00100h = 20930h (= 133424 desimal olarak)
Çıkan netice ana belleğin 133424. adresini işaret eder. Segmet/Offset yapısı bilgisayarın giriş/çıkış bağlantı noktalarına atanan adresleri öğrenmek ve programlamak için de kullanılabilir. Örneğin paralel portun taban adresi 0000:0408 (yada 0040:0008) şeklinde belirlenen segment:offset adresi ile bellidir. PortPrg07 programında peek fonksiyonu ile paralel port adreslerinin nasıl öğrenileceği gösterilmiştir.
Program 18.7 : peek fonksiyonu ile paralel port adreslerinin öğrenilmesi
1:  /* peek fonksiyonu ile paralel port adreslerinin öğrenilmesi */
2:
3:  #include <dos.h>
4:  #include <stdio.h>
5:
6:  main()
7:  {
8:     unsigned int DATA,STATUS,CONTROL;
9:
10:    DATA    = peek(0x0000,0x0408); /* segmet:offset - 0000:0408 */
11:    STATUS  = DATA + 1;
12:    CONTROL = DATA + 2;
13:
14:    puts("Paralel Port Adresleri (Hex):");
15:
16:    printf("DATA    : %X\n",DATA);
17:    printf("STATUS  : %X\n",STATUS);
18:    printf("CONTROL : %X\n",CONTROL);
19:
20:    return 0;
21: }
Paralel Port Adresleri (Hex):
DATA    : 378
STATUS  : 379
CONTROL : 37A

10. satırdaki peek fonksiyonu ile 0000:0408 adres gözünden alınan değer DATA değişkenine aktarılmıştır. Bu değer 378h dir ve paralel portun taban adresini işaret eder. PortPrg07 ile elde edilen sonuç bir çok makine için geçerlidir. Fakat bazı makinelerde bu değer farklı olabilir. PortPrg08 daha genel amaçlı bir rogramdır ve bütün makinelerde kullanılabilir.
Program 18.8 : Genel amaçlı paralel port programı
1:  /* peek ile elde edilen değer ile porta ver yazma */
2:  #include <dos.h>
3:  #include <stdio.h>
4:  main()
5:  {
6:     unsigned int port;
7:
8:     port = peek(0x0040,0x0008);  /* 0x0000,0x0408 anlamında */
9:     outportb(port,22);
10:
11: }

8. satırdaki port değişkenine peek fonksiyonunun sonucu aktarılmıştır. 9. satırda port ile belirtilen porta (DATA portu) 22 değeri yazılmıştır.
Son olarak peek ve poke fonksiyonlarının kullanımı PortPrg09 ve PortPrg10 da birer örnekle gösterilmiştir. Bu örnekler klavyedeki Num Lock, Caps Lock ve Scroll Lock durumları için hazırlanmış iyi bir örnekdir.
Program 18.9 : Klavyedeki Num Lock, Caps Lock ve Scroll Lock durumlarını inceleme
1:  /* peek ile klavye kontrolü */
2:  #include <stdio.h>
3:  #include <dos.h>
4:
5:  int main(void)
6:  {
7:     int deger;
8:
9:     puts("Klavyedeki Num Lock, Caps Lock ve Scroll Lock durumları:");
10:    deger = peek(0x0040, 0x0017);
12:
13:    if (deger & 16)   puts("Scroll Lock açık");
14:    else              puts("Scroll Lock kapalı");
15:
16:    if (deger & 32)   puts("Num Lock açık");
17:    else              puts("Num Lock kapalı");
18:
19:    if (deger & 64)   puts("Caps Lock açık");
20:    else              puts("Caps Lock kapalı");
21:
22:    return 0;
23: }

Program 18.10 : Scroll Lock tusunu aktifleştirir
1:  #include <stdio.h>
2:  #include <dos.h>
3:
4:  int main(void)
5:  {
6:    printf("Scroll Lock tuşunun kapalı olduğundan emin olup ENTER tuşuna basın\n");
7:    getchar();
8:    poke(0x0000,0x0417,16);  /* scroll lock açılıyor... */
9:    printf("scroll lock şimdi açık\n");
10:   return 0;
11: }



EK: CvrFonks.c

/*
   Bu fonksiyon 10 tabanındaki bir sayıyı
   8 bit olarak 2 tabanına çevirir
*/

void cevir(int x)
{
    int i=0,Binary[8];
    /* x değeri alınıp 2 tabanına çevriliyor ...*/
    while( x>0 )
    {
       Binary[i++] = x%2;
       x /= 2;
    }
    /* x in binary değerleri ekrana yazdırılıyor...*/
    for(i=7;i>=0;i--)
    {
      if( Binary[i]>1 || Binary[i]<0 )
      Binary[i]=0;

      printf("%d",Binary[i]);
    }
}

Hiç yorum yok:

Yorum Gönder