Arduino’dan Makine Diline – LED Yakma

Arduino’da LED yakmayı gösteren belki onlarca video ve makale vardır. Biz ise işin en derinine ineceğiz ve Arduino kodundan makine diline kadar aynı programı ve çalışma mantığını sizlere anlatacağız. Arduino’nun kaynak kodunu anlatırken Arduino ve AVR arasındaki bağı kurmuştuk. C ile AVR programlama derslerinde ise az da olsa mikrodenetleyici mimarisinden ve Assembly dilinden bahsetmiştik. Bu yazıda ise Arduino’da yazdığımız LED yakma kodunun hangi işlemlerden geçerek ve dönüşümlere uğrayarak bu kodun mikrodenetleyici işlemcisi tarafından çalıştırıldığını göreceğiz.

Bu verdiğimiz küçük bir örnek bütün bir sisteme en büyük resimden bakmamızı sağlayacak. Arduino ile LED yaktım diye sevinmenin yanı sıra bu ledin nasıl yandığını merak ediyorsanız bu yazı tam size göre olacaktır. 🙂

Arduino

Arduino kodu burada anlaması ve anlatması en basit koddur. Çünkü bütün işlemler perde arkasında gerçekleşmekte ve biz ise iki fonksiyon yazarak kodumuzu çalıştırmaktayız. Kartın üzerindeki ledi yakmak için gereken kod ise şu şekildedir.

Bu konuşma dili gibi gelen kod fonksiyonu anlamanıza biraz daha engel koymaktadır. LED_BUILTIN’den kasıt nedir ? OUTPUT ve LOW aslında ne manaya gelir? Bunun arkasında hangi değer ve kodlar vardır?, diye düşünmek yerine şöyle bir kod da yazabiliriz.

Böyle yazılmış bir kodda LED_BUILTIN’in 13. ayak olduğunu ve OUTPUT ve HIGH değerlerinin ise 1 olduğunu çok rahat anlayabiliriz. Tabi okunurluğu etkileyecektir orası ayrı fakat kaynak koduna bakmadan fonksiyonun işleyişi hakkında az da olsa fikir sahibi olmamızı sağlayacaktır.

pinMode ve digitalWrite fonksiyonları Arduino kütüphaneleri tarafından AVR koduna dönüştürülür ve AVR-GCC derleyicisi tarafından derlenir. Bu dönüşüm sürecini anlamak için Arduino’nun kaynak kodunu incelediğim yazıma göz atabilirsiniz. Aynı yazıyı buraya kopyalamak istemediğimden okumayanlar okuyup yazıya geri dönmelidir.

http://www.lojikprob.com/embedded/arduino-kaynak-kodu-incelemesi-6-digitalwrite-digitalread-ve-pinmode/

Bu yazıdan anlaşılacağı üzere Arduino ve AVR-GCC derleyicisi arasındaki bağı sağladık. Şimdi bu kodların dolambaçlı bir yoldan da olsa temel AVR giriş ve çıkış kodlarına dönüştürüldüğünü görüyoruz. Arduino ile işimiz burada bitti ve AVR-GCC kodlarıyla devam edelim.

AVR-GCC

C dilinde AVR programlarken kodumuzu çeşitli yollarla yazabiliriz. Ben en sade ve en temel haliyle bu kodu yazacağım.

Arduinodaki pinMode fonksiyonu bu kodu çalıştırmak için onlarca satır kod işletiyordu. Burada ise kodu doğrudan yazmış oluyoruz. Gerçi orada _BV() makrosu kullanılsa da _BV() makrosu burada yazdığımız (1<<5) biçimini oluşturmaktan öte gitmez. Aynı şekilde PORTB’ye değer atamakla ledimizi yakmış oluyoruz. Arduino’da kart üzerindeki ledin bağlı olduğu ayak AVR mikrodenetleyicisinin B portunun 5 numaralı ayağına denk geliyor.

Burada DDRB ve PORTB’nin ne olduğu konusunda kafamızda soru işareti yer alıyor. Bu bir makro mu tanım mı değişken mi bilemiyoruz. Ne maksatla kullanıldığı hakkında fikrimiz yok. O yüzden derleyicinin dosyalarını inceleyeceğiz ve bunların ne olduğuna bakacağız.

iom328p.h dosyasını açtığımızda bütün bu tanımların asıl değerleri karşımıza çıkıyor.

Burada DDRB dediğimiz _SFR_IO8(0x04) makrosundan başka birşey değil. Peki bu _SFR_IO8() makrosunun ne olduğuna gelirsek bunu da avrlibC kaynak kodlarına baktığımızda görüyoruz.

Bu makro giriş ve çıkış adresini bellek adresine çevirir. Burada __SFR_OFFSET değeri olan 0x20 değeri _MMIO_BYTE fonksiyonundan dönen değere ekleniyor ve _SFR_IO8() tarafından geri döndürülüyor. Yine karşımıza bir makro çıkıyor şimdi de bu makroya bakalım.

