Arduino Kaynak Kodu İncelemesi -6- DigitalWrite, DigitalRead ve PinMode

Şimdi sıkıcı kısmı sonunda atladık ve çok sevdiğim fonksiyon inceleme kısmına geldik. Siz de değer tanımlamalarını, makroları ve değişkenleri değil işleyen kodları görmek istemiş olsanız gerektir. Şimdi Arduino’da en sık kullanılan ve en temel fonksiyonlardan olan dijital giriş ve çıkış ile alakalı fonksiyonları inceleyeceğiz. Bunun için öncelikle bizim ile beraber wiring_digital.c dosyasını açıp bir taraftan dosyanın tamamını incelemeniz gereklidir.

Bu dosyayı şuradan açıp inceleyebilirsiniz.

https://github.com/arduino/ArduinoCore-avr/blob/master-older/cores/arduino/wiring_digital.c

Arduino’nun kaynak kodunu incelerken aynı zamanda Wiring’in kaynak kodunu da incelediğimizi belirtelim. Arduino’nun Wiring’in kodlarının kopyalanmasıyla meydana geldiğini biliyoruz. O yüzden bu ve buna benzer platformların tamamını incelediğimizi iddia edebiliriz. İçindeki kodlar donanıma göre farklılık gösterse de mantığı ve metodu benzerlik göstermektedir.

Burada iki adet kütüphane dosyasının alındığını görüyoruz. Bu wiring_private.h dosyası içinde wiring.h dosyasını da çağırıyor. O yüzden kodu incelerken üç adet dosyaya daha göz atmamız gerekebilir.

İşte pinMode() fonksiyonumuzun yapısı bu şekildedir. Bu fonksiyonu belki yüzlerce kere kullanırız fakat arka planda neler olduğundan haberimiz olmaz. AVR programlarken tek bir satırda bunu yapabilirken Ardunio’da onlarca gereksiz işlemin yürütüldüğünü görmekteyiz. Sadece pinMode değil digitalWrite ve digitalRead fonksiyonlarında da onlarca komut yürütülür ve fonksiyonların işleyişleri oldukça yavaşlar. Ben birkaç led yakayım, motor süreyim bana yeter diyorsanız sorun yok. Fakat ciddi bir uygulama yapmaya kalkayım diyorsanız sıkıntı çıkarabilir. Arduino’yu öğrenenlerin çoğu da oyuncak niyetine oynamak için öğrenmiyor açıkcası. Mühendislik öğrencisi Arduino’yu öğrenip kullansa dahi bunları bilerek kullanması gereklidir.

Bu fonksiyon uint8_t pin ve uint8_t mode olarak iki argüman alıyor. Pin adındaki değişkene ayak numarasını ve mode adındaki değişkene ise INPUT ya da OUTPUT olarak bir değişken koyduğumuzu hatırlatalım. Wiring.h dosyasını incelediğimizde INPUT’un sıfır (0), OUTPUT’un ise bir (1) olduğunu görürürüz. Yani 1 ve 0 değerlerinden başka bir şey değil. Fonksiyonun nasıl bir argüman aldığını tasavvur ettikten sonra bu argümanların nasıl kullanıldığına bakalım.

Burada pins_arduino.h dosyasında yer alan bir makro kullanılmış. Bu makro digital_pin_to_bit_mask değişkeninden uygun değeri okuyup geri döndürür. Bu değişkeni ise pins_arduino.c dosyasında görüyoruz. Örneğin değerimiz sıfır ise _BV(0) değeri elde edilmiş oluyor. Geri kalanı ise PORT değerinin bitlerine göre sıralanmış. Örneğin 8. ayağımızın _BV() değeri de sıfır oluyor fakat PORTB kullanılmış oluyor. Burada hangi portun kullanıldığı değil hangi ayağa karşılık gelen bitin kullanıldığı önemli. Önceki yazıda yer versem de burada tekrar bahsedelim. Burada pin argümanı ile okuma yapılan ve sonrasında bit değişkenine aktarılan sabitin veri yapısı şu şekildedir.

Burada ayak sırasına göre _BV() değerleri alınır ve geliştirici tarafından tanımlanan sabit bir değerdir. Bu değerleri ne zaman değiştirebiliriz derseniz, Arduino platformunu kullanan kendimize ait bir geliştirme kartı üretmek istiyorsak bu  bitleri donanıma göre değiştirebiliriz.

Dijital çıkış için pin yani portun bit değerini almayı başardık. Fakat hangi portun kullanılacağı hala meçhul. Bunun için yine böyle bir makro kullanıyoruz ve yine pin değişkenine tanımlı PORT adını buradan öğreniyoruz. digitalPinToPort() makrosunu yine pins_arduino.h dosyasında buluyoruz. Bu makro yine digital_pin_to_port sabit diziye ait değeri pins_arduino.c dosyasından okuyup geri döndürüyor. Şimdi bu sabit diziye bir bakalım.

