31 Ağustos 2020 Pazartesi

DS1621 Entegresiyle Seri Port Termometre


Merhaba. Bu yazıda uzun zamandır üzerine yazmayı ertelediğim bir projeden bahsedeceğim. Sabit disklerle ilgili seri bir noktada çıkmaza girdi, daha fazla araştırmak için yazmayı erteliyordum. Baktım ki ona hemen devam edemeyeceğim araya farklı konu almaya karar verdim.
 
Bu devreyi ilk olarak Güçlü TUĞAY'ın Elektronik Hobi kitabında gördüm. Güçlü TUĞAY kitabında devrenin kendisine de ait olmadığını belirtip yararlandığı kaynakları vermiş. Bende kitabın Ocak 2006 tarihli altıncı baskısı var ve maalesef bağlantılar çalışmıyor. Eğer bu kitaba sahipseniz sorun değil çünkü bir devre şeması (sanırım orjinalden revize edilmiş) kitapta zaten var ve Visual Basic (VB) kodları da yukarıdaki bağlantıdan indirilebilir. Benim açımdan bir sorun daha var, ağırlıklı linux kullanıyorum ve Windows kullansam bile VB outdated olduğu için kodları derlebileceğim bir derleyici bulmam zor. Gerçi iki yıl öncesine kadar VB 2005 Express Edition, Microsoft'un sitesinden indirilebiliyordu ama bulmak için biraz karıştırmak gerekiyordu. 

Çalışmayan bağlantılar için Internet Wayback machine imdadıma yetişti. 09.02.2005 tarihli arşivde devre şeması, parça listesi, devrenin nasıl çalıştığı ve VB kodları bulunabiliyor. Hatta baskı devre planı da var ama ben devreyi protoboard üzerinde kuracağım. Üstelik gördüğümüz üzere adamın adı "Alberto Ricci Bitti" ve bu adı google'da aratınca http://web.tiscali.it/riccibitti/ adresinde sayfanın bir kopyası daha var. Şansıma sayfanın altında "Useful Links" başlığında aynı devrenin C ile linux'ta çalışan sürümünü de buldum. Aksi takdirde kodu VB'den C'ye çevirmek işkence olacaktı. Ve tabi bu sayfa da internet arşivlerinde yer alıyor: Linux thermometer project (rev v0.3, 26.11.2003).


Devre Nasıl Çalışıyor
Devrenin termometre kısmı zaten tek bir entegreden oluşuyor. DS1621'i daha önce Arduino ile I2C yazısında anlatmıştım. Bu nedenle fazla ayrıntısına girmeyeceğim. Devre şeması aşağıda görülebilir:
 
PC Termometre devre şeması
 
Ben projede U2 entegresini kullanmayacağım çünkü ikinci bir sensöre ihtiyacım yok. Önceki Arduino yazısında AT2432'nin 3 adresleme bacağıyla 8 taneye kadar birlikte kullanılabileceğini yazmıştım. Aynı özellik DS1621'lerde de var (önceki yazıda atlamışım). Dolayısıyla 8 farklı sensörle sıcaklık ölçülebilir. Yukarıda U1'in tüm adres bacakları (A0, A1 ve A2) toprağa bağlanmış ancak U2'nin A0 adres bacağı Vdd'ye bağlanmış. 
 
Devrenin üst yarısı +5V gerilim regülatörü. RS232 standardına göre seri portta lojik 0, +3V ile +15V arası ve lojik 1, -3V ile -15V arasında kodlanır. DTR ve RTS pinlerinden gelen bu gerilim yarım diyot köprüsü gibi davranan D1 ve D2 üzerinden regülatörün Vin bacağına ve C1'e gelir; Vout ve C2 üzerinden entegrenin Vdd'sine +5V besleme sağlar. Kapasitörler enerjiyi depolayarak sıcaklık okuma çevriminin tamamlanabilmesine olanak tanır.