Bu makro ise belirtilen adrese dereferans işlemi uygular. Yani işaretçi tarafından gösterilen hafıza birimindeki veriyi geri döndürür. Böylelikle DDRB adresine değer atayabiliriz. Burası C programlamayı ilgilendirirken DDRB’nin adresinin aslında en başta ifade edilen 0x04 olduğunu görüyoruz. DDRB demenin 0x04 demekle aynı şey olduğunu söyleyebiliriz.

Aynı durum PORTB için de geçerlidir. Burada PORTB’nin hafıza adresinin 0x05 olduğunu görüyoruz. Buraya kadar C dili seviyesinde anlatacağımız bir şey kalmadı. Bundan sonra ise C dili Assembly’e çevrilecektir.

AVR Assembly

Bu yazdığımız kodu AVR Assembly dilinde şöyle yazabiliriz.

Burada aslına tam manasında alt seviyede bir program yazmadık diyebiliriz. Assembly dilinde programlama yaparken de çeşitli kütüphane dosyaları mevcut olabilmektedir. Aynı C dilinde programlama yaparken olduğu gibi DDRB ve PORTB’nin tanımlı olduğu adres değerleri burada gizlenmiştir. O adres değerlerini yine dosyayı okuyarak görebiliriz. Aynı kod şu şekilde de yazılabilir.

Bu sefer alt seviye programlamanın varacağı en son noktayı görmüş olduk. Kimse çıkıp da Assembly yerine makine dilinde yazayım demediği için ve Assembly dilinde de en alt seviye böyle program yazılabileceği için bundan ötesi yok diyebiliriz. Elimizde kod olarak sbi komutu ve değerlerden başka bir şey yok. Bu adreslerin ne olduğunu yukarıdaki incelememizde öğrensek de datasheet’den hepsini görmemiz mümkün. Örneğin 0x04’ün DDRB yazmacının adresi olduğunu görebilirsiniz.

Burada sbi adında bir komutumuz var. Bu işlemcinin komut kümesine ait bir kod. Yani programlama diline ait bir komut değil donanıma ait bir komuttur. Biz bu kodu yazarak doğrudan donanıma “Şunu Yap!” diyoruz. Bu komutun ne olduğunu anlamak için ise mikroişlemcinin komut kümesindeki kodları üreticinin yayınladığı belgeden öğreniyoruz. Şu bağlantıdan bütün işlemci komutlarını görebilirsiniz.

https://people.ece.cornell.edu/land/courses/ece4760/AtmelStuff/AVRinstr2002.PDF

Bu dosyadan öğrendiğimiz kadarıyla sbi komutu bir giriş çıkış yazmacındaki belirtilen biti bir (1) yapmaya yarayan bir mikroişlemci komutu olarak görev yapıyor. Bu komut birinci değer olarak yazmacın adresini alıyor ikinci değer olarak ise 0-7 arası olarak portun bit değerini alıyor. Bizim belirttiğimiz değerlere göre işlem yapıyor. Biz burada portun adreslerini ve bit numarasını belirterek önce DDRB yazmacının 5. bitini çıkış olarak tanımladık sonra ise PORTB yazmacının 5. bitinden bir (1) çıkışı aldık. C dilinden Assembly diline çevirince çıkan bu ifadeyi sonrasında çevirici program makine diline çeviriyor.

Makine Dili

Arduino’dan makine diline kadar geçen süreç sonunda yazdığımız program tabi ki burada yazdığımız gibi olacak değil. Arada pek çok teferruatlar ve alakasız kodlar da olacaktır. Her aşamada performanstan ve hafızadan kayıp yaşamamız kaçınılmazdır. Biz bunları görmezden geliyoruz ve gerçek kodun ne olduğunu ortaya koyuyoruz. Yukarıda yazdığımız AVR Assembly dilindeki kodun makine dilindeki karşılığı ise şu şekildedir.

Bunu bu şekilde anlamamız mümkün değil. Öncelikle daha rahat göstermek için ikilik formata çevirelim.

Burada bir ve sıfır olarak gördüğümüz değerleri anlamak için Assembly komutlarının opcode adı verilen ikilik tabanda ifadesine bakmamız gerekiyor. Yine paylaştığım dosyada görebileceğiniz üzere sbi komutunun ikilik tabanda işleyişi şu şekildedir.

1001 1010 AAAA Abbb

Burada “10011010” ile ifade edilen sbi komutunun ta kendisidir. A ise yazmaç adresi bitleri olup “AAAAA” şeklinde beş bit (0-31) arası değer alır. b harfi ile ifade edilen ise bizim belirttiğimiz bit konumu olup üç bit (0-7) arası değer alır. Bizim çevirdiğimiz kodu biraz tersten okumamız gerekse de şunu anlayabiliriz.
1001 1010 , sbi komutudur.

101, Bizim belirttiğimiz 5 değeridir.

00100, 0x04 adresidir (DDRB)

00101, 0x05 adresidir (PORTB)

Böylelikle hem Arduino, hem C, hem AVR Assembly hem de makine dilinde nasıl led yakıldığını ve Arduino kodundan led yakılışına kadar geçen süreci görmüş oldunuz. 🙂

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...

Bir cevap yazın

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