C# kullanarak, bilgisayarınızın paralel portu’na bağladığınız kendinizin
yaptığınız bir elektronik devreyi kontrol etmek çoğunuza heyecan verecektir. Bu elektronik devre çok değişik amaçlarla
kullanılmak istenmiş
olabilir;örneğin bir motor kontrol devresi, bazı led’leri yakıp söndürmeye yarayan bir devre veya
bazı sezicilerden bilgi toplayıp, bilgisayara besleyip, buna göre
bilgisayarın bazı cihazları kapatma veya açma eylemlerinde
bulunmasını istediğimiz bir devre olabilir. Veya daha da heyecan
verici şey; evinizdeki bilgisayarınıza bağlı bir
cihazı (örneğin bir kahve makinesiniveya
balıklarınızı yemleyen bir düzeneği) internet
üzerinden, bulunduğunuz mekandan(burası Sahra çölü de olabilir,
eskimoları ziyarete gittiğiniz
kutuplarda bir yer de) , yeter
ki internet bağlantınısı
kurabileceğiniz bir yer olsun(Hey CELL-O!; kutuplarda var
mısınız? ) kontrol etmek olsa gerek. İleriki bölümlerde bir ASP.NET sayfası
üzerinden böyle bir şeyi gerçekleştireceğiz. Yeterince heyecan
verici buldunuzsa okumaya devam edin!..
Örnek Uygulama
PARALEL PORT:
Kontrol kartımızı
takacağımız port bu port olacaktır. Centronics port’u da
denilen bu port, endüstriyel bir standart olup, USB portlarından önce
yazıcılar için kullanılan temel bir porttu. Bir bilgisayarda en
azından bir adet böyle bir port bulunmaktadır. Port, bilgisayarla gelmekte olup, ya bilgisayarın ana kartı üzerinde, ya
da sonradan eklenebilen I/O kartlarıyla bize sunulmaktadır. Bir bilgisayarda birden fazla böyle portlar
bulunabilir. Bunların adları LPT1,LPT2..., diye gidecektir.
Bilgisayaraınızdaki portların adreslerini
Control Panel>System>Hardware>Device Manager>Ports>LPT1 ‘den kolaylıkla öğrenebilirsiniz.
Eğer bir adet port varsa, adresi büyük olasılıkla
0x378’dir(Hexadesimal biçemde). Bu değer, portun temel-adresini
tanımlamaktadır.
DONANIM:
Bu bölümde,
denemelerimde
kullandığım , paralel
porta takılacak devrenin şemasını vereceğim. Bu
devreyi bir breadboard üzerinde kurmanız zor olmayacaktır.Yok
eğer, “ben daha derli toplu olmayı ve ileriki projeleri de düşünüyorum”, diyorsanız ve de kartın baskı devre ve elemanlardan oluşan kitini bütün halinde elde etmek isterseniz ,
benimle temas kurabilirsiniz.
Gelin önce, paralel porta bir göz atalım.Nasıl
bu port kontrol amaçları için kullanılabilir, onu inceleyelim ve daha
sonra da devremize göz atalım.
Şemada(Şekil1.1) gösterildiği gibi, bir paralel port üç gruptan oluşur.
1)Data grubu:
8 kanalı bulunan bu grup üzerinden bağlı devremize sinyal
gönderebiliriz. Bu grup 8bitlik bir CPU
port adresine karşılık gelir. Yani bu grubun adresi, ilgili
paralel portun temel adresi neyse odur.
2)Kontrol grubu:
Bu grubun kanallarının amacı da
bağlı cihaz veya devrelere kontrol sinyali yollamaktır. Bu
grupta 4 adet sürgülü çıktı kanalları( -STROBE,-LF/CR,-SLN ve
–INITILAIZE) bulunur. Bu grubun kontrolu ilgili CPU portunun temel adresi+2
adresinden yapılır.
STROBE,LF/CR ve SLIN yolları mantıksal olarak tersine
dönüktür(inverted). Halbuki, INITIALIZE böyle değildir. Yani
dışarıya bir yüksek sinyal göndermek istersek, dönük olan kanallara düşük sinyal
koymalıyız.
Şekil 1.1 Paralel port’un mantıksal
yapısı:
3)Status grubu
Bu grup
portumuza bağlı
devrelerden, bilgisayarımıza bilgi girişinin
yapılabileceği kanalları barındırır.Bu grupta 5
kanal vardır. (-ERROR,SLCT,PE,-ACK, ve BUSY) diye..ve dışarıdan içeriye doğru
yönlendirilmişlerdir. Gelen
bilgiler ilgili CPU portuna
beslenir ve bu portun adresi temeladres+1’dir.Bu adresin içeriğini
okuyarak , gelen sinyalleri
toplayıp değerlendirebiliriz.BUSY kanalı terslenmiştir,
fakat diğerleri değil.
KARTIMIZIN ŞEMASI
Şimdi bu bilgiler ışığında ,
kullanacağımız devrenin şemasına bir göz atalım.
Bu kart, bilgisayarımız ve kontrol veya bilgi
toplamaya yarayacak sezici devrelerimiz
arasında ara yüz görevi görecektir.Kartımızda her bir girdi veya
çıktı hattının mantıksal değerini göstermek
için LED’ler bulunmaktadır. Bu
LED’ler acalığıyla programımıza
yaptırdığımız I/O işlemlerini takip
edebileceğiz, kolaylıkla..
Kartımızdaki her bir I/O hattına
kolaylıkla bağlantı yapabilmek için vidalı klemens bulunmaktadır. Bilgisayarımıza
giren hatlar ise, Schmitt trigger tampon tümleşik devreleri
aracılığıyla
tamponlanmışlardır.
Devremiz, 8-15V’lık bir düzeltilmemiş doğru
akım güç kaynağından beslenebilir.Kart üzerinde bunu düzelten ve
istenen düzeye getiren bir 7805 +5V 1A
ve ek elemanlardan oluşan voltaj düzenleme devresi de
bulunmaktadır. Kaynağımızdan çekilecek akımın
düzeyini sınırlamak için bir
adet 1A’lik sigorta da bulunmaktadır.
Şekil 1.2
Kartın elektronik devre şeması
Paralel portumuzun DB0-DB7 ‘den oluşan Data port’u
74LS244 Schmitt trigger tampon devresi(IC2) ninin girdi kanallarına 8 adet
100ohm’luk dirençler aracılığıyla beslenmektedir. Tampon
devreninin çıktı kanalları ise vidalı klemense
getirilmiştir.Her bir hat aynı zamanda düşük akımlı
bir LED’e 3.3Kohm’luk direnç üzerinden bağlanmıştır. Ne
zaman ki, hattımızı mantıksal yüksek yaptık, ilgili
hattın LED’i yanacaktır. Kontrol portunun 4 hattı da Data
port’ta olduğu gibi tamponlanıp, klemense getirilmiştir.
Status portu’nun girdi işlemleri için
kullanıldığından bahsetmiştik, yukarıda.
Dolayısıyla, vidalı klemensten gelen girdi sinyalleri, bu
defa yine bir Schmitt trigger
tarafından karşılanır.
Bu devrenin çıktı kanalları ise Status portuna beslenir. Portlardan sağlanabilecek akım oldukça
düşük olduğu için, küçük
ledler kullanılmıştır.
YAZILIM:
Devremizi kontrol etmek için, şimdiye kadarki
söylediklerimizden de anlaşılacağı üzere bazı
adreslere bilgi göndermek, bazılarından ise bilgi okumak
gerekmektedir. Bu işlemleri C# veya VB.NET’de doğrudan bazı komutlarla
yapmamız olası değil. Bunlarda böyle olanaklar yok. O zaman biz
de başka bir dilde yazılmış DLL(Dynamic Data Library)’ler
kullanarak bu işin üstesinden geleceğiz.
Dolayısıyla biz burada, bu eksikliği
kapatacak , C++ diliyle
yazılmış bir DLL kullanacağız.
Çünkü C/C++’da doğrudan belirli
port adreslerine veri koymak ve oralardan veri okumak için komutlar
bulunmaktadır. Bunu kendimizin yazmasına da gerek yok. Zaten halihazırda bu işi oldukça iyi
yapan ve her türden Windows’la (95/98, 2000,XP) uyumlu, Inpout32.dll
(http://www.logix4u.net bir dll dosyası kullanacağız. Bu “dll”’i ParalelPortApp2.zip dosyamızın
içersinde de bulabilirsiniz.
Devre detaylarımızı bu şekilde
verdikten sonra şimdi,
yukarıda bahsettiğimiz dll hakkında biraz bilgi verelim. Bu dll dosyasında bizim için önemli olan iki adet metot
bulunmaktadır.
1)
Inp(int adress) metodu . Bu metodla belirli bir adresten bilgi okuruz.
2)
Out32(int adres,int veri) Bu metod da belirli bir adrese bilgi
göndermeye yarar.
Diyelim ki, bizim okumak istediğimiz paralel portun
temel adresi 0x378’dir. Bu durumda Status portunun adresi 0x378+2 yani, 0x380 olacaktır. Bu adresten , int data=Inp32(0x380) diyerek status portundaki bilgiyi
okuyabiliriz. Şimdi asıl programımıza geçelim. Önce
bu hazır aldığımız Inpout32.dll’i nasıl kendi
programımıza ekleyeceğiz
onu görelim. Bu dll dosyasını
indirdikten sonra , bilgisayarınızın C:\Windows\System32 dizinine kopyalayın. Sonra, bir C#
Windows Form uygulaması açın.
Projede yeni bir sınıf dosyası ekleyin. Adına InpOut.cs diyelim.
Burada , .NET Framework’ün System.Runtime.Interopservices
sınıfından yaralanarak, DLL’deki istediğimiz metodları
çekeceğiz.InteropServices sınıfının DllImport niteliğini kullanarak, Inpout32.dll’nden ilgili
metodları bu sınıfımızın içerisine
aşağıdaki gibi yazabiliriz.
using System;
using System.Runtime.InteropServices;
namespace FirstPortApplication
{
/// <summary>
/// Summary description for InpOut.
/// </summary>
public class InpOut
{
[DllImport("Inpout32.dll")]
public static extern short Inp32(int
address);
[DllImport("Inpout32.dll")]
public static extern short Out32(int
address,int data);
}
} |
Henüz kartımızı takmadan bir deneme
yapalım isterseniz.
Formumuzun
üzerine bir adet “button” ve iki tane de textBox sürükleyelim.
Burada
yapacağımız şey, TextBox1’in içerisine
yazacağımız, hexadesimal bir sayıyı paralel portumuzun Dataport
kısmına yazdırmak, sonra da bu yazdığımız
değeri oradan geri okuyup, TextBox2’ye yazmak olacaktır.
Kısacası, Data port’a değer göndereceğiz,
gönderdiğimiz değeri oradan tekrar okuyacağız;bu arada hem
Out32(int address,int data) hem de Inp32(int address) metodlarını kullanmış olacağız.
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void button1_Click(object sender, System.EventArgs e)
{
byte data = byte.Parse(hexaData,
System.Globalization.NumberStyles.HexNumber);
if(!(data>=0)) return;
InpOut.WriteControlPort(baseadres,data);
InOut.Out32(0x378,input);
int geri=InOut.Inp32(0x378);
int geri1=geri/16;
int geri2=geri-geri1*16;
textBox1.Text=Convert.ToString(geri1.ToString()+geri2.ToString());
} |
Data portuna koyduğumuz değer, ikili sistemde:
01000111, hexadesimal(67) , onlu olarak da 103 dür. Programımız,
kutuya hexadesimal rakam koymaya uygun olarak düzenlenmiştir. Aslında, metotlarımız
yazılan ve okunulan değerleri
tam rakam(integer) veya hexadesimal biçemli(0x67) gibi kabul etmektedir.
Hexadesimal rakam versek bile o, içeride bunu tam rakama çevirir. Inp32()
metodundan okuduğumuz değer ise devamlı onlu tam rakamdır. Biz bunu basit bir işlemle tekrar hexadesimal’a çevirip , TextBox2
kutusuna yazdırıyoruz. Hexadesimal’a çevirme nedenimiz,
kafamızda hangi bit’lerin 0, hangilerinin 1 olduğunun daha iyi
canlanması içindir.Örneğin, desimal 123 dediğimizde aslında
01111011 demekteyiz. Hexadesimal sitemde bu 7B’dir. “B” harfinden dolayı
sağdaki 4 bitten, 2.bit dışındakilerin 1 olduğu kolaylıkla
anlışılır. 7’nin
çözümü ise hiç zor değildir. Halbuki, 123 dendiğinde bundan
hangi bitlerin yüksek, hangilerinin düşük seviyede olduğunu
çıkarmak ancak biraz hesaptan sonra olabilir.
İkinci programımız daha detaylı olup,
hem Data ve Control Portuna bütün bir değer yazıp, aynı anda tüm
bitleri kontrol etmek, hem de her bir bit’in ayrı ayrı
açılıp-kapanmasını sağlamaya olanak verecek şekilde tasarlanmıştır.
Bu programda ayrıca
girdi kanalı olarak
kullanılan Status Portunu bir zamanlayıcı kontrolüne(Timer) bağlı olarak tarayıp, bu portun durumunu form üzerindeki
“CheckBox” kontrollerine aktaran bir metot da bulunmaktadır.
Birinci olarak, control veya
data port grubuna toptan değer göndermeye yarayan “Data Kelimesi yazma”
groupBox’una bir göz atalım. Burada, kutulara girilecek değer
hexadesimal olmalı. İlgili düğme tıklandığında
, içeride program bu değeri (örneğin FF) normal byte değerine çevirip, öylece ilgili porta
yazmaktadır.
Programda , ana form
üzerine Data ve Control Portunun herbir kanalı için ayrı bir
düğme sürükleyip, bırakma ve bunların herbirinin “Click” olayı
içine ilgili veri değerini girmek oldukça kolay , fakat bir o kadar da
hamallık gerektiren bir şeydir. Biz , burada bunun yerine ButtonArray
diye bir Collection sınıfı
oluşturduk. Bu ButtonArray sınıfının her bir
elemanı NewButton diye , kendisi de Button kontrol
sınıfından türemiş bir elemandan oluşmaktadır. Bu
NewButton kontrol nesnesinin normal
Button nesnesinden tek farkı, fazladan “status” diye bir boolean
değişken içermesidir. Bu değişken, düğmenin açık
mı, kapalı mı olduğunu belleyerek, düğmeye
basıldığında, açıksa kapanmasını(beyaz renge dönüş), kapalıysa
açılmasını(kırmızı renge dönüş) sağlamaktadır. Yani, sadece
görsellik açısından, işimize yaramaktadır, bu
değişken.
ButtonArray sınıfının
AddNewButtonC()(control port grubu
için) ve AddnewButtonD()(data port grubu için) metotlarını kullanarak, program
çalışmaya başladığında , gerekli sayıda
düğmeler ilgili groupBox kutusu içerisinde otomatik olarak
oluşturulmaktadır. Her bir
düğmenin “Tag” özelliğine düğme yaratılırken
sayı numarası atandığından , düğmelerden birisi tıklandığında bunun
hangi düğme olduğu kolaylıkla anlaşılabilmektedir.
Böylece, her bir düğme için bir “EventHandler” yazmak yerine, iki ayrı grup için birer “EventHandler” yazabilmekteyiz. Hangi düğmeye
basıldığı , “Handler” metodu içerisinde “Tag” özelliği
kontrol edilerek bulunur.
Bu düğmeler ,
basıldığında
iki konum arasında gidip gelen türde çalışacaklar(toggle
button). Düğmenin rengi kırmızıya döndüğünde led’in
yanması (hat yüksek seviyede), beyaza döndüğünde de ledin sönmüş
olması(hat düşük seviyede ) gerekmekdedir.
Girdi portu olarak kullanılan Status port’unun her
bir kanalına karşılık gelen bir “checkbox”
bulunmaktadır. Bu portun ilgili kanalı mantıksal yüksek olduğunda, kutu
“checked” , aksi halde “uncehcked”
olacaktır.
İlk programımızda da gösterdiğimiz
gibi , Data portuna veri koyarken fazla
bir güçlük yoktur.Çünkü, bu port’un kanallarında herhangi bir tersine
çevirme durumu
olmadığından , 1 verirsek 1, 0 verirsek 0, değeri
olduğu gibi karşılığını buldu, portun
çıkış kanallarında.
Control portuna veri gönderme:
Control port’unda
durum biraz değişiktir. Çünkü , paralel port bize
sunulduğu haliyle Control portuna denk gelen 8 bitlik bir port’un sadece
ilk 4 biti kullanmış, fakat
bunlardan 0,1 ve 3 numaralı bitler dışarı çıkarken tersine
çevrilmiştir. Bu ne demek
oluyor
şimdi? Şu demek oluyor: Bu
saydığımız bitleri yüksek yapmak için 0, alçak yapmak için
de yüksek değerleri
yazmalıyız ilgili porta.Ancak böyle yaparsak, çıkışta
istediğimiz sonucu elde edebileceğiz.Yani, diyelim ki , bu portun tüm
kanallarını yüksek yapmak istiyoruz(5V). Yazacağımız
değer ne olacak? Sağdan sola
doğru, 0100 veya desimal olarak 4
değeri vermeliyiz.. Sıfırlar da , çıkarken tersine
çevrilip, 1 olacaklarından çıkış olarak 1111
göreceğizdir; yani tüm control port kanallarının “yüksek” olduğunu.Bu portun kontrol ettiği
tüm ledler yanacaktır.
Fakat, bu
“terslenmiş” kanalların kafamızı
karıştırmaması için, yani bildiğimiz gibi 1
verdiğimizde 1, 0 verdiğimizde 0 değeri elde etmek için
aşağıdaki metodu kullanacağız. Metod, bizim için
gerekli dönüşümleri yaparak ,
aynen Data portta olduğu gibi, bu
portun ilgili kanalını 1 yapmak için 1, 0 yapmak için de 0
değerini gireceğiz. Gerisini metot halledecek:
Metoda bir göz atalım
public static void WriteControlPort(int
base_address,int port_data)
{
int data=port_data & 1 ;
if (data ==1) {port_data=port_data & (255-1);}
else{ port_data=port_data | 1;}
data=port_data & 2 ;
if (data ==2) {port_data=port_data & (255-2);}
else{ port_data=port_data | 2;}
data=port_data & 8;
if (data ==8) {port_data=port_data & (255-8);}
else{ port_data=port_data | 8;}
Out32(base_address+2,port_data);//output a byte to control
port.
} |
Metot parametre olarak, LPT1 portunun temel adresini ve
yerleştirmek istediğimiz veriyi almaktadır. Gelen bilginin, 0,1 ve 3. bitlerini ayrı ayrı yokluyoruz öncelikle. Bu
hanelerdeki değerleri alıp tersine çeviriyoruz, ve Status porta bu
yeni dönüştürülmüş veriyi gönderiyoruz. Bir hanenin değerini almak için,
verinin kendisini , ilgili hanenin ağırlıklı değeriyle
“AND”lemek yeterli olacaktır. Eğer elde edilen değer, yine ilgili bitin ağırlık
değeriyle eşit ise, o bit yüksektir, yoksa alçak.
Bir bitin değerini tersine çevirmek için,
eğer bir bit yüksekten alçağa değiştirilecekse, eldeki
veriden bu bitin ağırlık
değerini çıkarmak,yok eğer alçaktan yükseğer geçiş
yapılacaksa, eldeki değeri
ilgili hanenin ağırlık değeriyle “OR”’lamak gerekmektedir. Sonra da , son haline gelmiş veriyi,
temel adres’e 2 ekleyip, Out32()metoduyla ilgili adrese yazmak
kalıyor.Böylece Control portumuza istediğimiz veriyi yazmış
oluyoruz.
Control ve Data Port’ununda Her bir kanalın ayrı
ayrı kontrolu:
Bu eylemler için de ““ToggleDataPortBit()” ve “ToggleControlPortBit()”
metotları bulunmaktadır, programda. Bu metodlar önce ilgili kanalın değerini alır.
Bulduğu değerin tersini yazacak şekilde değer
oluşturur ve porta yazar.
“Status portu”’ndan veri okuma:
Status portunun okunması ise biraz daha ilginç. Bu portta daha önce bahsedildiği gibi 5
kanalımız bulunmaktadır.Bu port bir byte’lık bir
adresin 3,4,5,6 ve 7.
kanallarını kullanmaktadır, Biz sadece bunların ilk 4’üyle
ilgileniyoruz.Yani 3,4,5 ve 6. kanallar. Halbuki değer okurken, tüm bir
byte(8 bit) okunmaktadır.Okuduğumuz bir byte’lık değerden
0,1,2 ve 7. kanallara ait değerleri atmalıyız. Sonra da
elimizdeki , 0dddd000 haline gelen
değeri 3 defa sağa kaydırarak, 0000dddd yaparak, kontrol ettiğimiz değerlerin en
sağa gelmesini sağlamalıyız. Böylece, bu porttan 0-15 arası değer elde ediyor hale geliriz ve de bu değere bakarak ta
hangi kanalın mantıksal 1, hangi kanalın ise mantıksal 0
olduğu kolaylıkla çözümlenebilir.
Bunu şu şekilde yaparız:
public int ReadStatusPort(int base_address)
{
int data= InpOut.Inp32(base_address+1);
data=data & 120;
return (data >> 3);
} |
Metodumuza
parametre olarak LP1 portunun
temel adresini veriyoruz. Metot içerisinde buna bir ekleyerek, status portunun
adresini elde ediyoruz. Bu adresten okuduğumuz değeri 0,1,2 ve 7.
kanallarını devre
dışı bırakmak için 120 ile “AND” liyoruz. Elde edilen
0dddd000 formatındaki bilgiyi 3 defa sağa kaydırarak , xxxxdddd
formatımıza ulaşıyoruz.X’ler ilgilenmediğimiz bitleri
göstermektedir. En sağdaki bit sıfırıncı bittir..
Örnek vermek gerekirse; elde edilen data değeri 7
(desimal) ise , yani 0111, ilk üç bitin yüksek
son bitin ise alçak olduğu ortadadır.
Programımızda, girdi olarak kullanılan status portunun kanallarını taramak için bir adet timer
controlu kullanmaktayız. Timer’ın
herbir “tick” aralığında(interval=100ms alınmıştır), bu girdi
kanallarındaki olası değişiklikleri kontrol ettirmekteyiz
ve bunu yakıp söndürdüğümüz,
ekran ve kart üzerindeki led’lerle göstermekteyiz. Çok kolay bir şekilde
bu kanalları test etmenin yolu, +5V
terminaline bir kablo bağlamak ve kablonun diğer ucunu
sırasıyla status port’un klemense getirilmiş terminallerine değdirmektir.
Aslında, karta güç verildiğinde, bu portun girdilerinde sinyal
beslenmiş gibi ilgili ledler yanacaktır. Bu kullanılan tümleşik devreden kaynaklanan bir durum
olup, önemli değildir.
Örneğin, toprak hattına(0V) bağlanmış bir
kabloyu sırasıyla bu uçlara değdirin;ledlerin söndüğünü ve
de ilgili groupBox’taki “checkBox”’ların “checked” konumdan
“Unchecked” konuma geldiklerine tanık olacaksınız, kabloyu 0V’ta
tuttuğunuz sürece.
Bu bölüm bu kadar. Elimizdeki kart sadece kanalları
dışarı çıkarmaya yaradı. Porttan gelen akım çok
düşük olduğu için bunlarla daha büyük ledler veya küçük de olsa motor
v.s gibi aletleri kontrol etmemiz
olası değil. Gelecek bölümde
bu karttan gelen sinyalleri kullanıp, nasıl röle v.s kontrolu
yaparız, ona bakacağız. Bu ise bize d.c. motor, adım motor,
ve daha yüksek güç isteyen
cihazların kontrolü için olanak sağlayacaktır.
Fevzi Özgül
[email protected]
Makale:
C# ile Paralel Port Üzerinden Elektronik Devre Kontrolü C#, Visual C# ve .NET Fevzi Özgül
|