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]);
}
}