Görüldüğü gibi burada ayak numarasına göre port adları önceden belirtilmiş. Bu port adları kartın tasarımıyla  uyuşmak zorundadır. Örneğin 0’dan 7’ye kadar olan dijital ayaklar PORTD’yi kullanır. Sonrası ise PORTB’yi kullanır. Analog ayaklar ise PORTC’yi kullanır ve 14’den itibaren başlar. Bunu kart üzerinde yazılı halde de görebilirsiniz. Burada kartın tasarımına bağlı olarak bir dizilim yapılmıştır. Örneğin 0 numaralı ayağı seçtiysek bu değer içinden PD değeri alınacaktır. Yukarıdaki yapı ile mantığı aynı olduğu için kolaylıkla anlayabilirsiniz.

Burada reg adında bir işaretçi değişkeni tanımlanmıştır. Bunun kullanımına sonra geleceğiz.

Programlamayı hiç bilmiyoruz ve bariz bir hata yaptık ve kafamıza göre bir numara yazdık. Bu durumda NOT_A_PIN değeri elde edilir ve port değişkenine eklenir. Bu yapı kullanıcı hatasını engelleme yönünde yapılmış bir kontrol yapısıdır. Eğer ayak numarası kısmına 500 yazıp bir şey olmasını beklerseniz hiçbir şey olmayacaktır. return ile fonksiyondan çıkıp program işlemeye devam eder.

Bizim reg değişkenimize burada bir değer atanmaktadır. Bu makronun yine pins_arduino.h başlık dosyasında olduğunu görüyoruz. Bu ise port_to_mode sabit diziden verileri almaktadır. Bakalım argüman olarak kullandığımız port değerine göre hangi değerleri alabiliyoruz.

Bu değerler doğrudan yazmaçların adlarını adresliyor. Döndük dolaştık yine yazmaçlara geldik. Fakat yazmaçlara gelmek için de o kadar prosedürle uğraşmak zorunda kaldık. Birsürü değer, sabit ve dizi işte bu yazmaçlara ulaşmak içindi. Şimdi elimizde BV_() olarak bit değeri var ve PORT olarak yazmaç var. Artık AVR programlamada olduğu gibi işlemimizi yapabiliriz.

mode adıyla aldığımız argümanı ancak şimdi kullanabiliyoruz. Bu mode INPUT ya da OUTPUT olarak iki ayrı değer alabiliyordu. Burada INPUT_PULLUP değerini göremiyorum nedense. Ya o dönem koda eklenmemiş ya da kodun başka bir yerinde karşımıza çıkacak. Fakat şimdiye kadar görmedik. Bunu yok sayalım.

Burada INPUT’un sıfır (0) olduğundan bahsetmiştik. Kısacası yukarıdaki kod şu işlemi yapmaktadır.

Bu bildiğimiz AVR kodudur, başka birşey değil. DDR yazmacındaki bir biti sıfır durumuna getirir. Bunu yapmak içinse yine en temel yol olan bitwise operatörlerini kullanır. Bu komutu çalıştırmak için nerelere gidip geldiğimizi görüyor musunuz ?

Şimdi giriş tanımlamasını yaptık fakat çıkış tanımlaması kaldı. Onu da şu komutla yapıyoruz.

Program burada bizim OUTPUT yazıp yazmadığımıza bakmıyor. INPUT yani 0’dan başka değer yazdıysak bunu çıkış olarak anlıyor ve INPUT değilse çıkış olarak tanımlıyor. OUTPUT yerine 50 de yazsanız aynı yola çıkacaktır. Bu komut ise aşağıdaki komut ile aynı işi görmektedir. Yine o sabitten gidip PD değerini alır sonra gider PD değeri ile yazmaç adresini alır ve *reg’e koyar ve karşımıza yine aynı DDR yazmacı çıkar.

Bir satır kod ancak bu kadar zor yazılabilirdi. 🙂 Üç tane yazmaç öğrenmemek, iki operatör kullanmamak için ne kadar zahmete girildiğini görmüş olsanız gerek.

Dosyanın devamında ise turnOffPWM fonksiyonunu görüyoruz. Bu fonksiyon oldukça basittir ve tek yaptığı iş zamanlayıcılara ait yazmaçları sıfırlar. Burada bir uyumluluk için karar yapısı mevcut olduğundan biraz kod kalabalığı var. Hem Atmega128 hem de ATmega8 için yazılmış. Fonksiyon aldığı argüman ile hangi zamanlayıcıyı kapatacağını belirliyor.

