İndir
Transkript
İndir
ARM Programlama ARM Programlama 2 ARM Programlama ARM konusunda oluşturulan bu e-kitap size ARM Cortex-M4F tabanlı işlemcilerin çalışma yapısı, C programlama ve Assembler konusunda gerçekten hatırı sayılır bir kaynaktır. Eğitimler Texas Instruments firmasının düşük güç tüketimi ile çalışan ve oldukça ucuz olan Stellaris Launchpad kiti kullanılarak anlatılmıştır. Yazılar Dr. Miro Samek'in yayınladığı ve Eren BAŞTÜRK'ün çevirdiği eğitimler temel alınarak yazılmıştır. Bu yazıları hazırlarken desteğini gördüğüm Ahmet Alpat'a ve tüm Çizgi-TAGEM ailesine teşekkürlerimi sunuyorum. Faydalı bir yazı olması dileği ile, iyi çalışmalar. Arif Ahmet Balık Yazar Hakkında ARİF Ahmet BALIK Bir teknik lisede web programcılığı dalında okuyor. Gömülü sistemler ve FPGA meraklısı. birkodyaz.blogspot.com.tr adlı bloğun kurucusu ve yazarı. Şuan YTÜ Teknoparkta bir ARGE şirketi olan KATIHAL Elektronik Yazılım ARGE AŞ'de part-time olarak çalışıyor. İstanbul HackerSpace üyesi. İletişim [email protected] Eren BAŞTÜRK 1990 Kayseri doğumlu. 2005 yılında ilkokul eğitimini tamamlayıp Antalya Adem Tolunay Anadolu Lisesinde eğitimine devam etti. 2009 yılında Süleyman Demirel Üniversitesi Elektronik ve Haberleşme Mühendisliği bölümünü kazandı. Halen aynı üniversitede 3.sınıf öğrencisi olarak eğitimine devam etmektedir. Gömülü Sistemler , Linux İşletim Sistemleri , Fpga , Yazılım Geliştirme ve Sayısal Elektronik üzerine çalışmalarını sürdürmektedir. İletişim [email protected] ARM Programlama 3 ARM Programlama İÇİNDEKİLER 1. Başlangıç .........................................................................................................................4 2. Sayım ..............................................................................................................................10 3. Kontrol Akışı ..................................................................................................................42 4. Değişkenler ve İşaretçiler ..............................................................................................56 5. Led Yakıp Söndürme.......................................................................................................75 6. Önişlemci ve Volatile.....................................................................................................101 7. C'de Bitsel Opreratörler ...............................................................................................114 8. Diziler ve İşaretçi Aritmetiği.........................................................................................135 9. Fonksiyonlar ve Stack...................................................................................................155 10. Modüller, Özyineleme ve AAPCS………………………………………………………………………………169 ARM Programlama 4 ARM Programlama ARM Programlama 5 ARM Programlama Herkese Merhabalar, İlk derste ücretsiz gömülü geliştirme araç setini (IAR Embedded Workbench) yükleyeceğiz ve pahalı olmayan bir geliştirme kartı olan Stellaris Launchpad'i nereden sipariş edeceğimizi göstereceğim. Bu kart sayesinde kodlarınızı fiilen gerçek bir mikrodenetleyici üzerinde çalıştırabilirisiniz. Ancak geliştirme kartı olmadan da bu eğitim serisini takip edebilirsiniz çünkü komut seti simülatörünün nasıl kullanılacağını öğreneceğiz. C Programlama Dili Öğreneceğimiz yüksek seviyeli olarak adlandırılan programlama dili C'dir. Fakat sıklıkla düşük seviyeli makine koduna ineceğiz ve size gömülü işlem sürecinde neler olacağını göstereceğim. Siz'de işlemcinizin kodunuzu nasıl çalıştırdığını verileri nasıl işlediğini ve bir bilgisayarın gerçek dünyada yapabildiği şeyleri göreceksiniz. Bir ledi yakıp söndürmek gibi. Bu kavrama gücü sizin C dilini daha etkin şekilde ve daha fazla güven duygusu ile kullanmanızı sağlayacak. Programlama anlayışını kazanacaksınız. Sadece programınızın yapması gerekenleri değil, bunun yanında bu durumların nasıl makine dolarına nasıl çevrildiğini anlayışını ve işlemcinin kodları ne kadar hızlı çalıştırabildiği anlayışını kazanacaksınız. ARM Cortex-M4F ARM Programlama 6 ARM Programlama Bu Kursta Arm Cortex-M4F adındaki işlemciyi kullanacağız. Ama bu öğrendiklerimizi bütün Cortex-M işlemci ailesine CortexM0'dan M0+'a, Cortex-M3 ve M4'e uygulayabileceksiniz. Bu kurs boyunca günümüzde ve uzun yıllar boyunca bulabileceğiniz gömülü mikrodenetleyici işlemci çekirdeklerinden en popüler, en modern ve en fazla enerji tasarruflu işlemci ailesini seçtim. IAR Embedded Workbench Öncellikle bu kurs için ilk ihtiyacımız olan gömülü geliştirme setini "Embedded Workbench for ARM" için desteklenen IAR'dan EWW sistem olarak adlandırılan profesyonel araç setini seçtim. Bu araç setinin ticari versiyonu piyasadaki en pahalı versiyonlardan birisidir ancak IAR size kod limiti olan zaman limiti olmayan bir yazılım geliştirme sürümünü soruyor, şimdi hangi sürüm nasıl indirilecek ve nasıl yüklenecek göstereceğim. IAR EWARM'ın yükleneceği websitesi www.iar.com. Siteye girdikten sonra şekildeki gibi önce SERVICE CENTER sonra Downloads'a tıklayalım. ARM Programlama 7 ARM Programlama Sonra şekildeki gibi ARM için Size-limited licence'n altında bulunan bağlantıya tıklayalım. Karşımıza çıkan sözleşmeyi aşağıya kaydırarak şekildeki gibi indirme bağlantısına tıklayalım. ARM Programlama 8 ARM Programlama Dosya boyutu biraz büyük olduğu için indirme biraz zaman alabilir. Dosya indikten sonra çift tıklayıp açın ve yüklemenin tamamlanmasını bekleyin. ARM Programlama 9 ARM Programlama Yükleme bittikten sonra karşınıza çıkan ekrandan şekildeki gibi "Install IAR Embedded Workbench" üzerine tıklayın. Geriye kalan standart yükleme işlemini gerçekleştirin. Yükleme bittikten sonra karşınıza bir uyarı çıkacaktır bu uyarı size ARM Programlama 10 ARM Programlama "licence dongle yüklensinmi" diye soruyor. buna hayır deyip geçiyoruz. IAR ile ilk karşılaştığınızda size bir kayır işlem kutusu ibraz edilecek. "Register"a tıklayın. Doldurmak zorunda olduğunuz kayıt formu karşınıza çıkacak buradaki en önemli adım kod limiti lisans türünü seçmek geri kalan şeyleri doldurup son olarak "submit registration" butonuna tıklayın. Mail adresinize eglen linki açın ve lisans numarasını IAR'ı açtığımızda karşımıza çıkan ekrandaki metin kutusuna yazın. Tebrikler! Artık gömülü yazılım geliştirmek için bir araç setine sahipsiniz. Stellaris LM4F120 Launchpad Dersin son aşaması olarak pekte pahalı olmayan bir geliştirme kartının nasıl alınacağını göstereceğiz. Tekrar ediyorum bu aşama gerekli değildir IAR araç setinde bulunan simulatör ile derslerin büyük bölümünü takip edebilirsiniz. Şuan Çizgi-TAGEM'in internet sitesinde %29 indirimli olarak 39,83 TL'ye bu geliştirme kartını alabilirsiniz. ilgili web sitesi : http://market.cizgi.com.tr/product/education/ti-stellaris ARM Programlama 11 ARM Programlama SAYIM Tekrar merhaba. Gömülü sistem programlama derslerinin 2. serisine hoş geldiniz. Bu derste bilgisayarların nasıl sayım yaptığını göreceğiz. Bu ve bundan sonraki derslerde, ilk derste anlatılan IAR araç setinin ücretsiz sürümünü kullanacağız. İlk projemizi oluşturalım, IAR EWARM'ı başlatın ve Project > Create New Project menüsünü seçin. Karşımıza çıkan ekranda C proje tipini genişletelim ve main'e tıklatalım. ARM Programlama 12 ARM Programlama Bir dosya gezgini açılacak, projeniz için kaydedilecek bir yer seçmelisiniz. Ben bütün ders boyunca Gömülü Programlama adında bir klasör oluşturup projeleri buraya kaydedeceğim, sizinde böyle yapmanızı tavsiye ediyorum. Bu klasör altında ders 1 adında bir klasör daha açıp proje isimini ise proje yapıyorum ve kaydet diyoruz. ARM Programlama 13 ARM Programlama Gördüğünüz üzere, birazdan açıklayacağım proje oluşturulmuş bir main dosyası içeriyor. ARM Programlama 14 ARM Programlama Projenin konfigürasyonunu bitirmek için birkaç parametreyi ayarlamanız gerekiyor. Project > Options menüsüne tıklatalım. Target sekmesi içinde, işlemci türünü belirtmemiz gerekiyor. Device'i seçin ve seçim butonuna tıklayın, listeden "Texas Insruments > LM4F Family ve LM4F120H5QR" donanımını seçin. ARM Programlama 15 ARM Programlama Hemen sonra C/C++ Complier kategorisini seçin. Varsayılan dil olarak C'yi ve varsayılan C diyalektini c99 olarak görüyorsunuz. Bu eğitim boyunca size en yeni C diyalektini öğreteceğim. (Diyalekt : Yazım Standardı) Son olarak Optimizations sekmesini açalım ve varsayılan optimizasyon seviyesinin low olduğundan emin olalım. ARM Programlama 16 ARM Programlama yüksek optimizasyonlu kodun nasıl yazıldığını öğretene kadar, böyle kalması gerekiyor. OK deyip kapatıyoruz. Böylelikle, sonunda IAR araç seti aracılığıyla oluşturulmuş kodlara erişiyoruz. Nitekim ilk olarak geçerli olan C kodlarını doğrulamayı deneylim. Bunu derleme işlemi ile yapabiliriz. Bu derleme derleyici denilen (Complier) denilen bir program aracılığı ile çalışıyor. Project menüsünden Make seçeneğini seçin ve F7 kısa yolunu bir yere not edin, çoğunlukla bu kısayolu kullanacaksınız. ARM Programlama 17 ARM Programlama Projenizi ilk derlediğinizde IAR bir dosya gezgini ile size çalışma alanınızı sorar, genel bir isim verip kaydedelim (calisma_alani) Bu derleme 0 hata ve 0 uyarı ile tamamlanıyor. İlk legal programınız için tebrikler. ARM Programlama 18 ARM Programlama Kodların daha düzenli ve okunabilir olması için biraz düzenleme yapalım. ARM Programlama 19 ARM Programlama ilk küme parantezini bir üst satıra aldık ve return 0; ve bundan sonra gelecek ifadeleri 4 boşluk girinti olacak şekilde ayarladık, aslında bu derleyici için hiçbir önemi yoktur, derleyici bütün kodları tek ve uzun bir satır olarak algılar, yaptığımız bu düzenlemeler kodun okunabilirliğini arttırmak içindir. Fakat C ile yaptığımız her işlem legal değildir. Örneğin illegal bir şeyler yazıp F7'ye basıp tekrar derleyelim. Bu sefer derleyici bize hataları bildiriyor. Hata raporunun üzerine çift tıkladığınız zaman sizi hatanın olduğu satıra götürüyor. Problemi giderdikten sonra kodun doğruluğunu kontrol etmek için tekrar derleyiciye sormak iyi bir yol. ARM Programlama 20 ARM Programlama Derleyicinin, sizi omzunuzun üzerinden izleyen en iyi dostunuz olduğuna inanmanızı istiyorum, tüm ihtiyacınız sık sık F7'ye basarak ona bir şans vermeniz. Şimdi bilgisayarların nasıl sayım yaptığını göstermek için kullanacağımız bir sayaç değişkeni tanımlayalım. Bir değişken bir değeri bilgisayar belleğinde tutmak için bir konumdur, bir sayı gibi. C'de bir değer kullanmadan önce bir değişken tanımlamak zorundasınız. Bunu değişkenin türünü belirtip (1) ardından bir isim belirtip (2) ve opsiyonel olarak bir başlangıç değeri belirterek yapabilirsiniz (3). ARM Programlama 21 ARM Programlama Hemen F7'ye basarak kodumuzun doğru çalışıp çalışmadığını derleyiciye soralım. Güzel. Hiçbir hata yok fakat sayac adlı değişkeninin tanımlandığı ancak kullanılmadığını söyleyen bir uyarı var, bu doğru. Şimdi sayaç değişkenini şu anki değerinden birer birer arttıralım. C'de bu arttırma için ön arttırma (pre-increment) olarak adlandırılan özel bir operatöre (++) sahip. ARM Programlama 22 ARM Programlama Her zamanki gibi derleyiciyi kontrol edelim. Bilgisayarın nasıl sayım yaptığını izlemek istediğimiz için sayac adlı değişkeni birkaç kez daha arttıralım. ARM Programlama 23 ARM Programlama F7'ye basıp son bir kez daha derleyelim ve düzgün çalıştığından emin olalım. Şimdi bu programın nasıl çalıştırılacağını göstereceğim. Öncelikle projenin simulatör için yapılandırıldığından emin olun, aslında bunu üstteki simulatör menüsünden anlayabilirsiniz fakat iki defa kontrol edelim. Project > Options menüsüne tıklayın. Debugger kategorisinde Simulator seçeneğini görmelisiniz. ARM Programlama 24 ARM Programlama Programı çalıştırmak için 2 seçeneğe sahipsiniz. İlki Project > Download and Debugmenüsü, diğeri ise araç çubuğu butonu. Ben bu butonu kullanacağım. Şimdi, IAR araç seti hata ayıklayıcısı (Debugger) moduna geçti. Takip eden hata ayıklayıcı görünümlerinin görünür olduğundan emin ARM Programlama 25 ARM Programlama olun (Disassembly, Memory, Register ve Locals), bunları açmak için View menüsünden ilgili alanları seçelim. Menüleri sürükleyip düzenleyelim. Böylece ARM Cortex-M4F işlemcisinin içindeki en iyi görünüme sahibiz. ARM Programlama 26 ARM Programlama Öncelikle Disassembly menüsüne göz atalım. Programımız için derleyici tarafından üretilmiş olan kodları bu görünüm menüsü size makine kodu olarak gösteriyor. Ana (main) fonksiyomuzun başlangıcındaki vurgulanmış satırda işlemci durmuş. ARM Programlama 27 ARM Programlama Makine Komutları, bilgisayarın içinde başka bir deyişle sadece sayılardır. Sembollerin sağındaki komutlar, komut anımsatıcı (instruction mnemonics) olarak adlandırılır ve komut anımsatıcıları hata ayıklacı tarafından okunurluğu arttırmak için ekleniyor. ARM Programlama 28 ARM Programlama Sembollerin solunda kalan sayı sütunları komutların bellek adresidir. Bellek adresleri basit olarak belleğe bayt olarak atanan sayılardır. Unutulmamalıdır ki komutlar sadece bellekteki sayılardır. Bellek görünüm menüsüne bir göz atalım. Belleği büyük bir bayt tablosu olarak düşünebilirsiniz. Sıfırdan başlayıp sıralı olarak numaralandırılmış. ARM Programlama 29 ARM Programlama Bunlar sıraları sayılar, adresler olarak adlandırılanlar, bellek görünüm menüsünün solunda sayı sütunu boyunca gösteriliyor. Bellekteki makine kodlarını daha iyi tanımak için görünümü 2xbirim olarak değiştirelim çünkü ARM Cortex komutlarının büyük çoğunluğu bellekte 2 bayt işgal eder. Şimdi komutları rahatlıkla tanımalısınız. Örneğin 0x12c adresinde, aynı sayıyı Disassembly menüsünde göreceksiniz. ARM Programlama 30 ARM Programlama Bu 0x130 ile 0x14a bulunan komutlar içinde aynıdır. 0x12 ifadesinden sonra gelen c harfinin neyi temsil ettiğini kısaca açıklamak istiyorum. ARM Programlama 31 ARM Programlama Her bir komut bir sütunda saklanır ve sütunlar yukarıda görüldüğü gibi adlandırılır, yani0x120 adresi 000d verisini tutarken 0x122 adresi eb00 verisini tutar. Buraya kadar herşey anlaşıldıysa, C kodunun başından sonuna kadar satır satır ilerleyelim. Bunu yapmak için aşşağıda gösterildiği şekilde Step-Into butonuna tıklayın. ARM Programlama 32 ARM Programlama Görüldüğü üzere mevcut komut bir ilerledi ve sayac değişkeninin değeri 0 olarak değişti. ARM Programlama 33 ARM Programlama Ayrıca Register görünümünü dikkatlice incelerseniz PC saklayıcısı0x12e olarak değişti. PC program sayacını (Program Counter) simgeliyor çünkü program sayacı komutların sayımını yapıyor ve her zaman mevcut komutun adresini tutuyor. Şimdi bir sonraki komutu çalıştırmak için Step-Into butonunun üstüne bir kez daha tıklatalım. Bu sefer sayıcı (sayac) değerinin değeri 1'e yükseldi. Sayıcı (sayac) değişkenin R1 saklayıcısında yer aldığını locals ARM Programlama 34 ARM Programlama menüsünde görebilirsiniz. Kaydedici (Register) görünüm menüsünü gözden geçirdiğinizde deR1'in değerinin 1 oldğunu görebilirsiniz. Fakat nedir bu saklayıcılar ? Eğer şimdiye kadar bir hesap makinesi kullandıysanız önceden bir fikir sahibisiniz demektir çünkü mikrodenetleyicilerdeki kaydedicilerle hesap makinesindeki kaydediciler birbirlerine çok benzer. Genel anlamda bir hesap makinesinin bellek kaydedicisine ekleme yapabilir (+), geri çağırabilir (M+) ve temizleyebilirsiniz (MRC). ARM Programlama 35 ARM Programlama ARM Cortex-M işlemcisi bunun gibi 16 kaydediciye sahip, bunlarR0'dan R15'e sıralı olarak isimlendirilmiş. Fakat burada 15 tane kaydedici görüyoruz. Peki nerede bu 16. kaydedici. R15, PC kaydedicisinin diğer adıdır. Tüm bu kaydediciler 32 bitlik sayıları tutabilir. ARM Programlama 36 ARM Programlama Bu kaydedicilerin önem durumu gerçek şu ki makine kodları genel olarak bir saat çevriminde (clock cycle) doğrudan kayedicileri hünerli bir şekilde kullanabilirler. Zaten komutların kaydedicileri kullanmasının 2 örneğini gördünüz 0'dan R1'e olan değişim ve R1'e ekleme yapma gibi. Şimdi aynı düzende kod boyunca ilerleyelim (Step-Into) ve sayaç (sayac) değişkeninin artışını izleyelim. ARM Programlama 37 ARM Programlama Herşey beklenildiği gibi çalışıyor gibi görünüyor fakat sayaç (sayac) 10'a ulaştığında ilginç birşey oluyor. Hatırladığımız kadarıyla "sayac" R1 kaydedicisinde bulunuyor sayac'ın 10 değerinde Locals ve Register görünüm menüsü senkronizasyonun dışına çıkmış gibi görünüyor çünkü R1'in değerini 'A' olarak görüyoruz. Bu biraz açıklama gerektiriyor. Locals görünüm menüsü sayac değişkenini onluk sayı sisteminde gösteriyor. İnsanlar bunu benimsemiş çünkü normal bir insan 10 parmağa sahip. Halbuki, C programcılarının bir süre sonra işlerinde parmaklarının sayısı 16'ya çıkıyor. Örneğin alttaki resim yıllarca C programlamadan sonra parmaklarımın hali. ARM Programlama 38 ARM Programlama Programcılar 16'lık sayı sistemini, çalışmak için 10'luk sayı sistemine göre daha uygun buluyorlar çünkü 16'lık sayı sistemi ksusursuz bir şekilde tüm bilgisayarın temelini oluşturan ikilik sayı sistemini haritalıyor. Yanda gördüğünüz bu karşılaştırma 10'luk, 2'lik (BIN) ve 16'lık sayı sistemleri arasında tek bir 16'lık hane 4 bitlik bir grubu temsil ediyor. Karşılaştırmada, 10'luk (DEC) sistem 9 üzerindeki sayılar için 2 haneye ihtiyaç duyuyor. Her 16'lık hanenin öneünde garip görünümlü '0x' ön eki, C dilindeki hexadecimal (16'lık sayı sistemi) sayıların kodlanmasının bir düzenidir. Alttaki resim bir 32 bitlik sayının 16'lık sistemdeki karşılığının bir örneğinin uygulamasını tablonun üzerinden görebilirsiniz. Bitler 8 li dörlük paketler halinde gruplanmış. Her 8 bit bir Bayt olarak adlandırılıyor, her bayt iki adet 4 bit (yarım bayt - nibble) içeriyor. Biraz önce açıkladığım üzere, yarım bayt'lar direkt olarak 16'lık sistemin hanelerini haritalıyor. Örneğin '1010' yarım bayt'ı hex hanesi olarak A'yı haritalıyor. '0101' yarım bayt'ı 5'i haritalıyor ve benzeri şeyler. Sonunda tüm 32 bit'lik ikilik sayı sistemindeki dizi '0x260F3E5A' değerine denk geliyor. ARM Programlama 39 ARM Programlama Ayrıca ben, hata ayıklama (Debug) menüsünün konuyu daha iyi anlamanıza yardımcı olacağını düşünüyorum çünkü bunların çoğu 16'lık bir sistemi gösteriyor '0x' ön ekini anladığınız üzere. Şimdi, programımızla 'sayac' değişkeninin değerini arttırmayı bırakmadan önce sayılar büyük değer aldığında ne olacağını test etmek için bir bit ile aldatma yapalım. Bunu yapmak hata ayıklayıcısı (Debug) görünüm menüsünde oldukça kolay çünkü manuel olarak herhangi bir değeri değiştirebilirsiniz, sadece üstüne tıklayın ve yeni bir değer girin. Test için '0x7FFFFFFF' değerini yanmamız gerekiyor, bu sayı onluk sistemdeki en büyük sayıdır. Bu sayıyı yazıp Enter tuşuna basınca karşımıza onluk sistemdeki sayı çıkıyor. Şimdi Step-Into butonuna tıklayarak sayac değişkenini bir arttıralım. Çok garip birşey oluyor. R1 kaydedicisi '0x80000000' iken, sizin sayac değişkeninizin değeriniz onluk sistemdeki büyük bir negatif sayıyı gösteriyor. Tekrar, bu biraz açıklama gerektiriyor. ARM Programlama 40 ARM Programlama Sayac adlı değişkenimiz bir tamsayı (integer - int) olarak bildirildi, bu C dilinde işaretli bir sayıdır. Bu Pozitif/Negatif bütün sayıları depolayabilir anlamına geliyor. Sonunda anlaşıldı ki bilgisayar oldukça garip bir yöntemle negatif sayıları ifade ediyor. Yukardaki şekildeki dairesel grafik nasıl çalıştığını açıklıyor. Grafikteki her ok bir artışı simgeliyor. Küçük pozitif sayılar için, herşey tüm bitler dolana kadar beklenildiği gibi çalışıyor ama en önemli bir bit... Bu sayı 32 bitlik pozitif işaretli sayı, en büyük değeri temsil edebilir (0x7FFFFFFF). bu numarayı bir arttırırsanız en önemli bit'i taşımış olursunuz. sayı bu noktada negatif değerli bir sayı olur. Aslına bakarsak, bu 32 bit ile temsil edilen en küçük negatif oluyor (0x80000000). Oradan arttırmaya devam ettiğinizde değer tüm bitleri doldurmanıza kadar daha küçük bir negatif sayı oluyor (0x80000001... 0xFFFFFFF). Bu noktada -1'e ulaşıyoruz (0xFFFFFFF). -1'i arttırdığımız zaman 0 değerine ulaşıyorsunuz ve bu çevrim tekrarlanır. Hata ayıklayıcı (Debug) menüsüne geri dönelim, sayacın değerini -1 olarak değiştirip R1 kaydedicinin içeriğini izleyelim. Bunun için Step-Into butonuna tıklayarak sayac adlı değişkenimizi bir arttırıyoruz ve sayacı tekrar 0 yapıyoruz. Hata ayıklayıcı menüsünü 'X' butonuna basarak kapatın. Sevimsiz hocalar gibi davranıp size bir ödev veriyorum. Sizden işaretsiz tamsayıların (unsigned int) sayımını istiyorum. Bunu yapın. Sayac değişkeninin türünü unsigned int olarak değiştirmeniz gerekiyor. ARM Programlama 41 ARM Programlama Yeniden derleyin ve hata ayıklayıcı menüsünü başlatın. Bu derste biraz önce yaptığımız gibi. Son olarak, size Launchpad Board üzerinde bir kodun nasıl çalıştırılacağını gösterme sözü vermiştim. Bunu yapalım, proje seçeneklerini değiştirmeniz gerekiyor Project > Optionsmenüsünün üzerine tıklayın. Debugger kategorisini seçin aşşağı açılan listeden TI-Stellaris seçeneğini seçin. ARM Programlama 42 ARM Programlama Daha sonra, Download sekmesinin üzerine tıklayın ve Use flash loader(s) ve Verify download seçeneğini seçtikten sonra OK deyip kapatıyoruz. ARM Programlama 43 ARM Programlama Bu noktadan sonra bilgisayar ile Launchpad arasında USB kablo ile bağlantı kurabilirsiniz. Eğer kartı ilk defa takıyorsanız gerekli sürücülerin yüklenmesi için 2 dakika kadar bekleyin. Ledlerin yanmasını sağlayan kart içine üretilirken yüklenmiş bir programdır. Şimdi her zamanki gibi F7 kısayolunu kullanarak Launchpad'in flash hafızasının içine yazdığımız kodları yükleyebiliriz. Programınızın kartın içine kalıcı olarak yüklendiğini söylemek isterim, yani led sönecek. Ama umutsuzluğa kapılmayın, bunu ve bundan fazlasını ilerde yapacağız. ARM Programlama 44 ARM Programlama KONTROL AKIŞI Gömülü sistemler programlama derslerine hoş geldiniz, bu ders kodlarımızda tümüyle kontrol akışının nasıl değiştirileceğini göstereceğim. Önceki ders1 projesinin bir kopyasını alarak başlayalım ve sonra ders1’i ders2 olarak isimlendirelim. Eğer ders1 dosyasına sahip değilseniz bu yazıyı okuyabilirsiniz. ARM Programlama 45 ARM Programlama Yazılım geliştirmenin altın kuralı sadece küçük değişiklikler yaparak çalışan kodları her zaman saklamaktır. Dolayısıyla eğer çalışan bir şeye sahipseniz onu kaydedin. Bir aşamayı karıştırdığınız zaman yaptığınız bu işten çok memnun olacaksınız. Çalışan son sürümün yedeğini almak, hatalı kodu düzeltmeye çalışmaktan daha kolaydır. Ders2 klasörünün içine girin ve IAR araç setini açmak için çalışma alanı dosyasının üstüne çift tıklayın (.eww uzantılı dosya). Eğer IAR araç setine sahip değilseniz bu yazıyı okuyabilirsiniz. IAR araç seti açılınca geçen karşımıza ilk derste oluşturduğumuz C programı çıkıyor. Her C programı aşağıdaki gibi bir main fonksiyonunun yürütülmesiyle başlar. Main içinde, kontrolün yukarıdan aşağıya doğru aktığı çok basit doğrusal bir koda sahipsiniz. ARM Programlama 46 ARM Programlama İşlemcinizin bu kolay kontrol akışını nasıl ele alacağını görmek için hata ayıklayıcıya hızlıca bir göz atalım. Hata ayıklayıcının simülatör olarak ayarlandığından emin olun veDownload and Debug butonunun üzerine tıklayın. Hata ayıklama modunda ne gördüğümüzü hızlı bir şekilde hatırlayalım. Disassembly görünüm menüsü makine komutlarını gösteriyor. Register görünüm menüsü ARM Cortex-M kaydedicilerinin durumunu gösteriyor. Halihazırdaki komutların adreslerini içeren disassembly görünüm menüsünde sizin için vurgulanmış olan, bu dersin sizin için en ilgi çekici şeyi program sayaç (PC) kaydedicisidir. ARM Programlama 47 ARM Programlama Bir seferde bir makine kodu atlatın ve PC kaydedicisinin her adımda nasıl değiştiğini gözlemleyin. ARM Programlama 48 ARM Programlama Sadece R1 kaydedicisini arttırmak için komutları çalıştırdığımıza dikkat edin çünkü PCkaydedicisini arttırmak için herhangi özel bir komut bulunmuyor aksine her komut yan etki olarak PC kaydedicisini arttırıyor. Böylece buradan anlıyorsunuz ki bu basit komutlar kendi içlerinde tümüyle düzgün akış denetimindeki kodlar boyunca donanımla bütünleşiktir. Bu derste, bu donanımla bütünleşik kontrol akışının nasıl değiştirileceğini öğreneceksiniz, böylelikle program döngü yada şarta bağlı olarak kod parçacıklarının üzerinden atlayabilir. Kontrol akışındaki bu gibi değişiklikler yinelemeleri önlemeyi ve çalışma anında karar vermeye olanak sağlayacak. Şimdi bu nedenle hata ayıklayıcıdan çıkalım ve kodu bir döngüyle beraber kullanmak için kodun üzerinde değişiklik yapalım. ARM Programlama 49 ARM Programlama C dilindeki en basit döngü while döngüsüdür. Bunu "while" anahtar kelimesini ekleyerek yapabiliriz (1), bunu takiben parantez içinde bir şart kodlayalım (2), son olarak döngünün gövde kısmını kodlayalım (3). int main(){ int sayac = 0; while(sayac < 5){ sayac++; } return 0; } Bu kod şartı kontrol ederek başlıyor ve eğer bu şart doğruysa, bu döngünün gövde kısmındaki kodları çalıştırıyor ve şartı tekrar kontrol etmek için geri dönüyor. Bu döngüden sadece şart sağlanmadığında çıkış yapılıyor. ARM Programlama 50 ARM Programlama Bu durumda, sayaç değişkeni 21 artışa sahip oluyor böylelikle "(counter < 21)" şartı ile artışın aynı sayıda olması için çalıştıralım. Derleyelim ve bu kodu simülatörde çalıştıralım. İlk komut (int sayac = 0) sayaç değişkenini tutmak için şuan kullanılmakta olan R0kaydedicisine 0 değerini taşıyor. Bir sonraki B komutu çok ilginç bir dallanma komutudur çünkü bu komut PC üzerinde değişiklik yapıyor. ARM Programlama 51 ARM Programlama Bundan dolayı bu birkaç komutun üzerinden atlıyor. Komutun kendisinde hexadecimal sistemde 15 olarak kodlanmış fiilen gördüğümüz CMP komutu R0 kaydedicisini 21 sayısıyla karşılaştırıyor. Application Program Status Register (APSR) için bulunan CMP komutu APSR kaydedicisinin değiştirilmesinde ilginç bir yan etkiye sahip. Özellikle CMP komutu APSR de N (negatif) bitini belirliyor, çünkü negatif sonuçlandırılan karşılaştırma R0-5 bir fark olarak gerçekleştirilmiş. ARM Programlama 52 ARM Programlama BLT komutu dallanma komutunun bir çeşididir. Siz zaten önceden bunu gördünüz fakat bu şarta bağlı bir dallanmadır. Özellikle sadece N biti APSR'ye atandığı zaman BLT komutu PC üzerinde değişiklik yapıyor. Aksi takdirde BLT komutu basit olarak bir sonraki komut için başarısızlığa uğruyor ve program sonlanıyor. Bu noktada şu güzel bir soru : "Dallanma komutu nereye dallanacağını nasıl biliyor ?". Güzel, bu bilgi komutta kodlanmış olan bilgiden dışarı çıkıyor. Aşağıda tüm B komut türlerinin kodlanmasını açıklayan ARM mimarisi kullanma kılavuzundan bir sayfa. Komutumuz "0xD" ile başlıyor bu demek oluyorki bu T1 kodlaması kullanıyor. ARM Programlama 53 ARM Programlama Komuttaki sonraki yarım bayt koşul anlamına geliyor ve "0xB" LT (Less Than) koşulu anlamına geliyor. Sonunda, offset olarak adlandırılan fc baytı PC kaydedicisinin ne kadar değişmesi gerektiğini kodluyor. Şuan offset işaretli bir tamsayıdır ve ilk dersten, işaretli sayıların ikinin tümleyen gösterimini kullandığını hatırlamalısınız. Buna binayen fc baytı -4’ü temsil ediyor. Böylelikle şimdi, PC kaydedicisinin yeni değerini hesaplayabiliriz. 0x7E olarak verilen değerini şuanki PC’nin değeri 0x82 değerinden 4 eksilterek anlıyorsunuz. Hesap makinesini programcı modunda açıp 82 değerinden 4 çıkarırsak 7E değerini buluyoruz. Bu dallanmanın nereye gideceğini beklediğiniz yerdir. Bunu BLT komutunu çalıştırarak doğrulayalım. Hey! Şaşırtıcı!, siz haklısınız. PC geriye dallanıyor, böylelikle kod boyunca giderek doğrulayabileceğiniz bir döngüye sahipsiniz. Endişelenmeyin, komutların iç ytapısını irdeleme üzerine daha fazla zaman harcamayacağım fakat BLT komut açıklamasının öğretici olduğunu düşünüyorum çünkü bu ARM Cortex-M işlemcisinin iç işleyişine anlık bir bakış verdi. Şimdi kontrol akışına geri dönelim. Disassembly kodları while döngüsü için açıkladığım kodların farklı bir kontrol akışı gerçekleştiğini anladığınızı umuyorum. Asıl kodun ilk koşulu kontrol etmesi gerekiyor ve eğer koşul doğru değilse döngü gövdesinin üzerinden dallanır, yani arttırma işlemi gerçekleşmez. Derlenmiş kod koşulsuz bir bölüm ile başlıyor ve döngü gövdesinin yolunu geri döndürüyor ve koşulu test ediyor. Bunun hakkında düşündüğünüz zaman gerçi, bu iki kontrol akışı eşit, üretilen kodun daha hızlı olması bekleniyor çünkü bu sadece döngünün altında bir koşula sahip. Bu örnek iki önemli noktayı gösteriyor. İlk olarak, tek bir C ifadesi “while” gibi, birlikte gruplanmış olması gerekmeyen birden çok makine kodu üretebiliyor. ARM Programlama 54 ARM Programlama İkincisi : derleyici oldukça akıllı ve işlemcinin size göre daha iyi olduğunu biliyor ayrıca düzgün olmayan kontrol akışı işlemcinin sizin kodlarınızı ne kadar hızlı çalıştıracağı üzerine önemli bir etkiye sahip ve bir gömülü sistemler programcısı olarak bunun farkında olmanız gerekiyor. İlk olarak burada bir döngü ek yükü var çünkü sadece döngüyü işlemek için şuan ilaveten test ve dallanma yapıyorsunuz. Fakat bir dakika! burada daha kötü bir şey var. Dallanmalar ek olarak iletişim hattı (Pipeline) gecikmelerinden dolayı işletim gecikmesi ekliyor. Açıklayalım ; Bütün modern işlemciler, ARM Cortex-M'de dahil olmak üzere verimliliği arttırmak için bir iletişim hattı (Pipeline) kullanıyor. İşlemci işlemenin çeşitli aşamalarında çoklu komutlar üzerinde çalışan iletişim hattı bir assembly hattı gibidir. Bu verilen bir zamanda işlenebilen komutların sayısını arttırıyor. Her komut, bağımsız aşamalar dizisi haline bölünür, bellek kullanımı(1), kod çözme(2) ve çalıştırma(3) gibi. Halbuki bu adımların her birinin tamamlanması bir saat çevrimi (clock cycle) alıyor. Komutlar sırayla yürütüldüğü zaman iletişim hattı tam kapasite çalışıyor. Fakat sıralama bir dallanma komutu tarafından bozulduğu zaman iletişim hattı kısmen işleme tabi tutulan komutları atması gerekiyor ve tekrar komutu başlatması gerekiyor. Bu iletişim hattı gecikmelerinin birkaç çevrim için olduğu anlamına geliyor. Az önce gerçekten önemli sadece kritik zamanlı koddan söz ettim, kesme işlemi gibi ve diğer durumların çoğu için önemsiz olması gibi. Ne var ki, bazı şeyleri hızlandırmanız gerektiğinde ne ARM Programlama 55 ARM Programlama yapmanız gerektiğini biliyorsunuz. Bazı döngüleri ya tamamen yada ihtiyacınız olduğu kadarıyla açabilirsiniz. Örneğin while döngüsünü uygun olarak değiştirebilirsiniz : sayaç artış miktarını döngü boyunca tek bir geçiş başına arttırabilirsiniz ve döngüyü ayarlayabilirsiniz. Şimdi. Kodu çalıştırdığımız zaman, test etme ve dallanmanın daha az sıklıkla gerçekleştiğini görebilirsiniz. Oysa, 5 artışla aynı sayıda çalıştırıyorsunuz. Sonunda bu ders için, programın çalışma anında karar vermek için kontrol akışının nasıl kullanılacağını göstermek istiyorum. Farz edelim, örneğin siz sayaç değişkeninin tek sayı olduğu her zaman özel bir şey yapmak istiyorsunuz. Kodumuzu 20 artış haline getirelim ve “if” ifadesini yazarak kodlamaya başlayalım. İf anahtar kelimesini takiben parantez için şartla başlayın, bunu takiben şart doğru olduğunda kodu çalıştırın. Sayacın çift olup olmadığını test etmek için kullanılan koşul ifadesi biraz açıklama gerektiriyor. ARM Programlama 56 ARM Programlama Sayacın her biti arasındaki ve işlemi gerçekleştiren ampresan(&) işareti bitsel AND operatörü için ve ikinci terim içindir. Aşşağıdaki örnekte gördüğünüz gibi, bir testin ikinci terimi sayaç çift olduğu zaman sıfır ve sayaç tek olduğu zaman bir. Ünlem eşit operatörü (!=) eşit değil anlamına geliyor. Sadece şart yanlış olduğunda çalıştırılan bir kodu ayrıca isteğe bağlı olarak if’e ekliyebilirsiniz. C’de iç içe yerleştirilebilen kontrol akış ifadelerinin farkına vardığınızı umuyorum böylelikle while içinde if olan bir döngüye sahipsiniz. ARM Programlama 57 ARM Programlama DEĞİŞKENLER ve İŞARETÇİLER Merhaba. Gömülü sistemler programlama derslerine hoşgeldiniz. Bu derste değişkenler ve işaretçiler hakkında konuşacağız. Önceki ders2 projesinin bir kopyasını alalım ve bunu ders3 olarak isimlendirelim. Eğer ders2 dosyasına sahip değilseniz bu yazıyı okuyabilirsiniz. ders3 klasörünün içine girin ve IAR araç setini açmak için çalışma dosyasının üzerine çift tıklayın (.eww uzantılı dosya). Eğer IAR araç setine sahip değilseniz bu yazıyı okuyabilirsiniz. Ve burada ders2'de oluşturduğumuz C dosyası var. Bunu birazcık temizleyelim ve sayaç değişkeninin nerede bulunduğuna ve nasıl erişildiğine hata ayıklayıcıda hızlıca bir göz atalım. Kod boyunca gittiğiniz ve Locals görünüm menüsünü izlediğiniz üzere, sayaç değişkeninin R0 kaydedicisi içinde bulunduğu ve doğrudan makine kodlarıyla erişildiğini görüyorsunuz. Şimdi, değişken tanımlamasını (int counter = 0;) main fonksiyonunun dışına taşıyalım, yeniden derleyelim ve hata ayıklayıcıya geri dönelim. ARM Programlama 58 ARM Programlama İlginç şekilde, sayaç değişkeni artık Locals görünüm menüsünde değil, Çünkü artık local (yerel değişken) değil. Şimdi sayaç değişkenini görmek için farklı bir görünüm menüsüne ihtiyacımız var. Viewmenüsüne gelin ve Watch > Watch1 görünüm menüsünün üzerine tıklayın. ARM Programlama 59 ARM Programlama Watch görünüm menüsü açıldığı zaman ilk satıra tıklayın ve değişkenin adını yazın. ARM Programlama 60 ARM Programlama Artık sayaç yorumlanabilecek bir durumda. Göründüğü üzere, şimdi 0x2 ile sayaç değişkeninin konumu büyük bir sayıyla başlıyor. Bu adres ARM Cortex-M mikro denetleyicilerindeki Ram Accesed Memory'nin başlangıcıdır. Bu nedenle sayaç değişkeni RAM' de bulunuyor. Eğer gerçekten bu böyle ise değişken penceresinde (Memory) görünür olmalı. doğrudan bellek görünüm Bunu kontrol edelim, bellek görünüm menüsüne 0x2000000 sayısını atayın (1) ve bellek ayarlama görünüm menüsünü 4 byte'lık tamsayılar olarak göstermek için 4x birim şeklinde değiştirin (2). Şimdi, kod boyunca tek aşama gidelim ve hata ayıklayıcı görünüm menüsünü takip edelim. STR komutunun Watch1 görünüm menüsünde sıfırdan bire değişime neden olduğunu fark edin. ARM Programlama 61 ARM Programlama Kod boyunca ilerleyin ve artışı gözlemleyin. Bellekte derleyicinin sayaç değişkenine erişmek için ürettiği makine kodlarını anlamaya çalışalım. Bellekten kaydediciye yükleme yapmak için bulunan ilk ilginç komut LDR'dir. LDR.N komutu, ??main2 etiketinden R0 kaydedicisine bir şeyler yüklüyor. ARM Programlama 62 ARM Programlama Aslına bakarsak bu kaydırabilirsiniz. etikete ne Hey, yüklediğini görmek bu benzer R0'a yüklenen değer sayaç değişkeninin adresidir. LDR.N komutunu çalıştıralım ve R0'ı izleyelim. ARM Programlama 63 için aşağıya gözüküyor. ARM Programlama Bir sonraki LDR komutu R0'ı tekrar yüklüyor, fakat bu sefer sayaç değişkeninin adresi için olan değer R0'ın şu anki tutulan adresinden geliyor. Bir adım gidelim ve R0'ın şuan 3 değerine sahip olduğunu doğrulayalım. ARM Programlama 64 ARM Programlama ADDS komutu R0'ı bir arttırmada asıl işi yapıyor böylelikle R0 4 oluyor. Bir sonraki LDR.N komutu R1'e sayaç değişkeninin adresini yüklüyor. Ve sonunda, STR komutu R1 kaydedicisi tarafından gösterilen belleğe R0 kaydedicisinin değerini yüklüyor. Lütfen bu komutu çalıştırdıktan sonra Watch1 ve bellek görünüm menüsünün nasıl değiştiğini fark edin. ARM Programlama 65 ARM Programlama Bu noktada, ARM işlemcide belleğe erişmenin genel olarak modelini görmeye başladığınızı umuyorum. Sadece özel yükleme komutları ile okunabilen ARM, komut kümesi azaltılmış bilgisayar (RISC) olarak adlandırılan mimarinin bir örneğidir. Bu, tüm veri, manipülasyonlar, kaydedicileri içinde bulundurur ve sonunda değiştirilmiş kaydedici değerleri özel depolama komutu ile belleğe geri depolanabilir. Bu karşılaştırmada karışık komut kümeli bilgisayar (CISC) yapısı kompleks komutların kullanıldığı, bilgilerin bazılarının kaydediciler içinde olmasına gerek olmayan ve yine bellek içinde olabilen. Kişisel bilgisayarların içindeki saygı değer x86 işlemci gibi. Fakat işlemci mimarisi ne olursa olsun, sizin bellek adreslerinin görevlerini beğenerek başladığınızı umuyorum, çünkü bir yerden veri yüklemek yada veriyi tutmak için belleğe her erişim ister istemez bellek adreslerinin bilgisini gerektirir. ARM Programlama 66 ARM Programlama Tüm bunlar ilginç bir soruya yol açıyor. Eğer CPU için bunların bellek adresleri oldukça belirginse, bunlar C dilinde temsil edilebilir mi? Bunun cevabı "Evet"'tir. C programlama dilinde adresler değişkenlerin içinde işaretçiler(pointers) olarak adlandırılan yapıyla tutulabilir. Burada C'de bir işaretçi değişkeninin örneği var. Çoğu C bildirimi gibi, açıklamanın en iyi yolu bir tanesini geriye doğru okumaktır. Böylelikle, tamsayı belirtecinden (int) sonra yazılan yıldız işareti, p_int adlı değişkenin bir işaretçi olduğu anlamına geliyor. Başka bir deyişle tamsayı değişkenlerinin adresini tutabilen p_int, bir değişkendir. Eğer öyle ise, bu durumda p_int'in değerleri arasında tamsayı olarak tanımlanan sayaç değişkeninin adresini tutması gerekir. Doğrusunu söylemek gerekirse, C'de bu kolaylıkla sağlanabilir. Ampresand (&) operatörü sayaç değişkeninin adresini veriyor ve bu adres legal olarak p_int'e atanabilir. ARM Programlama 67 ARM Programlama Sonunda, referanstan ayırma (De - Referencing) işaretçisi olarak adlandırılan bu işaretçiden verilen bir adreste saklanan değeri almak oldukça kullanışlıdır. *p_int bu durumda sayaç değişkeninin değeri olan p_int işaretçisi içinde güncel olarak saklanan adresteki değer anlamına geliyor. Bu eşitlik sayesinde, sayaç değişkeni ile *p_int'i değiştirebilirsiniz ve program eskisi gibi aynı çalışır. yer Bakalım derleyici bu program ile memnun mu? İşler yolunda, böylelikle şimdi hata ayıklayıcıya gidelim ve görünüm menülerini hazırlayalım. Bu sefer sayaç değişkenini görmek için Watch1 görünüm menüsüne ve p_int işaretçisini görmek için Locals görünüm menüsüne ihtiyacımız var. Kod boyunca ilerlemeden önce, Disassembly görünüm menüsüne bir göz atalım ve p_int işaretçisini tanıtmadan önce şimdiki makine kodu ile öncekini karşılaştıralım. ARM Programlama 68 ARM Programlama Gördüğümüz üzere, sayaç değerinin adresini yükleyen LDR komutu en üst bölüme taşındı ve aynı komutun bir kopyası birlikte kaldırıldı. Başka bir deyişle, p_int işaretçi komutu makine kodunun işini kolaylaştırdı ve verimliliği arttırdı. Bu kod ayrıca p_int işaretçisinin R0'da bulunduğunu gösteriyor. Şuan kod boyunca adım adım ilerleyebilir ve sayaç değişkeninin artışını hem Watch1 görünüm menüsünde hem de bellek görünüm menüsünde izleyebilirsiniz, tamamen öncekiyle aynı. Bu sayaç değişkeninin bir adı olduğunu tamamen doğruluyor. Sonunda, eğer döngünün sonuna kadar çalıştırmak isterseniz fakat kod boyunca tek tek ilerlemekten sıkılıyorsanız bir kırılma noktası (Breakpoint) atayabilir ve programı tam hızında çalıştırmak için "Go" butonuna basabilirsiniz. Kırılma noktasını atadıktan sonra final değerinin 20 olduğunu doğrulayabilirsiniz, beklendiği gibi. ARM Programlama 69 ARM Programlama Dersin son istiyorum. adımında, işaretçilerin inanılmaz gücünü göstermek Bu noktada, bu müthiş bir hack olacak (hacklemek teriminin yanlış anlaşılması üzerine bir makalemi yakında yayınlayacağım). Bu size gömülü sistemler programlamasında fiilen kullanılan bir teknik gösterecek. Daha önce söylediğim üzere, bir işaretçi değişkeni, p_int gibi, bir tamsayının adresini tutuyor fakat bu neredeyse hemen hemen her adres olabilir, sadece sayaç değişkeninin adresi değil. Öyleyse, p_int'e uydurma bir adres atamayı deneyelim. Hata ayıklayıcıda gördüğünüz üzere ilk girişimizde sadece bir hex sayısı olarak ifade edilen bir adres kullanmayı deneyebilirsiniz. Fakat F7'ye basıp derlediğimiz zaman derleyici kodu kabul etmeyecektir. ARM Programlama 70 ARM Programlama Bir sonraki denememizde, sayının sonuna U ekleyerek bir işaretsiz sayı kullanmayı deneyebilirsiniz fakat derleyici bu ikisinden de hoşlanmıyor. ARM Programlama 71 ARM Programlama Bu noktada derleyici ile olan görüşmeniz bozuldu. Fakat C dili bir tür kalıplama kullanımı uygulamak için bir mekanizmaya sahiptir. Siz ifadenin önüne türünün adını yerleştirerek bunu gerçekleştirebilirsiniz. Şimdi derleyicinin herhangi bir seçeneği yok, bunu kabul edecektir. ARM Programlama 72 ARM Programlama İşaretçiyi inceleyelim ve kolayca anlaşılabilir tamsayı değerini. Gömülü programcılar bu gaye için ölü sığır (DEAD BEEF (Ölü Sığır). İngilizcede kullanılan bir metafordur. Bu metaforun başarısızlığa uğramış, bellek temizlenmesi gibi anlamları vardır.) gibi gözüküyor. Açıkçası, bu hack'in test edilmesi gerekiyor. Fakat kusurları önceden görmek için bir şeyler alabilir ki ben Stellaris geliştirme kartı üzerinde bu kodu çalıştırmak istiyorum. Böylelikle eğer bu karta sahipseniz bunu bilgisayarınızın USB konektörüne takın. Sonra, Projectmenüsünden Options'u açın ve hata ayıklayıcıyı TI Stellaris ara yüzü olarak ayarlayın. AyrıcaDownload sekmesinin altındaki "Use ARM Programlama 73 ARM Programlama flash loader" seçeneğini seçmeyi unutmayın. Eğer karta sahip değilseniz, bu adımları geçin ve simülatör ile birlikte takip edin. Her iki durumda da, hata ayıklayıcıya erişmek için "Download and Debug" butonuna tıklayın. ARM Programlama 74 ARM Programlama p_int işaretçisinin yeniden atanmasında bir kırılma noktasına sahip olduğunuza emin olun ve ayrıca sayaç değişkenini izleyebilmek için Watch görünüm menüsünü görülür yaptığınızdan emin olun. Kırılma noktasını çalıştırmak için "Go"'ya basın. Disassembly penceresinde bir sefer bir makine komutu gitmek için tıklayın. R0 kaydedicisine uydurma bir adres yükleyen LDR komutunu uygulayın ayrıca bu adres p_intdeğişkeninin Locals görünüm menüsünde ayrıca R0 görünüm menüsünde de göründüğünü doğrulayın. ARM Programlama 75 ARM Programlama R1'e 0xDEADBEEF değerini yükleyen LDR komutunu çalıştırın. Ve sonunda, bellekte p_intadresine 0xDEADBEEF'i depolayan STR komutunu çalıştırın. Bunun etkisi korkutucu türdür. Uydurma adresin bilinçli yanlış ayarlanmasından dolayı, 0xDEADBEEF değeri kısmen sayaç değişkeni üzerinden ve kısmen bellekte sonraki sözcüğün üzerine yazılmış olur. Cortex-M4 bu yanlış ayarlamayı kabul etti fakat Cortex-M0 bununla bir sorun yaşardı. ARM Programlama 76 ARM Programlama Böylelikle, siz şimdi işaretçileri bir etkili mekanizma olarak görüyorsunuz ayrıca eğer bunu dikkatsizce kullansaydım, bu çok tehlikeli olabilirdi. LED YAKIP SÖNDÜRME Merhaba. Gömülü sistemler programlama derslerinin 5. serisine hoş geldiniz. Bu derste Stellaris Launcpad'in üzerindeki bir ledin nasıl yanıp söneceğini test edeceğiz. Bu ders için, Stellaris Launcpad'in Manual) indirmeniz gerekiyor. Kullanım kılavuzunu (User Stellaris Launcpad'ine sahip değilseniz, bu dersi hala takip edebilirsiniz, fakat hata ayıklayıcınızın görünüm menüsü Launcpad'den biraz farklı olacak ve tabii ki ledin yandığını göremeyeceksiniz. Kılavuzun bu sayfası size Launcpad ile sağlanan USB üzerinden kartın nasıl bilgisayara bağlanacağını açıklıyor. ARM Programlama 77 kablosu ARM Programlama Bu sayfa ise kart üzerindeki bileşenlerin mikro denetleyiciye nasıl bağlandığını açıklıyor. ARM Programlama 78 ARM Programlama Farklı birimlerin, Genel Amaçlı Giriş/Çıkış anlamına gelen GPIO'lar ile birbirine bağlı olduğunu görebilirsiniz. ARM Programlama 79 ARM Programlama Kılavuzun sonunda ise kartın şematiğini bulabilirsiniz. Şematiğin ilk sayfasında, LED_R, LED_G, LED_B çıkışları ile ayrı ayrı kontrol edilen transistorlar tarafından güçlendirilen kullanıcı led'inin R, G ve B komponentlerini görebilirsiniz. ARM Programlama 80 ARM Programlama Pin F1, pin F2 ve pin F3 olarak etiketlenmiş mikro denetleyici pinlerine bağlı olan çıkışları tekrar üst düzey olarak görebilirsiniz. F harfi, bu durumda GPIO-F için bulunuyor. Böylelikle şimdi, Led'in nasıl bağlı olduğu hakkında bir fikre sahipsiniz, bu ders için IAR projesini hazırlayalım. Alışıldığı gibi, önceli ders3 dosyasının bir kopyasını alıp ve ders 4 olarak isimlendirip başlayalım. Eğer ders3 dosyasına sahip değilseniz bu yazıyı okuyabilirsiniz. ARM Programlama 81 ARM Programlama ders4 klasörünün içine girin ve IAR araç setini açmak için çalışma dosyasının üstüne çift tıklayın (.eww uzantılı dosya). Eğer IAR araç setine sahip değilseniz bu yazıyı okuyabilirsiniz. Eğer Launcpad kartına sahipseniz, şimdi bilgisayarınıza bağlamalısınız ve hata ayıklayıcı (Debugger) menüsünde TI Stellaris ara yüzünü konfigüre etmelisiniz ve ayrıca Download sekmesinin "Use flash loader" seçeneğinin işaretli olduğuna emin olmalısınız. ARM Programlama 82 ARM Programlama Eğer karta sahip değilseniz, hata ayıklayıcınızı simülatör olarak konfigüre edin ve yazıyı aynen takip edin. Ayrıca kart kullanıyorsanız, lütfen TI Stellaris menüsünün üzerine tıklayın ve "Reset will do system reset" seçeneğini işaretleyin, böylelikle kart her zaman temiz bir reset ile başlayacaktır. ARM Programlama 83 ARM Programlama Sonunda, main.c dosyasını önceki ders3 projesinden çekmeyi önlemek için lütfen projeyi tamamen derleyin. Şimdi, hata ayıklayıcıya gidelim ve işlemcinin çeşitli adresleri nasıl kullandığına hızlıca bir göz atalım. İlk olarak, en düşük adres 0 ile başlıyor, bunu makine kodlarından görebilirsiniz. Bunların hepsi mikro denetleyici içinde depolanmış sizin program kodlarınızın derlenmiş halidir. Bu en düşük adresler 0‟dan başlayıp flash hafızada haritalanıyor anlamına geliyor. Ayrıca siz 0x2 den başlayan adreslerin zaten değişkenler için kullandığını biliyorsunuz, sayaç değişkeni gibi. 0x2 adresi, rastgele erişimli belleğin (RAM) başlangıcının bir çok sıfır ile takip edildiği anlamına geliyor. Doğrusu, hata ayıklayıcının bu adreslerden herhangi bir şey okuyamadığı anlamına gelen RAM‟in başlangıcının hemen önünde tüm adreslerin içinde görebilirsiniz, büyük bir olasılıkla çünkü onlar kullanılmadı. Daha ARM Programlama 84 ARM Programlama iyi incelediğimiz zaman, 0x2000,8000 adresinde RAM‟in sonlandığını görebilirsiniz böylelikle onluk sistemde 32KB olan bir “island” 0x8000 adresi için bir uzatmadır. Bu mikro denetleyicinin 32KB‟lik RAM‟e sahip olduğu anlamına geliyor. Bu noktada adresler hakkında bu kadar biliyorsunuz. Fakat bu dersin hedefi olan led yakıp söndürmek için daha fazla öğrenmeniz gerekiyor. En iyi şekilde, tüm çeşitli “contient” ve “island”‟ların “map”‟ini bilmeniz gerekiyor, RAM island gibi. Sizin mikro denetleyicinizin hafıza haritasını “data sheet” olarak adlandırılan çok daha detaylı doküman açıklıyor. Bu url adresinden sizin Launchpad kartınızın üzerinde olan LM4F mikrodenetleyicisinin spesifik data sheet‟ini indirmenizi son derece tavsiye ediyorum. Fakat data sheet‟lerin çok geniş olma eğiliminde olduğunu hemen size söylemeliyim. Yinede bu nispeten kısa, 1200 küsür sayfalık bir data sheet. Şansımıza bu dökümanlar baştan sona okumaya yönelik tasarlanmış. Doğrusu bir gömülü sistemler mühendisi olmanın büyük bir kısmı datasheetler‟de nasıl yolunuzun bulunacağından oluşuyor böylelikle siz ihtiyacınız olan bilgiyi hızlı bir şekilde bulabilirsiniz. Bu eğitim serisinde bu beceriyi aşama aşama elde edeceğimizi umuyorum. Böylelikle örneğin, mikro denetleyicinizin hafıza haritasını (Memory Map) bulmak için, “memory map” dizesini basitçe datasheet içinde aratın. Evet, bulduk. Tipik bir ARM Cortex-M mikro denetleyicisinin hafıza haritasının bir kısmı. ARM Programlama 85 ARM Programlama Bu çok hoş ve herhangi bir bölüm ya da bellek öbeği olmadan ve basit bir bellek alanı. Eğer diğer mikro denetleyicilerle çalıştıysanız, özellikle eski 8 bitlik olanlarla, umuyorum bir liner 32 bitlik adres boşluğunun sadeliğini çok beğeneceksiniz. Umuyorum 256KB flash belleğe klarşılık gelen 0‟dan 3‟e adreslenen “On-chip Flash” olarak adlandırılan bu hafıza üzerinde ilk “island‟ı” tanıdığınızı umuyorum. ARM Programlama 86 ARM Programlama Statik Ram için bulunan SRAM, burada “Bit-banded on-chip SRAM” olarak adlandırılmış. 0x2 ile başlayıp 0‟la devam eden bilinen RAM island‟ı tanımalısınız. Çevre birimi bölümünde, GPIO portlarını not etmelisiniz. Bu “island” lar ilginç çünkü Led‟inizin kontrolü için “GPIO Port F”i arıyoruz. Aradğımız Port burada ARM Programlama 87 ARM Programlama Başlangıç adresini bir yere kaydedin ve IAR hata ayıklayıcısına geri dönün. Bellek görüntü menüsüne GPIO-F‟in adresini yazın ve ortaya ne çıkacağını izleyin. Oops! Datasheet‟te belirtilen GPIO-F‟in adres aralığı boş görünüyor (Karta sahip olmayanlar için bu geçerli değildir). Eğer bu size de oluyorsa ümitsizliğe kapılmayın. Donanım engellemesi, varsayılan güç koruması tarafından kapatılmasının genel nedenidir. Çip‟in belirli parçalarının clock sinyalinin bloklanması tekniği Clock-Gating ARM Programlama 88 ARM Programlama olarak adlandırılır ve modern modern mikro denetleyicilerde oldukça yaygındır. Böylelikle, datasheet‟e geri dönmeye ihtiyaç duyacağınız anlamına gelen GPIO-F bloğunun nasıl tersine çevrilmesi gerektiğini keşfetmeniz gerekiyor. Dökümanın başına gidin ve “clock gating” dizesini aratın. Evet, burada birşeyler var, Sayfaya gidelim. Evet, GPIO clock geçitleme kontrol kaydedicisi (GPIO Clock control register) burada. ARM Programlama 89 ARM Programlama Kaydedici açıklamasına daha yakından bir göz atalım, çünkü bu datasheet‟lerde yaygın olarak kullanılan çok genel bir format. Bitlerin bir bloğu olarak gösterilen kaydedici her zaman 0‟dan numaralanmış. Gösterilen bitlerin çeşitleri ise şöyle; RO (Read-Only) : Salt okunabilir R/W (Read / Write) : Okunabilir, yazılabilir WO (Write-Only) : Salt yazılabilir anlamına gelir. Bitlerin mantıksal olarak ilişkili olduğu gruplar kaydedici blok resminin altında dökümante edilmiş. ARM Programlama 90 ARM Programlama En önemli bitten başlıyor. Sizin için, en ilgi çekici 5. Bitin açıklaması çünkü bu bit GPIO Port-F için clock‟a olanak sağlıyor. Bu kaydedici tamamen ne aradığınızı doğruluyor. ARM Programlama 91 ARM Programlama Kaydedicinin taban (base) adresini kopyalayın ve ilaveten 0x608 adresini kaydedici adresine, tamamlamak için eklemeniz gerektiğini fark edin. ARM Programlama 92 ARM Programlama Hata ayıklayıcıya geri dönün ve orijinal bellek görünüm menüsünde GPIO Port-F başlangıç adresini eş zamanlı olarak izlerken saat geçitleme kaydedicisinde (clock-gating) bit 5‟i ayarlamak için sembolik bellek (Symbolic Memory) olarak adlandırılan ilave görünüm menüsünü açın. ARM Programlama 93 ARM Programlama Sembolik görünüm menüsüne datasheet‟ten aldığımız saat geçitleme kaydedicisinin temel adresini yapıştırın ve 0x608 offset‟i eklemeyi unutmayın. Şimdi, belirtilen saat geçitleme kaydedicisine gidin ve birinci dersten hatırladığınız 20 hex (ikilik sistemde 5. Bit‟i 1 yapar) sayısnı şekildeki gibi bit 5‟e atayın ve enter‟a basın. ARM Programlama 94 ARM Programlama Hey! GPIO-F‟in donanım engellemesi ortadan kalkıyor. Led yakıp söndürme aşamaya gelmedik. becerisine oldukça yakınsınız fakat daha o Datasheet‟in GPIO bölümünde bazı şeyler daha okumamız gerekiyor çünkü dijital bir çıkış sinyali olarak sırayla süreceğimiz kırmızı, mavi ve yeşil renklerin GPIO-F 1,2 ve 3 bitlerini konfigüre etmeniz gerekiyor. Konfigüre İşlemi 0x40025400 adresi için GPIO-F adres bloğu içinde aşağı kaydırın veya adresi yazıp aratın. İkilik sistemde „1110‟a ve 16‟lık sistemde E değerine denk gelen 1,2 ve 3 bitlerine 1‟i atayın. Çıkış Sinyali Ek olarak , pinler için fonksiyonu dijital çıkış olarak ayarlayın 0x4002551C adresi için GPIO-F bloğu içinde daha aşağıya inin ya da aratın ve tekrar 1,2 ve 3 bitlerine 1‟i atayın. ARM Programlama 95 ARM Programlama Böylelikle, sonunda led‟i kontrol edebiliriz. 0x400253FC konumunda GPIO-F data kaydedicisine gelin ve ilk olarak sadece en düşük yarım bayt‟a hex 2 değerini yazarak 1‟i atayın, tekrar bitlerin her zaman 0‟dan sayıldığını hatırlayın. Heyy! Bu işe etmelisiniz. yaradı, kırmızı led 0 yazarak led‟i söndürmeyi deneyin. ARM Programlama 96 parlıyor. Kendinizi takdir ARM Programlama Harika. Hex 4 yazarak bit 2‟yi ayarlamaya ne dersiniz. Evet, led bu sefer maviye döndü. Gerçekten harika! Şimdi ise hex 8 yazarak bit 3‟ü bir yapalım. Bu sefer led yeşil olarak yandı. 0 yapıp söndürüyoruz. Bu uzun uzun ve ağır işlemler bitiyor çünkü C‟deki tüm kodlama çok kolay olacak. Led‟i özel bellek adreslerine indirgeyerek kontrol etmek için herşey burada ve siz zaten bunun işaretçiler ile nasıl yapıldığını biliyorsunuz. Özellikle size 3. Dersin sonunda gösterdiğim PointerHack‟i kullancaksınız çünkü bu teknik herhangi bir bellek adresine istediğiniz sayıyı yazmanıza izin veriyor. Üstteki kodu silin sadece işaretçi kısmını bırakın. Aslında, sizin işaretçi değişkenlerini ayırmaya hiç ihtiyacınız yok çünkü birazdan göreceğiniz üzere Pointer-Cast diye adlandırılan yöntemi kullanacağız. 3. derste int için işaretçileri kullandınız fakat ARM kaydedicileri unsigned yani işaretsizdir, bu yüzden işaretçi tipini unsigned int olarak değiştirmeniz gerekiyor. ARM Programlama 97 ARM Programlama GPIO-F bloğunu tersine çevirmek için kullandığınız saat geçitleme sistemi kaydedicisinin adresiyle 3. Dersten üretilmiş olan adresi değiştirin. Parantez içinde tüm Pointer-Cast‟i çevreleyin ve tüm bu şeyin bir işaretçiyi unsigned int yapmak için olduğunu fark edin. Eğer böyle ise, yıldız operatörü ile bu işaretçiyi referanstan ayırabileceğiniz anlamına geliyor, aşağıdaki satırda gördüğünüz gibi. Şimdi, işaretçiye yazabilirsiniz. Hata ayıklayıcıdaki deneyimden hatırladığınız üzere bit 5‟i ayarlamanız gerekiyor bu kaydedici içinde hex 20 anlamına geliyor. “U” son ekiyle belirteceğiniz değer unsgined olmalı. Artık kullandığınız p_int işaretçisinden kurtulun ve F7‟ye basarak derleyicinin kodlarınızı sevip sevmediğini kontrol edin. ARM Programlama 98 ARM Programlama Tamam, hex E yazarak 1,2 ve 3 bitlerini atamaya ihtiyaç duyduğunuz GPIO-F Pin-Direction kaydedicisi olan bir sonraki kaydedici ile devam edebilirsiniz. Sonunda, GPIO-E konfigürasyon gerektiriyor ayrıca dijital fonksiyon kaydedicisinde 1,2 ve 3 bitlerini atamak gerekiyor. Buda tamam, GPIO-F Data kaydedicisi kırmızı renkli Led‟i bit 1‟i temizleyerek yada atama yaparak yakıp söndürebilirsiniz. ARM Programlama 99 ARM Programlama Aslında, eğer gerçekten led‟i yakıp söndürmek istiyorsanız, bunu bir defaya mahsus yakıp söndüremezsiniz. Bunu sonsuza kadar yapmanız gerekiyor. Yapalım, led yakıp ve söndürme için kodu bir while döngüsü içine alabilirsiniz. Şartın her zaman doğru olduğu anlamına gelen şartın yerine sabit 1 yazarsanız, sonsuz döngüyü kurmuş olursunuz. Bu kodu derlediğiniz zaman, sonsuz while döngüsünün akış yönüne ulaşamayacağını gönderen bir uyarı alacaksınız. ARM Programlama 10 0 ARM Programlama Launcpad üzerinde bu kodu test edelim. Sayaç geçitleme kaydedicisi ayarlamasının uyandırdığını not edin, beklendiği gibi. Kırmızı ledin parladığını görüyorsunuz. Ve tekrar karanlık oluyor. ARM Programlama 10 1 GPIO-F bloğunu ARM Programlama Sonsuz döngü güzel bir şekilde çalışıyor gibi görünüyor. Eğer herşey doğru çalışıyorsa, "Go" butonuna basarak kodu gerçek hızında çalıştıralım. O da ne! Led sürekli çalışır durumda kalıyor. Break butonuna basarak kodu durduralım ve tekrar tek tek ilerleyelim. Bu sefer herşey iyi, yinede gerçek hızında çalıştırdığımız zaman yanıp sönme duruyor. Problemin nerede olduğunu biliyor musunuz ? Evet, program insan gözünün Led'in hızlı yanıp sönmesini görmek için yetersiz. Program sadece çok çok hızlı çalışıyor. Programı yavaşlatmanız gerekiyor. Bunun için 2. derste öğrendiğiniz sayım while loop döngüsünü kullanabilirsiniz. Bir döngü CPU çevriminin çoğunu boşa harcamaya benzer fakat while döngüsünün şartında bir üst limit atlamasıyla gecikme kontrol edilebilir. Led'i yaktıktan ve söndürdükten sonra her ikisinde de tekrar bir gecikmeye ihtiyaç duyduğunuzu not edin. Tamam, tekrar bir deneme yapalım. Evet, Çalışıyor! ARM Programlama 10 2 ARM Programlama Bu led yakıp söndürme ile ilgi dersi sonlandıralım. Bunun çok fazla önemli olarak görünmemesine rağmen, bu sizin gömülü programlama kariyerinizde çok önemli bir dönüm noktası. Tebrikler! ÖN İŞLEMCİ ve VOLATİLE Gömülü sistemler programlama derslerine hoş geldiniz. Bu derste sizlere C preprocessor (Önişlemci) ve Volatile (Uçucu) anahtar kelimeleri ile “Led yak söndür”ü nasıl geliştireceğinizi göstereceğim. Her zamanki gibi, önceki ders4 projesini kopyalayıp, ders5 olarak yeniden adlandırarak başlayalım. Eğer ders4 dosyasına sahip değilseniz bu yazıyı okuyabilirsiniz. Yeni oluşturulan ders5 dizinine girin ve çalışmaalanı (.eww uzantılı dosya) dosyasına çift tıklayarak IAR’ı başlatalım. Eğer IAR araç setine sahip değilseniz bu yazıyı okuyabilirsiniz. Aşağıda ders4’te oluşturduğumuz programı görüyoruz. ARM Programlama 10 3 ARM Programlama Stellaris Launcpad kartındaki kırmızı led’i yakıp söndürme işini görse de, pek okunabilir durumda değil. Çünkü gizemli numaralar ile dolu olmasının yanında, ne olduğunu anlatan yorumlar da yok. Kodun okunabilirliğini arttırmak amacıyla, Registerlar (Yazmaç) için bu numaralar yerine isimler kullanabilmek çok iyi olurdu. Bunu başarmamızın bir yolu, herhangi bir C kod parçasını makro olarak kullanmanızı sağlayan C preprocessor (Önişlemci) kullanmaktır. Örneğin, yazım yaptığınız ilk Register için bir makro tanımlayalım. “#” karakteri, “define” kelimesi ve ardından makronun adı ile başlayan yeni bir satır oluşturun. ARM Programlama 10 4 ARM Programlama Entegre kılavuzu (Datasheet), GPIO için Run-Mode Clock Gating Control Register’ını çağırmış, bizde makromuzu bu registerin baş harflerinden yola çıkarak RCGCGPIO olarak adlandırdık. Makro adından sonra, makronun yerine geçeceği kodu yapıştırın. Makro tanımlandıktan sonra, bu makroyu orijinal kod parçası yerine kullanabilirsiniz. Derleyici buraya kadarki kodunuzu kabul edip etmediğini anlamak için F7’ye basın. ARM Programlama 100 100 100 ARM Programlama C preprocessor denmesinin nedeni, asıl derleme öncesindeki metin düzenleme işleminin ayrı bir ilk basamağı oluşudur. Preprocessor, “#” işareti ile başlayan tüm satırları kaldırır, böylece derleyici bunları görmez. Örneğin; Herhangi bir makro tanımı yapın ama kod içerisinde kullanılmasın. Hiçbir etkisi olmaz ve kod yine derlenir. Ayrıca, Preprocessor sadece kod içerisinde kullanılan makroları değiştirir. Böylece; derleyici sadece karakter karşılıklarını görür, makro isimlerini görmez. Bu demek olurki; makro C diline tamamen uymasa da olur. ARM Programlama 101 101 101 ARM Programlama Örneğin, “FOO” makrosu, bir işaretçi ifadesinin sadece bir parçasıdır. Ama makronun yerine geçtiği metin, içeriğe uygunsa,derleyici bunu kabul eder, çünkü aslında derleyici aradaki farkı anlamaz. Buradan çıkaracağımız sonuç ise, makrolarınızı nasıl tanımladığınıza ve yerine geçtikleri bölümlerde anlamlarının beklenmedik şekilde değişmesine dikkat etmeniz gerektiğidir. Örneğin, sağlama almak amacıyla, RCGCGPIO gibi makroları parantez içerisine alıp, referanstan ayırmak her zaman iyi bir fikirdir. Başka makrolar kullanarak, makro tanımlamak da mümkündür. Örneğin; entegre kılavuzunda belirtildiği gibi GPIOF_BASE makrosunu tanımlarsınız, bunu diğer makroların tanımında da kullanabilirsiniz. Mesela bacak yönü Register için GPIOF_DIR makrosunu, ana adresten 0x400 uzakta, dijital etkinleştirme için GPIOF_DEN makrosunu, ana adresten 0x51C uzakta ve GPIOF_DATA makrosunu, 0x3FC uzakta tanımlıyorum. ARM Programlama 102 102 102 ARM Programlama Son olarak, kodunuzda yorumlar eklemeniz şiddetle tavsiye edilir. Yorumlar sadece kodunuzu okuyan insanların yararınadır ve derleyici tarafından tamamen görmezden gelinir. C99 standardı 2 yorum tipini destekler; “/*” ve “*/” karakterleri arasında sınırlanan geleneksel C yorum tipi. Ve de “//” ile başlayıp satır sonunda biten C++ yorum tipi. Derleme öncesi bu yorumların hepsi birer boşluk ile değiştirilir yani programa bir fayda sağlamazlar sadece diğer yazılımcılar tarafından okunabilirliğini arttırır. İki yorum tipide makro tanımında kullanılabilir. Şimdi kodumuz hala led’i yakıp söndürebilecek mi,bunu görmek ilginç olacak. Bunu gerçekten görmek için, Stellaris Launcpad kartını kullanacağım. Ancak karta sahip değilseniz, Benzetimci için hata ayıklayıcı ayarını yapın ve takip edin (Debugger for simulator). ARM Programlama 103 103 103 ARM Programlama Harika, Led hala eskisi gibi yanıp sönüyor, tüm değişikliklerimiz çalışıyor. Şimdi GPIOF_DATA makrosunu, derleyicinin nasıl çevirdiğini ve bunun ekstra bir yük katıp katmadığını detaylıca inceliyelim. Sonuçta, kodumuz çalışırken CPU’nun adres öteleme işlemleriyle uğraşacağından endişeleniyor olabilirsiniz. Ama kodunuzu adım adım çalıştırırsanız, LDR.N komutunun, 0x400253FC adresini, toplama yapmadan, R0’a direkt yüklediğini görürsünüz. Başka bir deyişle, kod eskisi kadar verimli, çünkü derleyici mümkün olan her sabiti derleme sürecinde hesaplar ve çalışma sürecinde oluşabilecek gereksiz hesaplamaları önler. ARM Programlama 104 104 104 ARM Programlama Son olarak, led’i yakan asıl komutun hangisi olduğunu görmek çok ilginçtir. Eğer kodu kartınıza yüklediyseniz bunun STR komutu olduğunu görürsünüz. Yani, CPU’nun bakış açısıyla, dış dünya ile konuşmak aslında çok kolaydır ve belli bir adrese belli bir değeri yazmak şeklinde özetlenebilir. Tamamdır, programınız hala çalışıyor ve eskisi kadar verimli. Ancak tüm makroları kendiniz tanımlamak zorundaymışsınız gibi bir izlenim vermek istemiyorum. Aslına bakarsanız, zorunda değilsiniz. Çünkü mikro denetleyici üreticileri, mesela Stellaris kartı örneğindeki Texas Instruments, ARM Programlama 105 105 105 ARM Programlama bu makroları hali hazırda bir dosyada sunmaktadır. Hatta ders5 dizinine bu dosyayı kopyalamıştım. (buradan dosya içeriğini bulabilirsiniz. Tek yapmanız gereken bu içeriği kopyalayıp bir text dosyasına yapıştırmak ve dosya uzantısını '.h' olarak değiştirmek. http://users.ece.utexas.edu/~valvano/Volume1/lm4f120h5qr.h) Bu dosyayı, projenize sağ tıklayıp, Add > Add Files seçeneği ile ekleyebilirsiniz. Dosyanın adı “lm4f120h5qr.h” şeklindedir ve Stellaris Launchpad kartınızdaki işlemci türüne tekabül etmektedir. “.h” dosya uzantısı, üstbilgi (Header) dosyasıdır ve “.c” dosyalarına, örneğin main.c, eklenmesi için tasarlanmıştır. Bu üstbilgi dosyasını açarsanız, demin tanımladığımıza benzeyen bir sürü makro içerdiğini görürsünüz. Ancak, bu üstbilgi dosyasındaki işaretçi (Pointer) tanımları oldukça farklıdır ve biraz açıklamaya ihtiyaç duyuyorlar. ARM Programlama 106 106 106 ARM Programlama Üstbilgi dosyasından bir makro alayım ve karşılaştırmak için main.c dosyasına yapıştırayım. İlk fark işaretçi tipidir. Main.c’deki makrolarımız işaretsiz int (unsigned int) tipini kullanırken, üst bilgi dosyası işaretsiz long kullanmakta. Veri tiplerini başka bir derste anlatacağım ama şimdilik şöyle anlatayım; 32 bitlik bir cihazda, örneğin ARM işlemci, int tipi de long tipi de 32 bit genişliğindedir. Yani unsigned int ve unsigned long aynıdır. Asıl fark Volatile niteleyicisindedir. Derleyiciye işaretçinin işaret ettiği nesnenin aniden aniden aniden değişebileceğini bildirir. Bir nesneyi volatile olarak tanımlarsanız, derleyiciye, programda nesneyi değiştirecek bir ifade olmasada, bu nesnenin değişebileceğini söylersiniz. Örneğin; Launchpad kartında, GPIOF ARM Programlama 107 107 107 ARM Programlama Register’ının 2 bit’i, kullanıcı butonlarına bağlıdır. Kullanıcı bu butonlara basarsa ya da bırakırsa, bu bitler değişir. Bu olay tabii ki bir program komutu yüzünden meydana gelmez. Bu yüzden, GPIOF Register’ı ve hatta çoğu diğer Giriş/Çıkış Registeri, Volatile’dır. Bu önemlidir, çünkü derleyici volatile olmayan nesnelerin değerini CPU Register’ına geçirip, bir süre bu Register ile işlem yapıp, sonunda bu Registerdaki değeri nesneye geri yazacak şekilde bir optimizasyon yapabilir. Volatile nesnelerde, derleyicinin bu tarz bir optimizasyon yapma izni yoktur. Program, bir volatile nesneyle yazma ya da okuma işlemi yapmak isterse, derleyici bunu yapmak zorundadır. Açıkça görülüyorki, volatile niteleyicisi GPIOF gibi Giriş/Çıkış Registerları için kullanışlıdır. Ayrıca normal değişkenlerde de, derleyicinin yapabileceği optimizasyonları önlemek için kullanışlı olabilir. Örneğin counter değişkeni, sadece 2 gecikme döngüsünde kullanılmıştır. Ancak bu döngülere, derleyicinin bakış açısından bakarsanız, işleme herhangi bir katkıları yoktur. Çünkü counter değişkeninin son değerinin ya üzerine yazılmaktadır ya da bu değer atılmaktadır. Bu durumda, derleyicinin gecikme döngülerini yok etmek için optimizasyon yapmaya izni vardır. Optimizasyon seviyesini yükselterek bu durumu rahatlıkla görebilirsiniz. Project > Otions’a tıklayın. C/C++ Complier ve ardından Optimizations sekmesine gelin. “High” optimizasyon seviyesini seçin ve OK’e tıklayın. Tekrar derleyin ve programınızı Launchpad kartında çalıştırın. ARM Programlama 108 108 108 ARM Programlama Gördüğünüz gibi led yanar ve yanık kalır, sonsuza kadar. Kodunuzu adım adım çalıştırırsanız, led’i yakıp söndürme komutlarının yerinde olduğunu görürsünüz. Ancak aradaki gecikme döngüleri gitmiştir. Ancak artık volatile anahtar kelimesini bildiğinize göre, derleyicinizin bu gecikme döngülerini optimizsyonla yok etmesini önleyebilirsiniz. Counter değişkenini volatile yapmalısınız. Bu arada volatile kelimesi, veri tipinden önce, üstbilgi dosyasındaki makro gibi, ya da sonra yazılabilir. Veri tipiden sonra yazmanızı tavisye ederim. Şimdi volatile tanımının sorunu çözüp çözmediğini test edin. Evet, led yanıp sönüyor. Adım adım çalıştırırsanız, gecikme döngüsünü de görebilirsiniz. Şimdi .h header dosyasını ana programımıza dahil ederek kullanalım. Tekrardan, bunun için Preprocessor kullanıyoruz. Bir dosya eklemek için, yeni bir satıra “#include” yazıyorsunuz ve ardından tırnak içinde dosya adınızı ekliyorsunuz. Ve tanımladığımız makroları header dosyasındakilerle değiştirelim. Mikro denetleyici üreticisinin yazdığı header dosyası, Datasheet’teki Register isimlerini kullanmakta. Böylece aradığınız registerler’ı bulmakta sıkıntı çekmezsiniz. Örneğin; GPIO_PORTF_DATA, GPIO_PORTF_DIR ve GPIO_PORTF_DEN. Registerin doğru adı konusunda şüpheniz olursa, adresini kontrol ederek istediğiniz Register olduğunu doğrulayabilirsiniz. Tüm makroları değiştirdikten sonra, kendi tanımlamalarınızı silip kodu tekrar derleyebilirsiniz. ARM Programlama 109 109 109 ARM Programlama Kodumuzu son bir kez test edelim, bakalım led hala yanıp sönecek mi ? Böylelikle C Preprocessor ve Volatile anahtar kelimesi üzerine dersimiz sona eriyor. ARM Programlama 110 110 110 ARM Programlama Artık herhangi bir optimizasyon seviyesinde, doğru olarak çalışan programlar yazabileceksiniz. Tebrikler! BİTSEL OPERATÖRLER Merhaba, gömülü sistemler programlama derslerine hoş geldiniz. Bu derste Launchpad board üzerindeli komposite LED’in tüm renklerinin bitsel operatörler kullanılarak nasıl yakıldığını göstereceğim. Her zaman kigibi bir önceki projeyi kopyalayıp ismini ders6 ile isimlendirerek başlayalım. Eğer eğitimlere yeni başlıyorsanız, önceki proje için bu yazıyı okuyabilirsiniz. Ders6 klasörünün içine girip çalışma sayfamızı açıyoruz (.eww uzantılı dosya). Eğer IAR araç setine sahip değilseniz bu yazıyı okuyabilirsiniz. Bu programı ders5’de oluşturmuştunuz. Program 3 renkli led’in bağlı olduğu genel giriş/çıkış portlarının ayarlanması ile başlıyor. Daha sonra bir sonsuz döngü başlıyor. Önce kırmızı led’i yakıyor, bir süre bekleyip, kırmızı led sönüyor. Ve tekrar döngüye giriyor. Sonuçta kırmızı led yanıp sönüyor. ARM Programlama 111 111 111 ARM Programlama Bu derste, komposit led’in diğer renklerini de kullanmayı öğreneceğiz. Mavi ve yeşil renkler. Sanıyorum, kırmızı led’i yakıp söndürdüğünüz zaman boyunca mavi led’i yanık tutmak istiyeceksiniz. Peki bunu nasıl yapacaksınız ? İlk adım basit. Mavi led’e karşılık gelen GPIOF 2 bit’ini sonsuz döngüden önceye alıp ayarlama yapmak gerekir. ARM Programlama 112 112 112 ARM Programlama Daha sonra, döngünün içinde, kırmızı led yandığında bir sorun olacaktır. Kırmızı led bitini 1 yaptığımız zaman, mavi led’e bağlı bit 2 de dahil olmak üzere diğer bütün bitleri de 0 yapmış olacaksınız. Çünkü bütün led bitleri tek bir register içerisinde bulunur. Burada ihtiyacınız olan şey, bitleri yanlışlıkla diğerini bozmadan teker teker set/reset yapabilmektedir. İşte tam burada C dilinin bitsel operatörleri devreye giriyor. Şimdi C dilinde bitsel operatörleri kod yazarak öğrenmeye çalışalım. Birkaç tane unsigned integer değişken tanımlıyoruz, bunlara temel değerlerini atıyoruz. ARM Programlama 113 113 113 ARM Programlama c adlı değişken, bitsel operatörün sonucunu ifade edecek. Bu bitsel OR. Bu bitsel AND. Bu bitsel XOR. Bu bit tersleyici (1’e komplementini yani tümleyenini alır) ARM Programlama 114 114 114 ARM Programlama Bu sağa kaydırma biti. Ve son olarak sola kaydırma biti. Kodu derleyip çalıştırmadan olarak ayarlayalım. ARM Programlama önce, 115 115 115 optimizasyon seviyesini none ARM Programlama Debugger kısmında Setup sekmesinde simulatör seçeneğini işaretleyin. Böylece launchpad board kullanmanıza gerek kalmayacak. Artık F7’ye basarak programı derleyebiliriz. Download butonu ile Debugger’de kodu adım adım yürütelim. and Debug Buradaki adımda a, b ve c değişkenlerinin temel değerlerinin Locals Windows penceresinde ikilik (binary) olarak göstermemiz gerekiyor. ARM Programlama 116 116 116 ARM Programlama Bu adımda bitsel inceliyoruz. OR ifadesinin c değişkenindeki sonucunu Gördüğünüz gibi, bitsel OR ifadesi a ve b değişkenleri arasında mantıksal OR gibi davranıyor. ARM Programlama 117 117 117 ARM Programlama Eğer okuldan hatırlarsanız 0’ın false yani yanlış, 1’in true yani doğru anlamına geldiğini görebilirsiniz. Doğruluk tablosunu verecek olursak; 1 OR 0 = 1, 0 OR 1 = 1, 1 OR 1 = 1 ve 0 OR 0 = 0 dır. Disassembly penceresinde, 32 bitlik OR işlemlerinin iki operand ile tek bir makine çevriminde ORRS komutu ile, oldukça hızlı ve etkili yürütüldüğünü görürsünüz. Bitsel AND ifadesi a ve b değişkenleri arasında mantıksal AND gibi davranıyor. Doğruluk tablosunu hatırlarsanız; 0 AND 1 = 0, 0 AND 0 = 0, 1 AND 0 = 0 ve 1 AND 1 = 1 olduğunu rahatlıkla kavrayabilirsiniz. ARM Programlama 118 118 118 ARM Programlama Disassembly penceresinde, 32 bitlik AND işlemlerinin iki operand ile tek bir makine çevriminde ANDS komutu ile işlendiğini görürsünüz. Bitsel XOR ifadesi a ve b değişkenleri arasında mantıksal XOR gibi davranıyor. Doğruluk tablosunu hatırlarsanız; 0 XOR 1 = 1, 0 XOR 0 = 0, 1 XOR 0 = 1 ve 1 XOR 1 = 0 olduğunu rahatlıkla kavrayabilirsiniz. Disassembly penceresinde, 32 bitlik XOR işlemlerinin iki operand ile tek bir makine çevriminde EORS komutu ile işlendiğini görürsünüz. ARM Programlama 119 119 119 ARM Programlama Bitsel NOT ifadesi teklidir. Yani sadece tek bir operandı var. Her 1’i 0’a, her 0’ı 1’e çevirir. Disassembly penceresinde MVNS komutu ile işlendiğini, Move Negative ifadesi ile gösterildiğini görürüz. Sağa kaydırma oparetörü her biti bir sağa kaydırır. Sağa kaydırma işlemi bir nevi int türünde bir değişkeni 2 ile bölmektir. Bir hesap makinesi ile kanıtlayalım (DEADBEEF / 2). Locals görünüm menüsündeki değişkenleri onluk sistemde görünmesi için ayarlıyoruz. ARM Programlama 120 120 120 ARM Programlama Disassembly pencerisinde sağa kaydırma işinin LSRS komutu ile sağlandığını görüyoruz. Not : LSRS komutu 0’ları en önemli bit konumuna doğru kaydırır. Sola kaydırma operatörü her biti bir sola kaydırır. Kaydırma işlemi, 2’nin 3. Kuvvetine yani 8 ile çarpılmasına karşılık gelir. Fakat burada olduğu gibi ilk bitin 32 bit’e fazla gelme ihtimaline karşı fazla gelen bit’e 32 bitlik yapı için yer kalmayacaktır. Disassembly penceresinde sağlandığını görüyoruz. ARM Programlama sağa kaydırma 121 121 121 işinin LSLS komutu ile ARM Programlama NOT : LSLS komutu 0’ları en önemsiz bit konumuna doğru kaydırır. Böylece, bitsel operatörlerin unsigned sayılarda nasıl çalıştığını öğrendiniz. Signed sayılar için, sağa kaydırma çalışır. Bir örnek ile inceleyelim. operatörü tamamiyle farklı Signed int türünde, x adında bir değişken tanımlayıp, temel pozitif değerlerini atayalım. Başka bir signed int türünde, y adında bir değişken tanımlayıp, temel negatif değerini atayalım. Sonra, x’in sağa kaydırma işlemini başka bir değerde gösterelim. Son olarak y’nin sağa kaydırma işlemini de aynı değerde gösterelim. Derleyip test edelim. Locals görünüm görüntülüyerlim. menüsünde değişkenleri binary formatında Gördüğünüz gibi, pozitif değer kaydırılma sırasında değerini korudu. Sıfırlar en önemli bite doğru kaydırıldı. ARM Programlama 122 122 122 ARM Programlama 10’luk tabanda z ve x’ikarşılaştırırsanız, sağa kaydırmanın 2’nin 3 üssü yani 8’e karşılık geldiğini rahatlıkla görürsünüz. ( 128 * (23) = 1024 ) Ancak negatifin sağa kaydırılması diğerinden tamamiyle farklıdır. Çünkü şimdi 1’ler en önemli bit’e doğru kaydırıldı. Böylece, signed int türünde sağa kaydırma işleminde, kaydırma işleminden önce bit 0 ise 0’lar en önemli bite doğru kayar. Buna işaret taşması denebilir. Negatif değerin 2’ye tümleyenine karşılık gelir. (Ders 1’e öğrenildi) Bu işlem sağa kaydırma ve 2’nin katlarına bölünürken gerekli bir hal alır. Aslında, değeri decimal’e çevirdiğiniz zaman, z ve y’nin negatif olduğunu ve z’nin hala y’nin 8 ile bölümüne eşit olduğunu görebilirsiniz. ARM Programlama 123 123 123 ARM Programlama Unsigned integer ve signed integer arasındaki bu farklılık, disassembly penceresine baktığınızda çok açıktır. Gördüğünüz gibi derleyici sağa kaydırma işleminde signed sayılar için ASRS komutunu (Aritmetik Sağa Kaydırma), unsigned sayılar için ise LSRS (Mantıksal Sağa Kaydırma) komutunu üretir. Gömülü sistem programcısı olarak, C dilindeki bitsel operatörlerin arasındaki farkları ve ince ayrıntıları çok iyi bilmeniz gerekiyor. Mesela aritmetik kaydırma / mantıksal kaydırma. Bu tür sorular iş görüşmelerinde sıklıkla sorulmaktadır. Ayrıca bitsel operatörler oldukça yararlıdır. Sizin yazacağınız led yanıp söndürme programında size avantajlar sağlayacaktır. Başlangıç için, ledlerin bağlı olduğu GPIO bitlerini tanımlayabiliriz. Kırmızı led bit 1, mavi led bit 2 ve yeşil led bit 3. ARM Programlama 124 124 124 ARM Programlama NOT! : Bu bit kaydırma ifadeleri derleme zamanı sabitleridir, avantajı, kaydırılan bit numarasını derhal görebilirsiniz. Alt seviyeli bitler için bu avantaj pek etkileyici olamayabilir. Ama üst seviyeli bitler için, 18 bitlik gibi, 1 sağa kaydırma ifadesi sonucunda 0x4000 sayısının sonucunu görmek hiç de kolay bir iş olmayacaktır. Bu sabitlerin tanımlanması bize çok zaman kazandıracak. Ayrıca programda bir çok aptalca hatayı önleyecektir. Yani kesinlikle tavsiye ediyorum bunu! Led renkleri için sabitleri tanımladıktan sonra hex sayıları yerine bunları yazabiliriz. Kodun okunurluğunu arttıracaktır. ARM Programlama 125 125 125 ARM Programlama Aslında kodunuz kendini açıklar hale geliyor. Yorum satırı gereksiz bir hal alacaktır. Yorum satırlarını silebilirsiniz. Şimdi, mavi led’i söndürmeden kırmızı renkli led’in yanması durumunda yapılacak GPIOF ayarlamalarını ele alalım. Bunun için data registerındaki o anki değer ile kırmızı renk biti arasında bitsel OR operatörünü kullanabilirsiniz. Bu işe yarar, çünkü bitsel OR, GPIOF içindeki herhangi bir bit ve KIRMIZI_LED orijinal GPIOF bitini saklar. KIRMIZI_LED üzerindeki bütün bitler olması durumunda sonuç 1 olacaktır. 0 ise, değişiklik olmaz, 1 GPIO_PORTF_DATA_R xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx KIRMIZI_LED 00000000000000000000000000000010 --------------------------------------------------------GPIO_P.. | KIRMIZI_LED xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1x NOT! : Bu durum sadece GPIOF registerı üzerinden okuma ve yazma işlemi yaptığınızda geçerli olur. Yani böyle bir durumda okuma/yazma izni olup olmadığını veri sayfalarından kontrol edeceksiniz. C dili, yazım kolaylığı sağlamak amacıyla bazı kısaltmaların yapılmasına izin vermektedir. ARM Programlama 126 126 126 ARM Programlama Eşitliğin sağ veya sol tarafına OR işaretini taşımak mümkündür ve aynı sonucu verir. Aşağıda yazılmış çalışmaktadır. olan bu iki kod tam olarak aynı şekilde Böylece burada GPIOF registerındaki kırmızı led bitini ayarlamak için en kısa kodu görmüş oluyorsunuz. Lütfen bunu C dilinde bir tür kısayol olarak hatırlayın. GPIOF registerındaki kırmızı led bitini sıfırlamak için, kırmızı led bitinin tersi ile bitsel AND operatörünü kullanmalısınız. Bu durum işe yarar, çünkü bitsel AND operatörü ~KIRMIZI_LED’in 1 olduğu yerlerde orijinal durumunu korur. 0 olduğu zaman 0 durumuna değişir. GPIO_PORTF_DATA_R xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ~KIRMIZI_LED 00000000000000000000000000000010 --------------------------------------------------------GPIO_P.. & ~KIRMIZI_LED xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0x Ve tekrar, bu operatör eşitliğini daha kısa bir kod parçacığı ile yazabilirsiniz. Bunun C dilinde bit temizleme kısayolu olduğunu unutmayınız. Şimdi, bu kod kısayollarına biraz daha kritik bir göz atabilirsiniz. Örneğin, mavi led’i yakmak, aynı anda GPIOF registerında bir biti ayarlamaktır. Yani bu bit ayarlama kısayollarıyla kod yazmayı gerektirecektir. Aslında kodumuzdaki ilk üç satırın hepsi, bit registerlarının değerlerini gösteriyor, yani onlarında bit ayarlama kısayolları ile yazılması gerekecektir. Ve son olarak, kodunuzun okunabilirliğini arttırmak için daha fazla makro ekleyebilirsiniz. ARM Programlama 127 127 127 ARM Programlama Derlemeden önce, Project > Options sekmesinden, Optimization kısmını High seviyesine ve Debugger kısmını TI Stellaris’e ayarlıyoruz. ARM Programlama 128 128 128 ARM Programlama Şimdi programı Launchpad board’a yükleyip, çalıştıralım. ARM Programlama 129 129 129 ARM Programlama Gördüğünüz gibi mavi led hep açık, sönük olan kırmızı led, göz kırpar gibi açılıp kapanıyor. Kodlara, kırmızı led’in set/reset ayarı için bölme noktaları koyarsak, görüldüğü gibi, kırmızı led’in set olması Load – Modify – Store işlemlerinden oluşan bir döngüye gerçekleşiyor. Burada bitsel ORR makine komutu veriyi modife etmek için kullanılıyor. Kırmızı led’i resetlemek için, bir diğer Load – Modify – Store döngüsü devreye giriyor. ARM Programlama 130 130 130 ARM Programlama Buradaki ilginçlik, derleyici biti temizlemek için güzel bir kod olan BIC (Bit Clear) komutunu üretiyor. Bu oldukça dikkat çekici çünkü derleyici kodlarınızın bitsel AND ve terleyici ile olan işlemini harfi harfine takip etmeyecektir. Derleyici tüm kodlarda clear işlemini nerede yapma niyetin olduğunu anlamak yerine, daha kullanışlı bir kod üretiyor. Bu örneği iyi öğrenmenizi istiyorum. Çünkü bu örnek, bir biti temizleme gibi kodların kısa şekilde kullanışlarını, derleyicinin sizin aslında ne yapmak istediğini anlamasını sağlamayı, gösteriyor. Sonuç olarak, bu ders bitsel operatörler ile ilgi bilgi verdi. Bitlerde nasıl SET – CLEAR – TOOGLE ve kaydırma işlemlerinin yapılacağını gösterdi. ARM Programlama 131 131 131 ARM Programlama DİZİLER ve İŞARETÇİ ARİTMETİĞİ Gömülü sistemler programlama derslerinin 8. Serisine hoş geldiniz. Bu derste, C dilinde dizileri ve temel işaretçi aritmetiğini tanıtacağım. Bu bilgilerin Stellaris bilgi kaydedicilerine nasıl uygulandığını ve avantajlarını öğreneceksiniz. İlk önce, genel olarak bir önceki derste yaptıklarımızı hatırlayacağız. Eğer eğitim serisine yeni katıldıysanız buradan önceki ders dosyalarına ulaşabilirsiniz. Her zamanki gibi ders6 dosyasını kopyalayıp ders7 olarak adlandırıyoruz. Ders7 dosyasına girin ve workspace dosyasına tıklayarak IAR programını çalıştırın. Eğer bilgisayarınızda IAR yüklü değilse, bu dersi okuyabilirsiniz. ARM Programlama 132 132 132 ARM Programlama Bu proje ders6’da yaptıklarımızdır. ayıklamaya (Debug) geçelim. ARM Programlama 133 133 133 Biraz düzenleyelim ve hata ARM Programlama Bu program genel giriş/çıkış portlarını değiştirmek için “Read – Modify – Write – Sequence” tekniğini kullanır. Böylelikle diğer bitler değişiklikten etkilenmezler. Örneğin, bit 1 kırmızı ledi kontrol eder. Program GPIOF DATA’nın o anki değerini LDR talimatlarıyla okur. Daha sonra, bit 1’i ayarlamak için mantıksal OR işlemi yapar. Son olarak, değişen değeri yazdırır. Read – Modify – Write – Sequence gereklidir, çünkü GPIO bitleri tek bir byte’ın içinde tek adreste kayıtlıdır. Her bitin kendine özgü adreslerle donatıldığını hayal edin. Daha da iyisi, ihtimal dahilindeki GPIO kombinasyonlarının kendine özgü adreslerinin olduğunu düşünün. Tek atomik yazma işlemi, diğer bitleri işe karıştırmadan GPIO bitlerinin değiştirilmesini sağlar. Bu derste, Stellaris donanımında az önceki anlattıklarımı nasıl yapacağınızı göstereceğim. Tipik Read – Modify – Write – Sequence ile bir adet Atomic Write işlemini değiştirebileceksiniz. Ancak, nasıl yaptığımızı göstermeden, neden bu kadar zahmete girdiğimizi anlamak daha iyi olacaktır. En nihayetinde, Read – Modify – Write – Sequence çoğu zaman yeterince hızlıdır. Ancak ilgilendiğimiz konu hız değildir. GPIO bitlerine, gerçekten bağımsız olarak müdahale etmenizi amaçlıyorum. Tüm kodlarınızda, hatta kesmelerde bile. Kesmelere ilerleyen bölümlerde değineceğiz. Kesmeler büyüleyici bir konudur, Özellikle gömülü sistemlerde. Şimdilik kesmelerin donanım tabanlı olduğunu ve program akışınızı beklenmedik bir şekilde değiştireceğini söyleyeyim. Bir kesme aktif olduğunda, işlemcideki özel bir donanım Program Sayacındaki (PC) değeri değiştirir ve işlemci aniden ISR (Interrupt Service Routine) adı verilen kodları çalıştırmaya başlar. ISR kısa kodlardan oluşur. Kesme rutini bittiği zaman işlemci kaldığı yere (Orijinal kodlara) dönerek çalışmasına devam eder. ARM Programlama 134 134 134 ARM Programlama İlginç kısım ise; kesme rutini, ana programın GPIO değerlerini okuduktan sonra ve değiştirilmiş değeri yazmadan önce, Read – Modify – Write işleminin ortasında çalışırsa (1), kesme rutininin GPIO’da yaptığı değişiklikler kaybolur. Çünkü esas kodlarımız kullanımdadır ve GPIO’un eski değerini kullanıyordur (2). Bu, Read – Modify – Write sekansı için doğal bir problemdir. Stellaris GPIO donanım tasarlayıcıları bu hatayı düzeltmek için, Read – Modify – Write sekansı yerine Atomic – Write – Operation kullanmanızı tavsiye ediyor. ARM Programlama 135 135 135 ARM Programlama Nasıl çalıştığına bakalım. GPIO ile CPU (işlemci) arasında pek çok bağlantı vardır. Bunlara bus denir. Her bir bit atanmış veri yoluna ve adrese bağlıdır. Bit, adres yolu 1 olursa değişir. Diğer durumlarda bit etkilenmez. Örneğin, 3 GPIO bitini izole edip led’e bağlamak için adresimizin 0000111000 olması gerekir. En düşük iki adres, A0 ve A1 kullanılmadı çünkü donanım, tüm adreslerin 4’ün katı olması gerektirir. Yazdığınız veri, pinlerin durumuna karar verir. Örneğin, kırmızı ledi yakabilirsiniz, mavi ledi söndürebilirsiniz veya yeşil ledi yakabilirsiniz. Hepsini tek bir komutla yaparız. Umarım her şey anlaşılıyordur. Donanım dizaynında pek çok kayıtçı kendine özgü adres gerektirir. Sadece GPIO kendine özgü adrese sahip değildir. GPIO için 8 kayıtçı gerekir. Her bir bit kombinasyonu için ayrı adres şemada tanımlıdır. 8 adet GPIO bitleri için tüm mümkün bit kombinasyonlarında Stellaris GPIO, 0x40025000 adresi ile başlayan 256 adet 32 bit veri kayıtçısı barındırır. Şimdiye kadar, 1111111100 ikilik ve 0x3FC onaltılık GPIO_PORTF_DATA_R kayıtçılarını kullandık. Bu kayıtçının bitleri izole edilmemiş görünüyor fakat 8 GPIO bitleri veri yollarıyla değiştirilebilir. Bu derste diğer kayıtçıları da kullanacağız. Şimdi, diğer tüm GPIO kayıtçılarına C’de nasıl ulaşacağız? Birinci seçenek, 3. Eğitimde öğrendiğimiz Brute – Force yaklaşımıyla direkt adresleme. Örneğin, led ’den sorumlu 1 biti izole etmek için, manuel olarak adresi hesaplayabiliriz. Datasheet’te bulunan Base adress’ten başlarız, 2 bit sola kaydırarak KIRMIZI_LED’e ekleriz ve kullanılmayan 2 biti atlamış oluruz. İşaretçiye (pointer) ve işaretçiyi kaldırmak için sentezlenmiş adres dökümüne ihtiyacınız vardır. Unutmayın bu adres tek biti izole eder, bu bit için yazdığınız önemlidir. Diğer bitler için yazdığınız önemsizdir. ARM Programlama 136 136 136 ARM Programlama Gösterim için, KIRMIZI_LED’e 1 diğer bitlere de 0 değerini atayalım. F7’ye basalım ve derleyelim. Kit üzerinde bunu test etmek ilginçtir. Gördüğünüz üzere Read – Modify – Write sekansı, STR komutunu R2 adresinde basitleştirir. GPIO base adresi 8 eklenerek offset edilir. Kodlara baktığımızda, kırmızı ledi yanıyor, diğer ledleri değişmiyor olarak görürüz. Kodlar tam istediğimiz gibi kite yansıyor. Kodlar çalışıyor, fakat kodlarımız optimize şekilde değiller. Dizi tanımlaması ile kodları daha optimize hale getirebiliriz. Dizi (Array), aynı tipteki bir grup değişkenin ardışık hafıza bloklarına yerleştirilmesidir. Örneğin 256 adet GPIO veri kayıtçıları. ARM Programlama 137 137 137 ARM Programlama C dilinde, bir değişkene köşeli parantez içerisine bir sayı yazıp ekleyerek dizi tanımı yapabilirsiniz. Örneğin; bu dizi, her biri geçici tam sayı olan 2 sayıcıdan oluşur. Hatta tüm diziyi tanımlamak için bu şekilde bir çözüm uygulayabilirsiniz. Şimdi dizi elemanlarını kullanabilirsiniz. normal birer değişken olarak C dilinde, parantez içerisindeki sayı dizi indisi (Array index) olarak adlandırılır ve dizinin ilk elemanının indisi daima 0’dır. İkincisi 1’dir ve artarak devam eder. F7’ye basıp derleyelim. C’deki diziler işaretçilerle bağlantılıdır. Derleyici dizinin başladığı noktaya işaretçi gibi davranır. İ indisli işaretçiyi almak için, dizi işaretçisine i eklersiniz. Counter[1] yerine *(counter+1) yazılabilir. Bu işaretçi aritmetiğine bir örnektir. ARM Programlama 138 138 138 ARM Programlama Diziler ve işaretçiler arasındaki haberleşme çift yönlüdür çünkü her bir işaretçi dizi olarak görüntülenebilir. Örneğin, standart LM4F header dosyası, işaretçi GPIO_PORTF_DATA_BITS_R’yi tanımlar. Bu işaretçi, 256 GPIO kayıtçılarının tümüne ulaşmak için kullanılabilir. Sadece KIRMIZI_LED bitine ulaşmak için, GPIO_PORTF_BITS_R’a bu şekilde indis atayabilirsiniz. ARM Programlama 139 139 139 ARM Programlama İşaretçi aritmetiği kullanımı ile aynı kapıya çıkıyor. ARM Programlama 140 140 140 ARM Programlama Hata ayıklamaya geçelim ve bu üç seçeneği karşılaştıralım. Gördüğünüz gibi, üç uygulama, R4’e aynı adresi yazıyor. ARM Programlama 141 141 141 ARM Programlama Bu küçük deneyimiz, 3 farklı alternatifin de eşdeğer olduğunu ve aynı makine kodlarını ürettiğini gösteriyor. Kaynak kodumuza dönelim. Size işaretçi aritmetiğinin farklarından bahsedeyim. İlk olarak, adres aritmetiğini ve uzun – işaretsiz işaretçilerde raw adres aritmetiğini göreceğiz. KIRMIZI_LED’i, 4 byte boyutundaki GPIO kayıtçısına almak için, 2 bit sola kaydırmanız gerekir. İkinci olarak, işaretçi aritmetiği kullanırız çünkü GPIO_PORTF_DATA_BITS_R uzun – işaretsiz işaretçidir. İşaretçi aritmetiğinde elemanın büyüklüğünü küçültmek zorunda değilsiniz, bu otomatik olarak yapılır. Dizi indislemesi ve işaretçi aritmetiği denkliği nedeniyle böyle olmalıdır. Üç seçenek arasında dizi indisleme en iyisi gibi görünüyor. Bu seçeneği kullanıp diğerlerini ayrı tutuyorum. ARM Programlama 142 142 142 ARM Programlama Kırmızı ledi söndürmek için dizi indisleme tekniğini kullanalım. Bağlantı şemasından hatırlarsanız, bu teknikte KIRMIZI_LED bit konumuna 0 yazmamız gerekiyor. ARM Programlama 143 143 143 ARM Programlama Sonunda MAVİ_LED bit ayarı için tutarlı bir şekilde dizi indislemeyi kullanıyoruz. ARM Programlama 144 144 144 ARM Programlama Bu, GPIO bitlerini değiştirmek için, tekniğini kullandığımız son programdı. Fast – Interrupt Çalışma kitimizde test edelim. İlk olarak, tam hızda çalışalım ve ledleri gözlemleyelim. ARM Programlama 145 145 145 – Safe ARM Programlama Gördüğünüz gibi program önceden olduğu gibi çalışıyor. Kodları duraklattığımızda KIRMIZI_LED bitini temizlemek için, R0’da bulunan sadece bir adet STR komutu kullandığımız görülüyor. Programımız daha iyi hale geldi, mikrodenetleyicisi daha iyisini yapabilir. ARM Programlama 146 146 146 ancak Stellaris LM4F ARM Programlama Yukarıdaki görüntüye baktığımız zaman mikrodenetleyicinin bir değil de, iki adet çevresel bus’a sahip olduğunu görürüz. Advanced Peripheral Bus (APB) ve Advanced High – Performance Bus (AHB). GPIO portları her ikisine de bağlıdır. APB, ön tanımlı olarak seçilidir ve şimdiye kadar bunu kullandık. Ama APB eskidir ve AHB’den daha yavaştır. Bunun nedeni geriye dönük uyumluluk olması içindir. Dersin kalan kısmında AHB’nin nasıl aktive edileceğini göstereceğim. İlk olarak Datasheet’ten “GPIO High-Performance Bus Control” kısmını bulun. ARM Programlama 147 147 147 ARM Programlama Şekilde görüldüğü edilmektedir. üzere, PORTF (GPIOHBCTL)5. Bit ile kontrol Şimdi LM4F header dosyasını açın ve GPIOHBCTL kayıtçısına bakın. Kayıtçı ismini kopyalayın ve 5. Biti aktive edin. ARM Programlama 148 148 148 ARM Programlama Datasheet’te “APB Apeture” adıyla geçen, APB adres aralığından GPIO adreslerini değiştirmelisiniz. GPIO_PORTF için LM4F header dosyasına göz atın, _AHB ön eki ile başlayan kayıtçılarını bulun. ARM Programlama 149 149 149 ARM Programlama Bu ön eki, programınızda tüm GPIO_PORTF kayıtçılarına ekleyin. ARM Programlama 150 150 150 ARM Programlama En son şekli ile kodları kitimizde test edelim. Bu dersi, C dilinde diziler ve işaretçi aritmetiği ile bitiriyoruz. Stellaris GPIO konusunda uzman oldunuz. Tebrikler! ARM Programlama 151 151 151 ARM Programlama FONKSİYONLAR ve STACK Gömülü sistemler programlama dersinin 9. serisine hoş geldiniz. Bu derste C fonksiyonları ve Stack'i (Yığın) tanıtacağım. Fonksiyonlarla çalışmanın tüm önemli yönlerini sadece bir derste anlatmama imkan yok. Bu yüzden, genel olarak C'deki stack'lerin başka fonksiyonlar çağıran fonksiyonları çağırmayı nasıl mümkün kıldığına odaklanacağım. C ya da C++'ın alt düzeyde nasıl çalışacağına dair sadece tek bir şey öğrenmek istiyorsanız, C stack bu tek bir şey olmalıdır. Çünkü fonksiyonları, kesmeleri, programdan programa geçiş ve RTOS'u (Real Time Operating System – Gerçek Zamanlı İşletim Sistemi) anlamanın temelinde stack vardır. Her zamanki gibi, bir öndeki “ders7” projesini kopyalayıp, “ders8” olarak adlandırarak başlayalım. Derse henüz başlıyorsanız önceki proje için şu yazıyı okuyabilirsiniz. Yeni oluşturulan “ders8” dizinine girin ve çalışma alanı (.eww uzantılı dosya) dosyasına çift tıklayarak IAR ortamında açın. IAR programına sahip değilseniz şuyazıyı okuyun. Çabuk bir şekilde bu programın ne yaptığını hatırlayalım. ARM Programlama 152 152 152 ARM Programlama GPIO hatlarına bağlı olan led'leri kontrol etmek için LM4F mikrokontrolcüsündeki yazmaçları ayarlamakla başlıyor (1). Sonra, mavi led'i yakıyor (2) ve devamında sonsuz bir döngüye giriyor (3). Bu döngü içerisinde, kırmızı led'i yakıyor (4), gecikme döngüsünde bekliyor (5), kırmızı led'i söndürüyor (6), başka bir gecikme döngüsünde tekrar bekliyor (7) ve başa dönüyor. Şu aşamada, programa baktığınızda, gecikme döngüsünün tekrar kullanımının çirkin gözüktüğünü kabul edeceksinizdir. Hatta, DRY prensibine ters düşmektedir. Do Not Repeat Yourself (Kendini tekrar etme). Diğer bir deyişle; programlama yaparken tekrarlamaları yok etmek için uğraşmalısınız. Böylece aynı olması gereken kodlarınız uyumsuz çalışmaz. Bugün, tekrarlardan kaçınmanın ana tekniklerinden birini öğreneceksiniz. Bu teknik ise, aynı kodu harfi harfine tekrardan yazmaktansa, bu kod parçasını fonksiyona çevirip, gerektiği kadar çağırmaktır. C dilinde bir fonksiyon, namı diğer procedure, subroutine, sub-program, bir program içerisinde farklı yerlerden çalıştırılabilen, tekrar kullanılabilir bir kod parçasıdır. Bir kod parçasını, fonksiyona çevirmek için; ona bir isim, argüman listesi ve dönüş değeri tipi atamalısınız. Basit bir başlangıç yapalım: Gecikme fonksiyonumuzun adı “beklet” olsun, değişkeni ve döndürdüğü bir değer olmasın. ARM Programlama 153 153 153 ARM Programlama Dönüş tipi, isim ve argüman listesi birlikte bir fonksiyonun imzasını oluştururlar. İmzadan sonra, parantez içerisinde fonksiyon kodu olur. Fonksiyonunuz tanımlandıktan sonra, kolaylıkla istediğiniz kadar çağırabilirsiniz. Bir fonksiyonu çağırmak için gereken sözdizimi; fonksiyon adı ve parantez içerisindeki argümanlar şeklindedir. ARM Programlama 154 154 154 ARM Programlama Fonksiyon argümansız da olsa, parantezler gereklidir. Bir fonksiyon çağırmak, akışı değiştirerek fonksiyon kodunun başlangıcına atlamak, kodu yürütmek ve çağrıdan bir sonraki komuta geri dönmek demektir. F7'ye basarak bu kodun derlenip derlenmeyeceğine bakalım. ARM Programlama 155 155 155 ARM Programlama Bu kodu kit üzerinde denemeden önce, proje seçeneklerini değiştirelim. Optimizasyonu Low'a ayarlayın. Çünkü yüksek optimizasyon seviyelerinde derleyici o kadar akıllıdır ki, şimdiye kadar yaptıklarınızı tersine çevirip, fonksiyon ek yükünü yok edecektir. Buna bir fonksiyonu “Inlining” (Satıriçileme) denir ve bu durumda böyle Bir şey istemiyoruz. Ayrıca, fonksiyonlarla çalışırken “REQUIRE PROTOTYPES” seçeneğini işaretlemenizi şiddetle tavsiye ederim. Bu sefer F7'ye basarak derlemeye çalıştığınızda, beklet() fonksiyonunun bir prototipe sahip olmadğına dair bir hata alırsınız. Bir fonksiyon prototipi, fonksiyon imzasından sonra, kod bölümü yerine noktalı virgül koyarak oluşturulur. ARM Programlama 156 156 156 ARM Programlama Derleyici, tanımlarından önce her fonksiyonun prototipini görmelidir. Bu arada, beklet() fonksiyonunuz bir argüman listesi istememektedir. Eski C dili standartlarında, bunu “void” yazmak yerine sadece boş bırakrak tanımlayabilirdiniz. Bunu bir deneyelim hemen. Gördüğünüz gibi kod artık derlenmiyor. Bunun nedeni, geriye uyumluluk için, boş argüman listesinin, argümanların tanımlanmadığı ve her şey olabileceği anlamına gelmesidir. “REQUIRE PROTOTYPES” seçeneği ile, derleyici daha katıdır ve böyle zayıf tanımlanmış bir prototipi tanımaz. Fonksiyonun argüman listesini tekrar “void” olarak tanımlayalım. Artık kodu, Stellaris kartında çalıştırmak için hazırsınız. İlk iş, programın led'i hala yakabildiğini test etmek. ARM Programlama 157 157 157 ARM Programlama Ve yanıyor. Kodu durdurduğumuzda, programınızı beklet() fonksiyonu içinde bulacaksınız. Bu olağandır, çünkü program zamanının %99.999'unu gecikme döngüsünü yürütmekle geçiriyor. Test edilecek diğer ilginç şey ise, işlemcinizin gecikme fonksiyonunu aslında nasıl çağırdığı. Bir breakpoint (kesme noktası) koyalım ve programı çalıştıralım. Gördüğünüz gibi, gecikme fonksiyonunuza yapılan çağrı, BL isimli tek bir komuttan ibarettir. ARM Programlama 158 158 158 ARM Programlama Önceki kontrol akışı konulu 2. dersimizden hatırlarsanız, bir dallanma komutu, sadece program sayacı (Program Counter – PC) yazmacının değerini değiştirir. BL komutu ise, fazladan bir etkisi vardır ve sıradaki komutun adresini R14 yazmacına, Link Register'a (Bağlantı yazmacı) saklar. Böylece, LR, fonksiyon tamamlandıktan sonra dönülecek yeri hatırlar. BL'den sonraki komutun adresinin 0xA8'de olduğunu aklımızda tutalım. Bu arada, BL komutunun 4 byte uzunluğunda olduğuna dikkat edin. Diğer komutların çoğu sadece 2 bayt uzunluğundadır. ARM Cortex-M işlemcisinin THUMB2 isimli komut seti, çoğunlukla 2 byte, nadiren de 4 byte komutlardan oluşur. BL komutundan bir adım ileri giderseniz, program sayacının gerçekten de beklet fonksiyonunuzun başlangına atladığını ve LR'nin 0xA8 olarak değiştiğini görürsünüz. Bir dakika! Aslında 0xA9 oldu. Bu oldukça gariptir, çünkü THUMB2 komutları çift sayılı bir adres ile hizalanmalıdır ve 0xA9 tekdir. Bu garipliği birazdan, fonksiyonumuzun nasıl geri döndüğünü gözlemledikten sonra açıklayacağım. Ama bundan önce, fonksiyon kodumuz hakkınada birkaç ilginç şey açıklayacağım. ARM Programlama 159 159 159 ARM Programlama Fonksiyon, SP yazmacını ayarlamakla başlıyor. SP'nin açılımı Stack Pointer (Yığın yazmacı)'dır ve R13 yazmacının diğer adıdır. Sp, C çağrı yığını (call stack) mekanizmasının donanımsal uyarlamasıdır ve bu dersteki öğrenilecek en önemli yazmaçtır. C yığını, basitçe söylersek, RAM'deki tek bir taraftan büyüyen ya da küçülen bir alandır. Bu tarafa yığın tepesi denir ve SP yazmacı bu tepe adresi içerir. Hafızadaki yığını, Memory View'i (Hafıza Görünümü) SP'de saklanan adrese yönlendirerek. Kolayca görebilirsiniz. Yığını görmek için, hafıza görünümünü tek sütuna ayarlamak en iyisidir. ARM Programlama 160 160 160 ARM Programlama ARM işlemcilerde, yığın, en alt adreslere doğru (Hafıza görünümünde yukarıda) büyür ve yüksek adreslere doğru (Hafıza görünümünde aşağıda) küçülür. Diğer işlemcilerde, yığın ters yönde büyüyebilir. C yığınları için iyi bir benzetme, bulaşık yığınlarıdır. Tabakları sadece en tepeden alabilir ya da ekleyebilirsiniz. Yani artık anlayacaksınız ki, SP'den 4 çıkarmak, yığını bu kadar büyütür ve yığının tepesinde “counter” yerel değişkenine yer açar. Sonrasında bu değişken sıfırlanır ve 1 milyon kere arttırılır. Şimdi, fonksiyonun sonuna bir breakpoint koyalım ve nasıl geri döndüğünü görelim. Geri dönmeden önce derleyicinin yapması gereken ilk şey, fonksiyona girilince yığında yapılan değişiklikleri geri almaktır. ARM Programlama 161 161 161 ARM Programlama Bizim örneğimizde; counter değişkeni tarafından tutulan 4 baytlık alanı boşaltmak için yığın küçülür. Yığının şimdiki tepesinde görebileceğiniz gibi, counter'ın son değeri 0xf4240'tır ve ondalık tabanda 1 milyona denk gelmektedir. Yani gecikme döngümüzdeki tekrarlama sayısı. Sonraki komut ise, fonksiyondan geri dönme komutudur. Geri dönme; BX dallanma komutu ile gerçekleşir. BX, branch (dallan) ve exchange (değiştir)'in kısaltmasıdır. Bu komut, Program Counter'ı belirlenmiş bir yazmacın değerine ayarlar. Bu durumda LR. Ancak, LR'deki bitlerin hepsi PC'ye aktarılmaz. Özellikle PC'deki en düşük değerli bit 0'a ayarlanır. Bu mantıklıdır çünkü geri dönüş adresi çift olmalıdır. LR'nin en düşük değerli bit'i adreleme yerine, komut ARM Programlama 162 162 162 ARM Programlama seti değişim bit'i olarak kullanılır. Bu bit 1 ise, işlemci THUMB komut setine, 0 ise ARM komut setine geçiş yapar. Ancak ARM Cortex-M serisi, sadece THUMB2 komut setini destekler ve aslında ARM setine geçemez. Yani, Cortex-M'de, BX komutunun bu davranışı sadece eskiden kalan bir mirastır. Şimdi BX komutunu işletelim ve nereye gideceğimizi görelim. Gerçekten de 0xA8 adresine gidiyoruz, ki bu adres beklet() fonksiyonumuzdan bir sonraki komutun adresidir. Son olarak ne olacağını görmek için, beklet() fonksiyonunun sonuna gidelim ve LR'nin en düşük değerli bitini 0 yapalım. Böylece çekirdek durumu ARM'a çekirdeğinde destekli değildir. ARM Programlama 163 163 163 dönecektir, ama ARM, Cortex-M ARM Programlama Gördüğünüz gibi, BusFault (Veriyolu Hatası) istisnai durumu oluşuyor. İstisnai durumları, kesmeler hakkındaki bir sonraki yazıda anlatacağım. Şimdilik, bir işlemcinin olasılıksız bir durumu nasıl idare ettiğini göstermek istedim. Makine istisnai durum işleyicisine girer. Bu da projenize tanımlayacağınız bir fonksiyon gibidir. İşleyiciden çıkmak için, makineyi yeniden ayarlamanız (Reset) gerekir. Reset, sizi main'in başına getirdiği için, başka bir fonksiyonu çağıran bir fonksiyonu incelemeniz için iyi bir fırsattır. Artık main()'in de aslında, beklet fonksiyonu gibi bir fonksiyon olduğunu fark ettiğinizi umuyorum. beklet()'i çağırmadan önce, main bir yaprak (Leaf), ağacının bir yaprağı gibi, fonksiyondu, çünkü başka fonksiyonu çağırmamıştı. beklet()'e çağrıyı eklediğinizde, main artık bir yaprak fonksiyon olmaktan çıkar ve kendi geri dönüş adresini korumak için özel bir şey yapması gerekir. Hatırlarsanız, geri dönüş adresi, LR yazmacında saklanır. Ama bu yazmaç, BL komutu tarafından yeni bir geri dönme adresi ile doldurulur. BL komutunu işleten her fonksiyon, bir şekilde LR'nin önceki değerini doğru yere dönmek için saklamalıdır. Asıl soru, LR'yi saklamak için en iyi yer neresidir? Koddaki gibi, bu yerin stack olduğunu umarım görürsünüz. PUSH (itme) işlemi, belirli yazmaç listesini stack'e kaydeder ve stack'i büyütmek için otomatikman ve hassasiyetle stack işaretçisini azaltır. Bunu PUSH komutunu çalıştırarak doğrulayalım. ARM Programlama 164 164 164 ARM Programlama Özetlersek, stack'in 2 amaç için kullanıldığını öğrendiniz. İlki; çağrılan fonksiyonların yerel değişkenlerini tutmak ve ikincisi; geri dönme adresini saklamak. Son olarak, fonksiyon argümanlarının ne için olduğunu ve nasıl kullanılacağını göstermek istiyorum. Fonksiyon argümanları fonksiyon çağırıldığında yerel değişkenlerin ilk değerlerini belirtmenizi sağlar. Böylece her çağrı, farklı argüman değerleri ile yapılabilir. Örneğin, beklet() fonksiyonunuzun her yürütmede farklı bir tekrarlama sayısını işletmesini isteyebilirsiniz. Bunun için, fonksiyon içindeki tekrarlamayı sınırlayan “iter” isimli bir integer argüman tanımlayabilirsiniz. Bir fonksiyona argüman tanımladıktan sonra, her çağrılışında başlangıç argüman değerleri verilmelidir. Programı şimdi derlemeyi denerseniz, derleyici, beklet() fonksiyonuna yapılan 2 çağrı için hata verecektir. Çünkü artık prototipe uymuyorlar. ARM Programlama 165 165 165 ARM Programlama Bu da prototipleri kullanmanın güzelliğidir. Çünkü derleyici, her fonksiyon çağrısında doğru sayıda ve tipte argümanı vermeyi unutursanız, sizi uyarabilecektir. Artık argümanları verelim. İlk çağrı için 1 milyon tekrarlama ve ikincisi için sadece 500 bin kullanıyorum. Böylece kırmızı led, sönük olduğu sürenin iki katı yanık kalacaktır. Bu kodu LaunchPad kartından çalıştıralım. İlk olarak, tüm breakpoint'leri kaldıralım ve led'i izlemek için engel olmadan çalıştıralım. ARM Programlama 166 166 166 ARM Programlama Gerçekten de göreceksinizki kalmakta. Sonra, parametrelerin nasıl fonksiyonuna yapılan kırmızı renk, iki iletildiğini çağrılara kat süreyle yanık görmek için breakpoint beklet() koyun. Gördüğünüz gibi, BL komutundan önce, R0'a sabit bir değer atanmış. beklet()'e ikinci çağrıda bu sabit 0x7A120, yani ondalık tabanda 500 bin. İlk çağrıda ise, R0'a yüklenen değer önceden de tanıdığımız 0xF4240, yani ondalık tabanda 1 milyon değeri yükleniyor. ARM Programlama 167 167 167 ARM Programlama Gördüğünüz gibi, iki durumda da, iter argümanı R0 yazmacına taşınmıştır. Şimdi gecikme fonksiyonumuza geçelim ve iter argümanını nasıl kullandığını görelim. Gerçekten de, iter argümanı R0'da ve counter değişkeni stack'in tepesinde bulunuyor, çünkü counter'ın adresi SP yazmacındaki değer ile aynı. Böylelikle fonksiyonlar ve çağrı stack'i üzerine ilk dersimiz sona eriyor. Fonksiyonlar son derece önemlidir, çünkü onları düzgün bir şekilde tasarlayabilirseniz, işin NASIL yapıldığını göz ardı edebilir ve sadece NE yapıldığına odaklanabilirsiniz, ki bu çok daha kolaydır. ARM Programlama 168 168 168 ARM Programlama Modüller, Özyineleme ve AAPCS Gömülü sistemler programlama derslerine hoş geldiniz. Bu derste C’de fonksiyon konusuna devam edeceğim. Bugün fonksiyonların programınızı nasıl ayrı dosyalara böldüğünü öğreneceksiniz. İlk özyineleme fonksiyonunuzu yazacaksınız ve ARM uygulama prosedürü çağırma standardı (AAPCS) hakkında bilgi edineceksiniz. Yine her zamanki gibi önceki ders8 projesini kopyalayarak başlayalım ve ismini ders9 olarak değişelim. Eğer eğitime yeni katılıyorsanız önceki proje için bir önceki yazıyı okuyabilirsiniz. Ders9 dizinine girin ve workspace (.eww uzantılı dosya) dosyasına çift tıklayarak IAR Tool Set’i açın. Eğer IAR Tool Set’iniz ilk eğitime gidebilirsiniz. Şimdiye kadar ne yaptığımızı hızlıca anlatayım. Son derste belirtilen sayıda döngü yinelemesi için beklet fonksiyonumuzu oluşturmuştuk. Aşağıda beklet fonksiyonumuzun ifadesi var. Bu ayrıca prototip olarak bilinir. Bu resimde ise beklet fonksiyonumuzun kendisi var. Beklet fonksiyonu programda iki yerde çağırılmış. ARM Programlama 169 169 169 ARM Programlama Tek fonksiyonun defalarca çağırılabilir olması, tekrarlanan kodların yerine ilk etapta fonksiyonların kullanımı için iyi bir örnek oldu. Fonksiyonlar programda daha çok uyum sağlamaya imkân sağlar. Fonksiyonlar programı parçalara bölmenizi sağlar, böylece beklet gibi rutinlerin, dosyaların hepsini main fonksiyonunda tutmak zorunda kalmazsınız. Bugün yapmak istediğim ilk şey beklet fonksiyonunu kendi dosyasına taşımak. İlk olarak yeni belge araç butonuna tıklayarak yeni bir dosya oluşturalım. Sonra beklet fonksiyonunu kesip bu dosyaya yapıştıralım. ARM Programlama 170 170 170 ARM Programlama Son olarak bu dosyayı proje dosyanıza “beklet.c” olarak kaydedin. Bu noktada dosya proje dizininizdedir ama henüz projenin parçası değildir. Projeye bu dosyayı eklemeniz gerekecek. Projeye sağ tıklayın ve ekleyi (Add) ve açılan menüden beklet.c’i seçin. ARM Programlama 171 171 171 ARM Programlama Bu aşamada F7’ye basarak projeyi derlediğinizde bir hata alırsınız. ARM Programlama 172 172 172 ARM Programlama Bu, beklet fonksiyonu bir prototip olmadan tanımlanmış demektir. Kolayca bu hatayı giderebilirsiniz. Prototipi main.c’den beklet.c’e kopyalayın. ARM Programlama 173 173 173 ARM Programlama Fakat bu çok berbat bir düzeltmedir. Çünkü Eğer birini değiştirip diğerini unutursanız, tekrarlanan prototipler çok kolay farklı hale gelebilir. Bu KENDİNİ TEKRARLAMA! prensibi (DRY) için bir örnektir. Tek prototip tanımlayıp bunu tüm dosyalarınızda içerebilirsiniz. Bu beklet fonksiyonunun prototipi olabilir. Daha önce olduğu gibi yeni bir belge oluşturun ve beklet fonksiyonunun prototipini kesip bu dosyaya yapıştırın. ARM Programlama 174 174 174 ARM Programlama Dosyayı “beklet.h” olarak kaydedin. Şimdi tekrarlanan fonksiyon prototipi yerine, beklet.h dosyasını main.c ve beklet.c ’ye include (içermek) edebilirsiniz. ARM Programlama 175 175 175 ARM Programlama ARM Programlama 176 176 176 ARM Programlama Son dokunuş olarak birden fazla dahil edilmesine karşı koruma için beklet.h dosyasına bir şeyler ekleyebilirsiniz. Otomatik koruma yararlıdır çünkü bir başlık dosyası altında başka başlık dosyası içerebilir. Başlık dosyasının birden fazla dahil edilmesi kolayca böyle bir duruma yol açabilir. Aslında böyle çoğu başlık dosyası çeşitli kütüphanelerde sağlanır. Lm4f.h başlık dosyası da birden çok içermeye karşı bir tür koruma bulundurur. Bu C önişlemcisi ile aşağıdaki gibi sağlanır. ARM Programlama 177 177 177 ARM Programlama Dosyanın üst kısmında #ifndef önişlemci yönergesi, takiben dosyanın süslenmiş ismi (mangling name) ve isim süslemenin belirli kuralları. Burada örnek olarak iki alttan çizgi, ön genişletme (boşluk), son genişletme büyük harfle dosya ismi görüyorsunuz. Burada düşünce bu makronun önceden tanımlanmış olmasıdır. Böylece işlemci ilk seferinde #ifndef’in sonrasına gidecektir. Ancak sonraki satırda #define önişlemcisi ile makro tanımlanır, böylece bu tanımlama artık açıktır. Bu nedenle başlık dosyası tekrar içerilemez. Önişlemci geçilen #ifndef talimatına gitmeyecektir ve dosyanın sonuna yani #endif satırına atlayacaktır. Şimdi beklet.h başlık dosyasında bu tekniği uygulayalım. Lm4f.h dosyasındaki ilk 2 satırı kopyalayıp beklet.h dosyasına kopyalayıp ismini değiştirelim. ARM Programlama 178 178 178 ARM Programlama Dosya sonuna #endif önişlemcisini eklemeyi unutmayın. Hızlıca bir özetle siz C programlama dilinin en güçlü özelliklerinden biri hakkında kullanabileceğiniz bilgiler edindiniz; Ayrı ayrı derlenmiş kaynak dosyalarından programlar oluşturma yeteneği. Kullanıcı fonksiyonlarıyla etkinleştirilen bu yetenek kritik önem taşır çünkü sadece bir dosyayla tamamlanan programlama, hem elverişsiz hem de rahatsız edicidir. Bir yoldan bir program dosyaları düzenlemesi size genel olarak yapısını anlamanıza yardımcı olabilir ve bu yapıyı uygulamak için derleyebilmenizi sağlar. Bu şekilde modüle etme, derleme işlemini hızlandırmak için faydalıdır. Çünkü sadece değişen dosyaların derlenmesi gerekir. Şimdi diğer bazı özelliklerini keşfetmek için hareket edelim. Bir değer döndürme yeteneği gibi. Örnek olarak tam sayı argümanın faktöriyelini hesaplamak için bir fonksiyon düşünün ve bu fonksiyonu yukarıdan aşağı tasarlayalım. Bu tasarım prototip ve kullanma durumu ile başlıyor. Fonksiyon ismi fakt ve unsigned tipinde n adında bir argümanı var. ARM Programlama 179 179 179 ARM Programlama Faktöriyel fonksiyonu unsigned (işaretsiz) değerini döndürür bir kez, iki kez, üç kez, .. her seferinde n kadar. ARM Programlama 180 180 180 ARM Programlama Sonra fonksiyonu yazmadan önce main fonksiyonuna gelelim ve fonksiyon için değerleri atayalım. Faktöriyel fonksiyonu tarafından Bir unsigned volatile değişkeni olan x, döndürülen değerleri saklamak için kullanılacaktır. ARM Programlama 181 181 181 ARM Programlama Derleyici geçici değişkeni korumak için onun uzağında optimize eder. Birazdan burada nasıl bir değer döndüren fonksiyon çağrıldığını göreceksiniz. Fonksiyondan dönen değeri sadece bir değişkene atayabilirsiniz. ARM Programlama 182 182 182 ARM Programlama Bu noktada siz ayrıca fonksiyon argümanlarının izin verilen aralığını düşünmelisiniz. Matematiksel olarak her faktöriyel için düşük değer sıfır tanımlanır. Aslına daha açıkçası bizim fonksiyona göndereceğimiz argüman tipi unsigned olmalıdır çünkü bu fonksiyon unsigned değer döndürür. Ayrıca hemen bir değişkene atanmış olmak yerine, bir ifadede kullanılabilir. ARM Programlama 183 183 183 ARM Programlama Son olarak siz ayrıca bir şey yapmadan sadece dönüş değeri ile fonksiyonu çağırabilirsiniz. Fonksiyonunuz yan etkilere sahipse bu mantıklı olacaktır. Temizleme yaparsanız bu dönen değere dikkat etmeniz gerekmez. Bu satırda dönen değeri boş olarak ayarlayalım. ARM Programlama 184 184 184 ARM Programlama Bu kısımda programınızı derlemeye çalıştığınızda bir hata alırsınız. Fakat bu bir derleyici hatası değil, linker (bağlayıcı) hatasıdır. Bir program oluşturmanın sonraki adımı tüm derleme birimlerinin hepsinin birbirine bağlandığı yerdir. Hata penceresini yukarı kaydırdığımızda bağlantı aşamasında başarısız olduğunu görebilirsiniz, nedeni fakt fonksiyonunun bulunamayışıdır. ARM Programlama 185 185 185 ARM Programlama Çünkü fonksiyonumuzu henüz tanımlamadık. Bunun nedeni açıktır. Fakat ben dikkat çekmek istedim. Bu hata tipi derleyici tarafından saptanamaz. Çünkü derleyici hangi dosya içinde bu işlevin tanımının bulunduğunu bilemez. Şimdi fakt fonksiyonunu tanımlayalım. Prototipi kopyalayalım ve sayfanın en altına yapıştıralım. Fonksiyon kodlarımızı yazmadan önce matematiğinizi tazelemek isteyebilirsiniz. Fonksiyon için aslında iki tanımlama var, yinelemeli (iteratif) ve özyinelemeli (recusif). Bu uygulama için siz özyinelemeli tanımlamayı kullanacaksınız. Burada faktöriyel 0, 1’dir (0! = 1). Ve N sıfırdan büyük olmak şartı ile n faktöriyel, n kere n-1 faktöriyeldir (n > 0 n! = n*(n1)!). Bu dönüşümü C içine alın. Eğer n işaretsiz değişkeni 0’a eşitse.. ARM Programlama 186 186 186 ARM Programlama İşaretsiz 1 değerini döndür. ARM Programlama 187 187 187 ARM Programlama Aksi halde, geriye n*fakt(n-1U) döndür. ARM Programlama 188 188 188 ARM Programlama Gördüğünüz gibi bir fonksiyon dönüş ifadeleri vasıtasıyla sonuç üretir. Şimdi F7’ye bastığınız zaman derleme ve bağlamanın ikiside başarılı olacaktır. Tebrikler! İlk özyinelemeli fonksiyonunuzu yazdınız. Bu fonksiyon kendisini çağırıyor. Tamam, şimdi nihayet gerçekten nasıl çalıştığını görme zamanı. Kodun gerçek donanımda nasıl çalıştığını görmek için, bunu TI boardında çalıştıracağım. Ama eğer TI kitiniz yoksa simülatörü de kullanabilirsiniz. Ctrl+D’ye basarak kodu kite yüklüyoruz. Kod içinde adımlamadan (satır satır çalıştırma) önce yığının üstünü görmek için bir bellek görüntü menüsünü ayarlayacağım. Son dersten hatırlayacağınız gibi bu, yığın işaretçisindedir. SP kayıtçısında. Hafıza görüntüsünde yığın içeriğini izlemenize yardımcı olması amacıyla geçerli yığının üstünü işaretlemek için görüntülerde bir ok kullanacağım. ARM Programlama 189 189 189 ARM Programlama Kod içerisinde adımladığınızda göreceksiniz ki faktöriyel fonksiyonunuzun çağrılması iki talimattan oluşur. Birincisi argüman değeri R0’a hareket eder ve sonra şube kullanımı ve BL talimatı ile fonksiyon çağrılır. ARM Programlama 190 190 190 ARM Programlama Faktöriyel fonksiyonu içinde kodun yaptığı ilk şey register itmek (PUSH); R4 ve LR link registerlarını yığına iter. Neden olduğunu anlamalısınız. Neden LR kaydedilmiştir? Bunun nedeni faktöriyel önceden var olan bir fonksiyon değildir. Bu yüzden LR registerı BL talimatı tarafından kendisini tekrar çağırmak için korunur. ARM Programlama 191 191 191 ARM Programlama Bir sonraki talimat LR register kaydının neden gerekli olduğuna dair size bir ipucu vermelidir. Belki son dersten fonksiyonun ilk argümanının R0 registerına geçtiğini hatırlıyorsunuzdur. Ama biz faktöriyeli R0 içine değer dönmesini sağlamak için kullanmaktayız ve ayrıca argüman olarak iç içe faktöriyel çağrısını kullanıyoruz. Böylece derleyici R4’den R0’a hareket eder. Burada R0 registerı içine dönüş değeri olarak 1 hareket değerinin ettirildiğini görebilirsiniz. ARM Programlama 192 192 192 ARM Programlama Fonksiyonun son komutu çok ilginçtir. Çünkü bu komut bir taşla iki kuş öldürür. Bildiğiniz gibi fonksiyon başlangıcında herhangi bir yığın işlemi yürütülürken fonksiyon tam olarak Main’e dönmeden her şeyin başa döndürülmesi gerekir. Burada fonksiyon başlangıçta itilmiş olan 2 registerı atar. Ama LR orijinal içeriği doğudan geri dönüşe yol gösteren program sayıcı (PC) içine yükler. ARM Programlama 193 193 193 ARM Programlama Lütfen dikkat edin, yığındaki değer PC içine atanır. Aslında hex 49 tek sayısı eklenir. PC geri dönüş adresini bir çift sayı olan hex 48 olarak getirir. ARM Programlama 194 194 194 ARM Programlama Ben adresin en küçük anlamlı bitinin BX komutunda neden özel olarak ele alındığını açıkladım; var olan bir fonksiyondan geri döndürmede kullanıldığı için (beklet gibi). Burada gördüğünüz gibi PC’ye atama talimatı da özel bölgededir ve BX talimatı gibi davranır. Son olarak burada gördüğünüz üzere tekrar fonksiyon R0 değeri döndürür. Yığının üstünde hafızaya alınan; x değişkeninin bulunduğu yerdeki. ARM Programlama 195 195 195 ARM Programlama Faktöriyeli sonraki çağırmanızda siz bu faktöriyelin kendisini çağırdığını görebilirsiniz. ARM Programlama 196 196 196 ARM Programlama Burada en önemli gözlem faktöriyelin sonraki çağırımı önceki çağrı dönmeden ve registerlar yığından çıkmadan önce olmasıdır. Bu yüzden bunlar yığının üst kısmında, birinci çağrı tarafında bulundurulmaktadır. Burada gördüğünüz gibi özyineleme çağrısı dönüşünden sonra döndürülen R0 değeri R4’de saklanan n argümanının orijinal değeri ile çarpılır. Sonuç fonksiyondan döndürülmek için tekrar R0’da saklanır. ARM Programlama 197 197 197 ARM Programlama Main fonksiyonuna dönerseniz, ifadenin nasıl gerçekleştirildiğini; faktöriyel değerini R0’dan aldığı yerde ki gibi görebilirsiniz. 5 faktöriyelinin son çağrısı özyineleme çağrısının 6. Düzeyini gösterir. Lütfen kodları hızlıca adımlarken açıkça fark edilebilen n argümanının eksilmesini ve dönüş adreslerini yığın oluşumu ile izleyin. ARM Programlama 198 198 198 ARM Programlama Bu noktada özyinelemeli çağırma durdurulduğunda bütün iç içe çağrılar bir zaman ekleyerek dönecektir. Tekrar dönüş dizisine adımlıyorum. Lütfen yığının nasıl açıldığını gözlemleyin. ARM Programlama 199 199 199 ARM Programlama Faktöriyel fonksiyonu nihayet tüm yapılanlarla Main’e döndükten sonra sonuç olarak 5 faktöriyelinin gerçek değeri, R0’da hex 78 ve 120 decimal’i ürettiğini görebilirsiniz (ondalık sistem). ARM Programlama 200 200 200 ARM Programlama Bu derste değinmek istediğim son konu fonksiyon çağırma kurallarıdır. Önceden beri tartışılan bir şeyi fark ettiğinizi umuyorum. Fonksiyon çağırıcı ile çağırılan fonksiyon arasında bazı uzlaşmalar olmalıdır. Örnek olarak her iki tarafında fonksiyona sağlanacak dönüş adresinin LR registerda olduğunu anlaşılmalıdır. Ayrıca her iki tarafta ilk argümanın R0’a geçileceğini kabul etmelidir ve dönüş değeri de R0’a dönecektir. Elbette birçok böyle küçük anlaşmalar vardır. Bunların hepsi bir resmi sözleşme olan ARM Uygulama Prosedürü Çağırma Standardı (AAPCS)’de bulunur. Tüm resmi doküman oldukça karmaşıktır ve online arama yaparak AAPCS’yi bulabilirsiniz. Burada ben sadece AAPCS’nin ARM registerlar için yükümlülükleri nasıl atadığından bahsetmek istiyorum. Çünkü ARM işlemcilerde kesme işlemeyi öğrendiğiniz zaman bu sizin için çok faydalı olacak. Saklayıcı R0’dan R3’e ve R12’ye parametre geçirmek ve değer döndürmek için kullanılır ve bir fonksiyon dönüşü olabilir. ARM Programlama 201 201 201 ARM Programlama Diğer taraftan fonksiyon R4’den R11’e 8 registerı rezerv etmelidir. Bu, fonksiyon R4’den R11 arasını kullanamaz anlamına gelmez. Fakat bunu yaparsa, fonksiyon kodu onları yığına kaydetmelidir ve dönüş yapmadan önce onları geri yüklemelidir. Örnek olarak faktöriyel fonksiyonunuzu R4 kullanarak tekrar çağırın ama onu yığına kaydedin. Bu kural fonksiyon çağırıcının ön rezerv edilmiş registerları kullanmasına izin verir, fonksiyon çağırıldığında değerler sağlam kalır. Yine n parametresi değerini çağırın; faktöriyel özyinelemeli çağrısının sağlam kalması için R4’de saklanmıştır. Çünkü bu son çarpımda gereklidir. Faktöriyel hesaplamasını konuşurken, bu derste kullandığım özyineleme uygulaması derin çağırma sırasında gösterim için uygun olur. Yığın büyüme ve küçülmesini izleyebilirsiniz. Ama normalde bir derin çağrı sırasında gömülü programlamadan tam olarak kaçınılmalıdır çünkü bu yığın için çok fazla RAM kullanır. Faktöriyelden çok daha iyi bir uygulama olacaktır; iteratif tip veya daha da iyisinin kullanımı. Modüller, değer döndüren fonksiyonlar, özyineleme fonksiyonları ve ARM uygulama prosedürü çağırma standardı ile tamamladık. Ancak fonksiyonlarla olan işimizi bitirmedik. Sonraki derste siz fonksiyon parametreleriyle ilgili daha fazla bilgi edineceksiniz. İşaret parametreleri içeriği ile kapsamlı ve yerel yığın tabanlı değişkenler hakkında bilgi edineceksiniz. Son olarak yığın taşmasında neler olabileceğini göreceksiniz. Herkese iyi çalışmalar. ARM Programlama 202 202 202