Benzer şekilde bu pinlerden (ve CTS pininden) gelen gerilim R2,DZ1 ve R1,DZ2 çiftleriyle I2C'nin çalışma gerilimi olan +5V'a düşürülür. Tersi yönde, entegrenin SDA pinine süreceği 5V, seri portun çalışma gerilimi içindedir ve başarılı bir biçimde okunur. SDA'dan çıkan seri kodlanmış veri yazılım tarafından seri porttan okunur. 

Ben devreyi protoboard'a kurdum. Devre karmaşık değil ama bir noktadan birden çok eleman çıktığı için (özellikle SCL'ya bağlanan) karışıklık olabiliyor. En azından ben her seferinde ilk denememde yapamadığım için ikinci kez denedim. 


Parça Listesi:
2x 1N4148 Diyot
2x 5.1V 1/4W Zener
2x 4.7K Direnç
1x 100nF Kapasitör*
2x 47μF 16V Kapasitör
1x DS1621 Entegre
1x LM2940CT-5 Gerilim Regülatörü (5V)

* 100nF kapasitör(ler) bypass kapasitör olarak düşünülmüş. Dolayısıyla aslında entegrenin beslemesine oldukça yakın yerleştirilmesi gerekiyor. Diğer taraftan ben bunları devreye koymadım. "Yeterince" iyi çalışıyor.


Değerlerin Okunması
En başta belirttiğim üzere devre bilgisayara seri porttan bağlanıyor. Bugünlerde seri portu olan bilgisayar bulmak zor. Dizüstü bilgisayarlarda imkansız, masaüstü makinalarda da nadiren bulunuyor. İstisnai olarak benim seri portu olan bir dizüstü makinam var. Ancak elinizde USB - Seri dönüştürücü varsa bunlar (USB - Paralel dönüştürücülerin aksine) iş görüyor. Elimde bir ucu erkek bir ucu dişi seri kablo vardı. Uzatma olarak onu kullandım. Fotoğraflarda da görülebilir. 

Aşağıdaki fotoğrafla yandaki arasında iki yıl var. Bu nedenle oldukça farklılar. Yandaki fotoğrafta seri porttan bir veri isteği geldiğinde devrede süreyle +5V'un kaldığını görebilmek için bir LED ekledim (870 ohm dirençle birlikte). Devre oldukça kısa süre gerilim altında kalıyor.


Aşağıdaki fotoğrafta, entegreyi parmağımı bastırarak ani sıcaklık değişimleri elde etmek için besleme katının oldukça dışına yerleştirmiştim.


Yazılımı yukarıda verdiğim bağlantıdan indirdim. main.c dosyasındaki PollOnce() fonksiyonu, readtemp.c içindeki ReadTemp() fonksiyonunu sensör numarası ile çağırıyor. Bundan önce, i2cbus.c içerisindeki Open_ComPort() fonksiyonuyla cihazın bağlı olduğu COM port açılıyor.

ReadTemp() fonksiyonu oldukça basit. I2C_Start() ve I2C_Stop() fonksiyonları adı üzerinde I2C iletişim başlangıç ve bitiş bit dizilerini veri yoluna sürüyor. I2C_Tx_byte() fonksiyonunun argümanları, DS1621'in adresi, komutları. Komutların listesi DS1621'in datasheet'inde var.  0x90, DS1621'in taban adresi. Sensör adresi bu sabite eklendiğinde çipin I2C adresi bulunuyor. 0xEE komutu bir sıcaklık okuma isteği gönderiyor. 0xAA ile sıcaklık veriyoluna sürülüyor ve I2C_Rx_byte() fonksiyonuyla veriyolundan okunuyor. İlk okunan değer, sıcaklığın tamsayı kısmı, ikinci değer ondalık kısmı (DS1621'in hassasiyeti yarım derece. Dolayısıyla ikinci değer 0 veya 128 olabilir). Eksi sıcaklıklarda DS1621, değerin ilk bitini set ediyor (sign bit) örn -1 derece için 255. Ancak bizim makinamız 8bit'ten daha büyük olduğundan, onu sign bit algılamak yerine 128'den büyük bir sayı olarak okuyor. Bu nedenle gerçek sıcaklığa çevirmek için 128'den büyük değerlerden 256 çıkarmak gerekli.

i2cbus.c dosyası, programın seri portla haberleşmesi ve gönderilecek/alınacak byte'ların I2C protokolüne uyarlanmasını sağlıyor. I2C_{T|R}x_byte() fonksiyonları aldıkları byte parametresini, bit bit işleyip 0 ve 1'ler için SDA ve SCL pinlerine sinyalleri uygun sırayla sürüyor.

Bu kodda çok önemli bir sorun var. Kod, bugünün hızlı bilgisayarlarında düzgün çalışmıyor. Bu nedenle ufak değişikler yapılması gerekiyordu. 

main.c içinde;
a) 11. satıra "int i2wait = 0" ekleyerek i2wait değişkenine bir ilk değer verdim. Bu bir bekleme parametresi olarak kullanılacak.
b) 88. satırda case 'l': bloğunun altına aşağıdaki satırları ekledim:
case 'w':
  i2wait = atoi( argv[i + 1] );