digitalWrite fonksiyonu da yukarıda anlattığımız pinMode fonksiyonundan çok farklı değildir. Arduino kodlarında aynı şeyler tekrar tekrar yapılsa da biz kendimizi yorup tekrar tekrar anlatmayacağız. Bu fonksiyonda farklı noktaları ele alıp pinMode ile aynı olan kısımları geçeceğiz.

Burada farklı bir makronun kullanıldığını görüyoruz. Bunun sebebi ise dijital ayaklardan PWM çıkışının da alınmasıdır. Bu PWM çıkışı alınan zamanlayıcıları tespit etmek ve durdurmak gereklidir. PWM çıkışı alınan bir ayaktan dijital çıkış alınması beklenemez. Bunu programcı programlarken dikkat edemeyebilir. Sonuçta Arduino mühendislere yönelik değil sanatçılara ve tasarımcılara veya yeni öğrencilere yönelik bir alet. Bunu programın hatırlayıp yapması gerekiyor ve bunun için öncelikle bu makroyu kullanıyoruz.

Bu makronun yine pins_arduino.h dosyasında olduğunu görüyoruz. Diğer makrolarla aynı işi yapıp digital_pin_to_timer adındaki dizi sabitinden değeri okuyup geri döndürüyor. Peki bu sabitte nasıl değerler yer alıyor şimdi ona pins_arduino.c dosyasından bakalım.

Burada basit olarak tüm ayakların hangi zamanlayıcıyı kullandığı verisi yer alıyor. Bu veriler ise pins_arduino.h başlık dosyasında görülebilir.

Her bir zamanlayıcıya farklı numaralar verilse de kontrol yapısında NOT_ON_TIMER yani sıfır(0)’ın önemi görülüyor. Burada sadece NOT_ON_TIMER’ın önemini görmekteyiz.

Makroları kullanarak önceden tanımlanan sabit dizilerden değeri çağırdık ve değerimiz NOT_ON_TIMER’dan (0) farklı bir değer oldu. Bu bacağa bağlı bir zamanlayıcının olduğu anlamına gelir. TIMER0A, TIMER0B olarak sırayla adlandırdığımız numaraların önemini ise turnOffPWM fonksiyonunda görüyoruz. Eğer sıfırdan farklı bir değer elde ettiysek turnOffPWM fonksiyonu yürütülür.

Şimdi turnOffPWM fonksiyonunu daha iyi anlayabiliriz. Örneğin elimizde TIMER1B yani 4 değeri ile turnOffPWM fonksiyonuna gidiyoruz. Bu fonksiyon sıralı karar yapılarından oluşuyor ve bizim değerimize uygun kodu yürütüyor.

TIMER1B değerini yine ayaklara göre önceden tanımlanmış sabitlerden aldığımızı hatırlatalım. Bu değer ise burada TCCR1A’nın COM1B1 bitini sıfırlamak için kullanılmış. Normalde programcı bu biti eliyle sıfırlaması gerekirken burada program bizim yerimize yapıyor.

Bu fonksiyonda ise yukarıda anlattığımızın aynısı yapılıyor. Bu sefer PORT yazmaçları üzerinden yürütülüyor. O değeri de portOutputRegister makrosu ile elde ediyoruz. İşleyişini yukarıda anlattığım için tekrar tekrar anlatmayacağım.

digitalRead fonksiyonunun iç yapısını ise yukarıda görmekteyiz. Fonksiyon yapı olarak digitalWrite() ve pinMode() fonksiyonlarına çok benzemektedir. Aynı kodları çıkardığımızda geriye kalan yine AVR kodu kalmaktadır. Burada ise PIN yazmaçlarındaki bit değeri okunur ve HIGH ya da LOW olarak geri döndürülür. Mantığı aynı olduğu için tek tek açıklamamıza gerek yok.

Sonraki yazılarımızda geriye kalan dosyaları inceleyeceğiz. Şu ana kadar kodların temelini incelemiş olduk ve işin mantığını buradan anladık. Bu yazıları okuduğunuzda Arduino dersi verdiğini iddia eden bazı kişilerin Arduino hakkında ne kadar aldatıcı bilgiler verdiğini anlayabilirsiniz. Bu kişiler bugün bile insanları yanlış yönlendirmektedir.

UYARI!!

 

Gökhan Dökmetaş

"Arduino Eğitim Kitabı" ve "Arduino ve Raspberry PI ile Nesnelerin İnterneti" kitaplarının yazarı. Başkent Teknoloji ve Dedektör Merkezi'nde Ar-ge Sorumlusu. Araştırmacı-Yazar.

You may also like...

3 Responses

  1. Murat Özsert dedi ki:

    Teşekkürler. Emeğinize sağlık.
    “Bu fonksiyon uint8_t pin ve uint_8 mode olarak iki argüman alıyor. ” cümlesinde 2. argümanın tipi yanlış yazılmış.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.