break;

Böylelikle komut satırından -w ile bekleme süresi girilebilecek.

i2cbus.c içinde;
c) 24. satıra "extern int i2wait;" ekleyip main.c'deki global değişkeni burada tanımladım.
d) SCL_High(), SCL_Low(), SDA_High(), SDA_Low() fonksiyonlarının sonunda Wait() fonksiyonunu çağırdım. Orjinal dosyada 171, 176, 181 ve 186. satırlar.
e) Son olarak 203. satırda usleep(i2wait); satırını ekledim. 

Dosyanın değişiklikleri içeren son hali buradan indirilebilir. make komutu derlemek için yeterli. Son olarak Linux Mint'de kendi kullanıcımla programı çalıştırabilmek için kullanıcımı dialout grubuna eklemem gerekti. Eğer program kendi kullanıcınızla çalışmıyor ama sudo'yla çalışıyorsa sorun kullanıcı gruplarındadır.

gettemp'in kullanımı çok zor değil. Öncelikle dmesg'le hangi seri portlar var bakıyorum:

dmesg | grep -i "serial\|tty"
[    1.188451] Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
[    1.188486] serial 00:04: [io  0x03f8-0x03ff]
[    1.188570] serial 00:04: [irq 4]
[    1.189083] serial 00:04: activated
[    1.209764] 00:04: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A

Benimki fiziksel seri port olduğu için kernel yüklenir yüklenmez bulunuyor. USB - Seri dönüştürücü kullanılacaksa watch "dmesg | tail" ile en son takılan aygıttan port bulunabilir. Benim portum ttyS0 olduğundan -c parametresiyle 0 vermem gerekiyor. Entegrenin I2C adresini de 0 ayarladığımdan -p0 kullanmam gerek. İstenirse -d 1 (veya 2) ile debug modu açılabilir. Dikkatli okuyucu 'w' parametresini kullanmadığımı farkedecektir. Boş bir Wait() fonksiyonu bile okuma çevriminin tamamlanması için yeterli gecikmeyi sağlıyor.

Bu devrenin pratik olarak kullanımı belki bilgisayarda değil ama seri port kullanan cihazlarla olabilir. gettemp'i crontab'a koyup çıktısını dosyaya yazdırarak evin günlük sıcaklığını dakika dakika kaydetmiştim. Tek bir seri porttan sekiz taneye kadar sensör bağlanabilmesi ilk bakışta önemli gibi görülebilir ama I2C veriyolunun uzunluğu 1-2m'yi aşamaması nedeniyle tek kullanım alanı dar alanda yüksek sıcaklık değişimlerinin gözlemlenebileceği yerler olabilir. (Uzun mesafe veri iletişimi benim uzmanlığım değil ancak stackexchange'de uzun mesafede kullanacaksanız I2C yerine diğer protokolleri tercih edin diyor). 

Bugün hava 20 derece