İçindekiler:

AVR Assembler Eğitimi 3: 9 Adım
AVR Assembler Eğitimi 3: 9 Adım

Video: AVR Assembler Eğitimi 3: 9 Adım

Video: AVR Assembler Eğitimi 3: 9 Adım
Video: 9 Atmega32 Assembly Tutorial- AVR ATmega32 ADC Fundamentals and Assembly Programming Part 2 2024, Temmuz
Anonim
AVR Assembler Eğitimi 3
AVR Assembler Eğitimi 3

3 numaralı eğiticiye hoş geldiniz!

Başlamadan önce felsefi bir noktaya değinmek istiyorum. Bu eğitimlerde oluşturduğumuz devreleri ve kodu denemekten korkmayın. Kabloları değiştirin, yeni bileşenler ekleyin, bileşenleri çıkarın, kod satırlarını değiştirin, yeni satırlar ekleyin, satırları silin ve ne olduğunu görün! Bir şeyi kırmak çok zordur ve kırarsanız kimin umurunda? Mikrodenetleyici de dahil olmak üzere kullandığımız hiçbir şey çok pahalı değildir ve işlerin nasıl başarısız olabileceğini görmek her zaman eğiticidir. Bir dahaki sefere ne yapmayacağınızı öğrenmekle kalmayacak, daha da önemlisi neden yapmayacağınızı da öğreneceksiniz. Eğer benim gibi bir şeyseniz, çocukken ve yeni bir oyuncağınız olduğunda, onu neyin doğru yaptığını görmeniz çok uzun sürmedi mi? Bazen oyuncak onarılamaz bir şekilde hasar gördü, ancak önemli değil. Bir çocuğun merakını, kırık oyuncaklara kadar keşfetmesine izin vermek, onu bulaşık makinesi yerine bilim insanı ya da mühendis yapan şeydir.

Bugün çok basit bir devreyi bağlayacağız ve sonra teoriye biraz ağırlık vereceğiz. Bunun için üzgünüm, ama araçlara ihtiyacımız var! Söz veriyorum, daha ciddi bir devre inşa edeceğimiz 4. derste bunu telafi edeceğiz ve sonuç oldukça güzel olacak. Ancak, tüm bu öğreticileri yapmanız gereken yol çok yavaş ve düşünceli bir şekildedir. Sadece ilerler, devreyi kurar, kodu kopyalayıp yapıştırır ve çalıştırırsanız, elbette işe yarayacaktır, ancak hiçbir şey öğrenmeyeceksiniz. Her satırı düşünmeniz gerekir. Duraklat. Deney. İcat etmek. Bunu bu şekilde yaparsanız, 5. dersin sonunda harika şeyler inşa etmeye başlayacaksınız ve daha fazla ders almanıza gerek kalmayacak. Aksi takdirde, öğrenmek ve yaratmak yerine sadece izliyorsunuz.

Her durumda, yeterli felsefe, başlayalım!

Bu eğitimde ihtiyacınız olacak:

  1. prototipleme panonuz
  2. bir LED
  3. bağlantı telleri
  4. 220 ila 330 ohm civarında bir direnç
  5. Talimat Seti Kılavuzu: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Veri Sayfası: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. farklı bir kristal osilatör (isteğe bağlı)

İşte tam eğitim koleksiyonuna bir bağlantı:

Adım 1: Devrenin Oluşturulması

Devreyi Kurmak
Devreyi Kurmak

Bu eğitimdeki devre son derece basittir. Esasen "blink" programını yazacağız, yani ihtiyacımız olan tek şey aşağıdaki.

Bir LED'i PD4'e, ardından 330 ohm'luk bir rezistöre ve ardından Toprak'a bağlayın. yani

PD4 - LED - R(330) - GND

Ve işte bu!

Teori zor slogging olacak olsa da…

Adım 2: Yorumlara ve M328Pdef.inc Dosyasına Neden İhtiyacımız Var?

Sanırım içerme dosyasının ve yorumların neden yararlı olduğunu göstererek başlamalıyız. Bunların hiçbiri aslında gerekli değildir ve onlarsız aynı şekilde kod yazabilir, bir araya getirebilir ve yükleyebilirsiniz ve mükemmel bir şekilde çalışır (include dosyası olmadan montajcıdan bazı şikayetler alabilirsiniz - ancak hata olmaz)

Yorumları ve include dosyasını kaldırmam dışında bugün yazacağımız kod şu:

.device ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16, 0x05 çıkış 0x25, r16 ldi r16, 0x01 sts 0x6e, r16 sei clr r16 çıkış 0x26, r16 sbi 0x0a, 0x04 sbi 0x0b, 0x04 sbi 0x0b, 0x0b, c cbi 0x0b, 0x04 rcall c rjmp bc: clr r17 d: cpi r17, 0x1e brne d ret e: inc r17 cpi r17, 0x3d brne PC+2 clr r17 reti

oldukça basit değil mi? Haha. Bu dosyayı derleyip yüklediyseniz, LED'in saniyede 1 yanıp sönme hızında yanıp sönmesine ve yanıp sönme 1/2 saniye ve yanıp sönmeler arasındaki duraklamanın 1/2 saniye sürmesine neden olacaksınız.

Ancak, bu koda bakmak pek aydınlatıcı değildir. Bunun gibi bir kod yazacak olsaydınız ve onu değiştirmek veya gelecekte başka bir amaçla kullanmak isteseydiniz, zor zamanlar geçirirdiniz.

Öyleyse yorumları koyalım ve bir anlam ifade edebilmemiz için dosyayı geri ekleyelim.

3. Adım: Blink.asm

İşte bugün tartışacağımız kod:

;************************************

; yazan: 1o_o7; tarih:; sürüm: 1.0; dosya şu şekilde kaydedildi: flash.asm; AVR için: atmega328p; saat frekansı: 16MHz (isteğe bağlı);*****************************************; Program işlevi:--------------------; bir LED'i yanıp sönerek saniyeleri sayar;; PD4 - LED - R(330 ohm) - GND;;--------------------------------------.nolist.include "./m328Pdef.inc".list;==============; Bildirimler:.def temp = r16.def taşmaları = r17.org 0x0000; sıfırlama işleyicisinin bellek (PC) konumu rjmp Reset; jmp'nin maliyeti 2 cpu döngüsü ve rjmp'nin maliyeti yalnızca 1'dir; yani 8k bayttan fazla atlamanız gerekmiyorsa; sadece rjmp'ye ihtiyacınız var. Bazı mikrodenetleyiciler bu nedenle yalnızca; rjmp'ye sahip olun ve jmp.org değil 0x0020; Timer0 taşma işleyicisinin bellek konumu rjmp overflow_handler; bir timer0 taşma kesintisi meydana gelirse buraya gidin;============ Sıfırla: ldi temp, 0b00000101 TCCR0B, temp; Saat Seçici Bitleri CS00, CS01, CS02'yi 101'e ayarlayın; bu, Zamanlayıcı Sayacı0, TCNT0'ı FCPU/1024 moduna geçirir; bu nedenle CPU freq/1024 ldi temp, 0b00000001 sts TIMSK0, temp; Timer Overflow Interrupt Enable (TOIE0) bitini ayarlayın; Timer Interrupt Mask Register (TIMSK0) sei'nin; global kesmeleri etkinleştir -- "sbi SREG, I" clr temp out TCNT0, temp; Zamanlayıcı/Sayacı 0 sbi DDRD, 4 olarak başlatın; PD4'ü çıkışa ayarlayın;=======================; Programın ana gövdesi: yanıp sönme: sbi PORTD, 4; PD4 geri çağırma gecikmesinde LED'i açın; gecikme 1/2 saniye olacaktır cbi PORTD, 4; PD4 geri çağırma gecikmesinde LED'i kapatın; gecikme 1/2 saniye rjmp yanıp sönecektir; başlangıç gecikmesine geri dön: clr overflows; taşmaları 0 sn_count olarak ayarlayın: cpi taşmaları, 30; taşma sayısını ve 30 brne sec_count karşılaştırın; şube, ret'e eşit değilse sec_count'a geri döner; 30 taşma meydana geldiyse yanıp sönmeye geri dön overflow_handler: inc overflows; taşma değişkenine 1 ekleyin cpi taşmaları, 61; 61 brne PC+2 ile karşılaştırın; Program Sayacı + 2 (sonraki satırı atla) eşit değilse clr taşmaları; 61 taşma meydana gelirse sayacı sıfır reti'ye sıfırlayın; kesintiden dönüş

Gördüğünüz gibi, yorumlarım şimdi biraz daha kısa. Komut setindeki komutların ne olduğunu öğrendikten sonra bunu yorumlarda açıklamamıza gerek yok. Sadece programın bakış açısından neler olup bittiğini açıklamamız gerekiyor.

Tüm bunların ne yaptığını parça parça tartışacağız, ama önce küresel bir bakış açısı elde etmeye çalışalım. Programın ana gövdesi aşağıdaki gibi çalışır.

İlk önce PORTD'nin bit 4'ünü "sbi PORTD, 4" ile ayarladık, bu PD4'e 1 gönderir ve bu pin üzerindeki voltajı 5V'a koyar. Bu, LED'i açacaktır. Daha sonra saniyede 1/2 sayan "gecikme" alt programına atlıyoruz (bunu nasıl yaptığını daha sonra açıklayacağız). Ardından, PD4'ü 0V'a ayarlayan ve dolayısıyla LED'i kapatan PORTD'de yanıp sönmeye ve bit 4'ü temizlemeye dönüyoruz. Daha sonra 1/2 saniye daha geciktiririz ve ardından "rjmp yanıp sönme" ile tekrar yanıp sönmenin başlangıcına geri döneriz.

Bu kodu çalıştırmalı ve yapması gerekeni yaptığını görmelisiniz.

İşte buyur! Bu kodun fiziksel olarak yaptığı tek şey bu. Mikrodenetleyicinin ne yaptığının iç mekaniği biraz daha ilgili ve bu yüzden bu öğreticiyi yapıyoruz. Öyleyse sırayla her bölümü tartışalım.

4. Adım:.org Assembler Yönergeleri

.nolist,.list,.include ve.def assembler yönergelerinin ne yaptığını önceki eğitimlerimizden zaten biliyoruz, bu yüzden önce bundan sonra gelen 4 kod satırına bir göz atalım:

.org 0x0000

jmp Sıfırla.org 0x0020 jmp overflow_handler

.org ifadesi, montajcıya bir sonraki ifadeyi "Program Belleğinde" nereye koyacağını söyler. Programınız yürütülürken, "Program Sayacı" (PC olarak kısaltılır) yürütülmekte olan geçerli satırın adresini içerir. Bu durumda, PC 0x0000'deyken, o bellek konumunda bulunan "jmp Reset" komutunu görecektir. jmp Reset'i bu konuma koymak istememizin nedeni, program başladığında veya çip sıfırlandığında, PC'nin bu noktada kodu yürütmeye başlamasıdır. Yani gördüğümüz gibi, hemen "Sıfırla" etiketli bölüme "atla" dedik. Bunu neden yaptık? Bu, yukarıdaki son iki satırın atlandığı anlamına gelir! Niye ya?

İşte bu noktada işler ilginçleşiyor. Şimdi, bu öğreticinin ilk sayfasında işaret ettiğim tam ATmega328p veri sayfasıyla bir pdf görüntüleyici açmanız gerekecek (bu nedenle, "ihtiyacınız olacak" bölümündeki 4. maddedir). Ekranınız çok küçükse veya çok fazla açık pencereniz varsa (benim durumumda olduğu gibi) benim yaptığımı yapabilir ve bir Ereader'a veya Android telefonunuza koyabilirsiniz. Montaj kodu yazmayı planlıyorsanız, her zaman kullanacaksınız. Harika olan şey, tüm mikrodenetleyicilerin çok benzer şekillerde organize edilmiş olmasıdır ve bu nedenle veri sayfalarını okumaya ve onlardan kodlamaya alıştığınızda, farklı bir mikrodenetleyici için aynısını yapmanın neredeyse önemsiz olduğunu göreceksiniz. Yani aslında sadece atmega328p'yi değil, tüm mikrodenetleyicileri bir anlamda kullanmayı öğreniyoruz.

Tamam, veri sayfasında 18. sayfaya dönün ve Şekil 8-2'ye bir göz atın.

Mikrodenetleyicideki Program Belleği bu şekilde kurulur. 0x0000 adresi ile başladığını ve iki bölüme ayrıldığını görebilirsiniz; bir uygulama flaş bölümü ve bir önyükleme flaş bölümü. Sayfa 277 tablo 27-14'e kısaca başvurursanız, uygulama flaş bölümünün 0x0000 ile 0x37FF arasındaki konumları aldığını ve önyükleme flaş bölümünün 0x3800 ile 0x3FFF arasındaki konumları aldığını göreceksiniz.

Alıştırma 1: Program belleğinde kaç konum var? yani 3FFF'yi ondalık sayıya çevir ve 0'dan saymaya başladığımız için 1 ekle. Her bellek konumu 16 bit (veya 2 bayt) genişliğinde olduğundan, toplam bellek bayt sayısı nedir? Şimdi, bir kilobaytta 2^10 = 1024 bayt olduğunu hatırlayarak bunu kilobayta dönüştürün. Önyükleme flaş bölümü 0x3800'den 0x37FF'ye gidiyor, bu kaç kilobayt? Programımızı depolamak için kullanmamız için kaç kilobayt bellek kaldı? Başka bir deyişle, programımız ne kadar büyük olabilir? Son olarak, kaç satır kod alabiliriz?

Pekala, artık flash program belleğinin organizasyonu hakkında her şeyi bildiğimize göre,.org ifadeleri tartışmamıza devam edelim. İlk bellek konumu 0x0000'in Reset olarak etiketlediğimiz bölüme atlama talimatımızı içerdiğini görüyoruz. Şimdi ".org 0x0020" ifadesinin ne yaptığını görüyoruz. Bir sonraki satırdaki talimatın 0x0020 bellek konumuna yerleştirilmesini istediğimizi söylüyor. Oraya yerleştirdiğimiz talimat, kodumuzda "overflow_handler" olarak etiketlediğimiz bir bölüme bir atlamadır… şimdi neden bu atlamanın 0x0020 bellek konumuna yerleştirilmesini isteyelim ki? Bunu öğrenmek için veri sayfasında 65. sayfaya dönüyoruz ve Tablo 12-6'ya bakıyoruz.

Tablo 12-6, bir "Sıfırlama ve Kesinti Vektörleri" tablosudur ve bir "kesinti" aldığında PC'nin tam olarak nereye gideceğini gösterir. Örneğin, 1 numaralı Vektör'e bakarsanız, kesmenin "kaynağı", "Harici Pin, Açılış Sıfırlama, Kahverengi Çıkış Sıfırlama ve Bekçi sistemi sıfırlama" olarak tanımlanan "SIFIRLAMA"dır. bu şeyler mikrodenetleyicimizin başına gelirse, bilgisayar programımızı 0x0000 program hafıza konumunda yürütmeye başlayacaktır. O zaman.org yönergemiz ne olacak? Peki, 0x0020 bellek konumuna bir komut yerleştirdik ve tabloya bakarsanız, bir Zamanlayıcı/Sayaç0 taşması olursa (TIMER0 OVF'den geliyor) 0x0020 konumunda ne varsa onu çalıştıracağını göreceksiniz. Bu ne zaman olursa, PC "overflow_handler" olarak etiketlediğimiz noktaya atlayacaktır. Güzel değil mi? Bunu neden yaptığımızı birazdan anlayacaksınız, ama önce öğreticinin bu adımını bir kenara bırakalım.

Eğer kodumuzu daha düzenli ve düzenli hale getirmek istiyorsak, şu anda tartıştığımız 4 satırı gerçekten aşağıdaki ile değiştirmeliyiz (bkz. sayfa 66):

.org 0x0000

rjmp Sıfırla; PC = 0x0000 reti; PC = 0x0002 reti; PC = 0x0004 reti; PC = 0x0006 reti; PC = 0x0008 reti; PC = 0x000A … reti; PC = 0x001E jmp overflow_handler: PC = 0x0020 reti: PC = 0x0022 … reti; PC = 0x0030 reti; bilgisayar = 0x0032

Böylece, belirli bir kesinti meydana gelirse, sadece "reti" olur, bu da "kesmeden geri dön" anlamına gelir ve başka hiçbir şey olmaz. Ancak bu çeşitli kesintileri hiçbir zaman "Etkinleştirmezsek", kullanılmazlar ve bu noktalara program kodunu koyabiliriz. Mevcut "blink.asm" programımızda sadece timer0 taşma kesmesini (ve tabii ki her zaman etkin olan sıfırlama kesmesini) etkinleştireceğiz ve bu yüzden diğerleriyle uğraşmayacağız.

O zaman timer0 taşma kesintisini nasıl "etkinleştiririz"? … bu eğitimdeki bir sonraki adımımızın konusu budur.

Adım 5: Zamanlayıcı/Sayaç 0

Zamanlayıcı/Sayaç 0
Zamanlayıcı/Sayaç 0

Yukarıdaki resme bir göz atın. Bu, bazı dış etkiler programımızın akışını "kestiğinde" "PC"nin karar verme sürecidir. Dışarıdan bir kesme olduğuna dair bir sinyal aldığında yaptığı ilk şey, o tür kesme için "interrupt enable" bitini ayarlayıp ayarlamadığımızı kontrol etmektir. Yapmadıysak, bir sonraki kod satırımızı yürütmeye devam eder. Bu özel kesme etkinleştirme bitini ayarladıysak (o bit konumunda 0 yerine 1 olacak şekilde) "global kesmeleri" etkinleştirip etkinleştirmediğimizi kontrol edecek, değilse tekrar bir sonraki satıra gidecek kodlayın ve devam edin. Global kesmeleri de etkinleştirmişsek, o zaman bu tür kesmenin Program Belleği konumuna (Tablo 12-6'da gösterildiği gibi) gidecek ve oraya yerleştirdiğimiz komutu yürütecektir. Şimdi tüm bunları kodumuzda nasıl uyguladığımıza bakalım.

Kodumuzun Sıfırla etiketli bölümü aşağıdaki iki satırla başlar:

Sıfırla:

ldi sıcaklık, 0b00000101 çıkış TCCR0B, sıcaklık

Bildiğimiz gibi, bu, 0b00000101 olan hemen ardından gelen sayıyı temp'e (yani R16) yükler. Daha sonra bu numarayı "out" komutunu kullanarak TCCR0B adlı register'a yazar. Bu kayıt nedir? Pekala, veri sayfasının 614. sayfasına gidelim. Bu, tüm kayıtları özetleyen bir tablonun ortasındadır. 0x25 adresinde TCCR0B'yi bulacaksınız. (Artık kodun yorumlanmamış versiyonumda "out 0x25, r16" satırının nereden geldiğini biliyorsunuz). Yukarıdaki kod parçasından 0. biti ve 2. biti ayarladığımızı ve gerisini temizlediğimizi görüyoruz. Tabloya bakarak bunun CS00 ve CS02'yi ayarlamış olduğumuz anlamına geldiğini görebilirsiniz. Şimdi veri sayfasındaki "PWM ile 8-bit Zamanlayıcı/Sayaç0" adlı bölüme gidelim. Özellikle, o bölümün 107. sayfasına gidin. Az önce register özet tablosunda gördüğümüz "Timer/Counter Control Register B" (TCCR0B) registerının aynı açıklamasını göreceksiniz (böylece direk buraya gelebilirdik ama özet tabloların nasıl kullanılacağını görmenizi istedim) ileride başvurmak için). Veri sayfası, o kayıttaki her bir bitin ve ne yaptıklarının bir tanımını vermeye devam eder. Şimdilik tüm bunları atlayıp sayfayı Tablo 15-9'a çevireceğiz. Bu tablo, "Saat Seçim Biti Açıklamasını" gösterir. Şimdi, o yazmaçta az önce belirlediğimiz bitlere karşılık gelen satırı bulana kadar o tabloya bakın. Satırda "clk/1024 (ön ölçekleyiciden)" yazıyor. Bunun anlamı, Zamanlayıcı/Sayaç0'ın (TCNT0), CPU frekansının 1024'e bölümü olan bir hızda ilerlemesini istediğimizdir. Mikrodenetleyicimiz 16MHz kristal osilatör tarafından beslendiği için bu, CPU'muzun komutları yürütme hızının şu anlama geldiği anlamına gelir. Saniyede 16 milyon talimat. Dolayısıyla TCNT0 sayıcımızın işaretleyeceği oran saniyede 16 milyon/1024 = 15625 defadır (farklı saat seçme bitleriyle deneyin ve ne olduğunu görün - felsefemizi hatırlıyor musunuz?). 15625 sayısını daha sonrası için aklımızın bir köşesinde tutalım ve sonraki iki kod satırına geçelim:

ldi sıcaklığı, 0b00000001

sts TIMSK0, sıcaklık

Bu, TIMSK0 adlı bir kaydın 0. bitini ayarlar ve geri kalan her şeyi temizler. Veri sayfasındaki sayfa 109'a bakarsanız, TIMSK0'ın "Zamanlayıcı/Sayaç Kesintisi Maske Kaydı 0" anlamına geldiğini ve kodumuzun "Zamanlayıcı/Sayaç0 Taşma Kesme Etkin" anlamına gelen TOIE0 adlı 0. biti ayarladığını göreceksiniz. … Orası! Şimdi bunun neyle ilgili olduğunu görüyorsunuz. Artık üstteki resmimizdeki ilk karardan istediğimiz gibi "interrupt enable bit set"imiz var. Şimdi tek yapmamız gereken "genel kesintileri" etkinleştirmek ve programımız bu tür kesintilere yanıt verebilecek. Kısa süre içinde global kesintileri etkinleştireceğiz, ancak bunu yapmadan önce bir şeyle kafanız karışmış olabilir.. neden her zamanki "out" yerine "sts" komutunu TIMSK0 kaydına kopyalamak için kullandım?

Beni daha önce görmediğiniz bir talimatı kullandığımı gördüğünüzde yapmanız gereken ilk şey veri sayfasındaki 616. sayfaya dönmek. Bu, "Talimat Seti Özeti"dir. Şimdi kullandığım "STS" talimatını bulun. Bir R kaydından (R16 kullandık) ve "Doğrudan SRAM'a depola" konumundan k (bizim durumumuzda TIMSK0 tarafından verilmiştir) bir sayı aldığını söylüyor. Öyleyse neden TIMSK0'da depolamak için 2 saat döngüsü alan (tablodaki son sütuna bakın) "sts" kullanmak zorundaydık ve daha önce TCCR0B'de depolamak için sadece bir saat döngüsü alan "out"a ihtiyacımız vardı? Bu soruyu cevaplamak için sayfa 614'teki kayıt özet tablomuza geri dönmemiz gerekiyor. TCCR0B kaydının 0x25 adresinde ama aynı zamanda (0x45) adresinde olduğunu görüyorsunuz değil mi? Bu, SRAM'da bir kayıt olduğu anlamına gelir, ancak aynı zamanda "port" (veya i/o kaydı) olarak adlandırılan belirli bir kayıt türüdür. "out" komutunun yanındaki talimat özet tablosuna bakarsanız, R16 gibi "çalışan kayıtlardan" değerleri alıp bir PORT'a gönderdiğini göreceksiniz. Böylece TCCR0B'ye yazarken "out" kullanabilir ve kendimize bir saat döngüsü kaydedebiliriz. Ama şimdi kayıt tablosunda TIMSK0'a bakın. 0x6e adresine sahip olduğunu görüyorsunuz. Bu, bağlantı noktası aralığının dışındadır (bunlar yalnızca SRAM'ın ilk 0x3F konumlarıdır) ve bu nedenle sts komutunu kullanmaya ve bunu yapmak için iki CPU saat döngüsü almaya geri dönmeniz gerekir. Lütfen hemen şimdi 615. sayfadaki talimat özet tablosunun sonundaki Not 4'ü okuyun. Ayrıca, PORTD gibi tüm giriş ve çıkış bağlantı noktalarımızın tablonun altında yer aldığına dikkat edin. Örneğin, PD4, 0x0b adresinde bit 4'tür (şimdi yorumlanmamış kodumda tüm 0x0b öğelerinin nereden geldiğini görüyorsunuz!).. tamam, hızlı soru: "sts"yi "out" olarak değiştirdiniz mi ve bakın ne oldu? olur mu? Felsefemizi unutmayın! kır! şeyler için sadece benim sözümü almayın.

Tamam, devam etmeden önce, bir dakikalığına veri sayfasında 19. sayfaya dönün. Veri belleğinin (SRAM) bir resmini görüyorsunuz. SRAM'deki ilk 32 kayıt (0x0000'den 0x001F'ye), kodumuzda değişken olarak her zaman kullandığımız R0'dan R31'e kadar "genel amaçlı çalışan kayıtlardır". Sonraki 64 kayıt, 0x005f'ye kadar olan I/O portlarıdır (yani, "sts" yerine "out" komutunu kullanabileceğimiz kayıt tablosunda parantez içinde olmayan adresleri yanlarında olan bahsettiğimiz kayıtlar) Son olarak SRAM'ın sonraki bölümü, 0x00FF adresine kadar özet tablosundaki diğer tüm kayıtları içerir ve son olarak geri kalanı dahili SRAM'dir. Şimdi hızlıca, bir saniyeliğine 12. sayfaya dönelim. Burada, her zaman değişkenlerimiz olarak kullandığımız "genel amaçlı çalışan kayıtların" bir tablosunu görüyorsunuz. R0 ila R15 ve ardından R16 ila R31 arasındaki kalın çizgiyi görüyor musunuz? Bu nedenle R16'yı her zaman en küçüğü olarak kullanıyoruz ve bir sonraki derste buna biraz daha gireceğim, burada ayrıca üç adet 16 bitlik dolaylı adres kaydına, X, Y ve Z'ye de ihtiyacımız olacak. Şimdi buna ihtiyacımız olmadığından ve burada yeterince çıkmaza girdiğimizden beri buna girelim.

Veri sayfasının 11. sayfasına bir sayfa geri dönün. Sağ üstte SREG kaydının bir diyagramını mı göreceksiniz? Bu kaydın 7. bitinin "I" olarak adlandırıldığını görüyorsunuz. Şimdi sayfaya gidin ve Bit 7…'nin açıklamasını okuyun. yay! Global Interrupt Enable bitidir. Yukarıdaki diyagramımızdaki ikinci kararı geçmek ve programımızda timer/counter overflow kesintilerine izin vermek için ayarlamamız gereken budur. Dolayısıyla programımızın bir sonraki satırı şöyle olmalıdır:

sbi SREG, ben

bu, SREG kaydında "I" olarak adlandırılan biti ayarlar. Ancak, bunun yerine talimatı kullandık

sei

Bunun yerine. Bu bit, programlarda o kadar sık ayarlanır ki, bunu yapmak için daha basit bir yol yaptılar.

Peki! Şimdi, "jmp overflow_handler" ne zaman olursa olsun yürütülecek şekilde, taşma kesintilerini kullanıma hazır hale getirdik.

Devam etmeden önce, çok önemli olduğu için SREG kaydına (Durum Kaydı) hızlıca bir göz atın. Bayrakların her birinin neyi temsil ettiğini okuyun. Özellikle, kullandığımız talimatların çoğu bu bayrakları her zaman ayarlayacak ve kontrol edecektir. Örneğin, daha sonra "hemen karşılaştır" anlamına gelen "CPI" komutunu kullanacağız. Bu talimat için talimat özet tablosuna bir göz atın ve "bayraklar" sütununda kaç tane bayrak belirlediğine dikkat edin. Bunların hepsi SREG'deki bayraklardır ve kodumuz onları ayarlayacak ve sürekli kontrol edecektir. Birazdan örnekler göreceksiniz. Son olarak, bu kod bölümünün son biti:

clr sıcaklığı

çıkış TCNT0, temp sbi DDRD, 4

Buradaki son satır oldukça açık. PortD için Veri Yön Kaydının 4. bitini ayarlar ve PD4'ün ÇIKIŞ olmasına neden olur.

İlki, temp değişkenini sıfıra ayarlar ve ardından bunu TCNT0 kaydına kopyalar. TCNT0 bizim Zamanlayıcı/Sayaç0'ımızdır. Bu onu sıfıra ayarlar. PC bu satırı çalıştırır çalıştırmaz timer0 sıfırdan başlayacak ve saniyede 15625 kez sayacak. Sorun şudur: TCNT0 bir "8-bit" kayıttır, değil mi? Peki 8 bitlik bir kaydın tutabileceği en büyük sayı nedir? Peki 0b11111111 öyle. Bu 0xFF sayısıdır. Hangisi 255'tir. Ne olduğunu görüyor musunuz? Zamanlayıcı saniyede 15625 kat artarak ilerliyor ve 255'e her ulaştığında "taşıyor" ve tekrar 0'a geri dönüyor. Sıfıra dönerken aynı zamanda bir Zamanlayıcı Taşması Kesintisi sinyali gönderir. PC bunu alıyor ve şimdi ne yaptığını biliyorsun değil mi? Evet. Program Belleği konumu 0x0020'ye gider ve orada bulduğu talimatı yürütür.

Harika! Hala benimleysen yorulmaz bir süper kahramansın! Hadi devam et…

Adım 6: Taşma İşleyicisi

Öyleyse, timer/counter0 yazmacının az önce taştığını varsayalım. Artık programın bir kesme sinyali aldığını ve 0x0020'yi çalıştırdığını biliyoruz, bu da Program Sayacı, PC'ye "overflow_handler" etiketine atlamasını söyler, bu etiketten sonra yazdığımız kod şudur:

taşma_işleyicisi:

inc taşmaları cpi taşmaları, 61 brne PC+2 clr taşmaları reti

Yaptığı ilk şey "overflows" (genel amaçlı çalışma registerı R17 için bizim adımızdır) değişkenini arttırmaktır, sonra taşmaların içeriğini 61 sayısı ile "karşılaştırır". iki sayı ve sonuç sıfır ise SREG kaydındaki Z bayrağını ayarlar (bu kaydı her zaman göreceğimizi söylemiştim). İki sayı eşitse Z bayrağı 1, iki sayı eşit değilse 0 olacaktır.

Sonraki satırda "eşit değilse dal" anlamına gelen "brne PC+2" yazıyor. Esasen, SREG'deki Z bayrağını kontrol eder ve eğer bir DEĞİLSE (yani iki sayı eşit değildir, eğer eşit olsaydı, sıfır bayrağı ayarlanır) PC, PC+2'ye dallanır, yani bir sonrakini atlar. line ve doğrudan "reti" ye gider, bu da kesmeden, kesme geldiğinde koddaki herhangi bir yere geri döner. Eğer brne komutu sıfır bayrak bitinde 1 bulursa dallanmaz ve bunun yerine sadece bir sonraki satıra devam eder, bu satır taşar ve onu 0'a sıfırlar.

Bütün bunların net sonucu nedir?

Her zaman bir zamanlayıcı taşması olduğunda, bu işleyicinin "taşma" değerini bir artırdığını görüyoruz. Dolayısıyla "taşma" değişkeni, meydana gelen taşma sayısını sayıyor. Sayı 61'e ulaştığında sıfıra sıfırlıyoruz.

Şimdi bunu neden yapalım ki?

Görelim. CPU'muz için saat hızımızın 16MHz olduğunu ve zamanlayıcının yalnızca saniyede 15625 sayım hızında sayması için TCCR0B kullanarak "önceden ölçeklendirdiğimizi" hatırlayın, değil mi? Ve zamanlayıcı 255'e ulaştığında taşar. Yani saniyede 15625/256 = 61.04 kez taşar. Taşma sayısını "overflows" değişkenimiz ile takip ediyoruz ve bu sayıyı 61 ile karşılaştırıyoruz. Yani görüyoruz ki "overflows" saniyede bir 61'e eşit olacak! Böylece işleyicimiz "taşmaları" saniyede bir sıfıra sıfırlayacaktır. Dolayısıyla, "taşma" değişkenini basitçe izleyecek ve sıfıra her sıfırlandığında not alacak olsaydık, gerçek zamanlı olarak saniye saniye sayıyor olurduk (Bir sonraki öğreticide daha kesin bir sonucu nasıl elde edeceğimizi göstereceğimizi unutmayın. Arduino "gecikme" rutininin çalıştığı gibi milisaniye cinsinden gecikme).

Şimdi zamanlayıcı taşma kesintilerini "işledik". Bunun nasıl çalıştığını anladığınızdan emin olun ve ardından bu gerçeği kullandığımız bir sonraki adıma geçin.

7. Adım: Gecikme

Artık zamanlayıcı taşma kesme işleyicisi "overflow_handler" rutinimizin "overflows" değişkenini saniyede bir sıfıra ayarlayacağını gördüğümüze göre, bu gerçeği bir "gecikme" alt rutini tasarlamak için kullanabiliriz.

Gecikmemizin altından aşağıdaki koda bir göz atın: etiket

gecikme:

clr taşmaları sec_count: cpi taşmaları, 30 brne sec_count geri

Programımızda her gecikmeye ihtiyacımız olduğunda bu alt programı çağıracağız. Çalışma şekli, önce "taşma" değişkenini sıfıra ayarlamasıdır. Sonra "sec_count" etiketli bir alana girer ve taşmaları 30 ile karşılaştırır, eğer eşit değillerse sec_count etiketine geri döner ve sonunda eşit olana kadar tekrar tekrar karşılaştırır, vb. zamanlayıcı kesme işleyicimizde taşma değişkenlerini artırmaya devam ediyor ve bu yüzden burada her dolaştığımızda değişiyor. 1/2 saniye gecikme

Alıştırma 2: overflow_handler rutinini aşağıdaki şekilde değiştirin:

taşma_işleyicisi:

inc reti taşar

ve programı çalıştırın. Farklı bir şey var mı? Neden veya neden olmasın?

Adım 8: Göz kırpın

Son olarak göz kırpma rutinine bakalım:

goz kirpmak:

sbi PORTD, 4 yeniden arama gecikmesi cbi PORTD, 4 yeniden arama gecikmesi rjmp yanıp sönme

Önce PD4'ü açıyoruz, sonra gecikme alt rutinimizi çağırıyoruz. PC bir "ret" ifadesine ulaştığında, rcall'dan sonraki satıra geri dönmesi için rcall kullanıyoruz. Daha sonra gecikme rutini, gördüğümüz gibi taşma değişkeninde 30 sayım için geciktirir ve bu neredeyse tam olarak 1/2 saniyedir, sonra PD4'ü kapatırız, 1/2 saniye daha geciktirir ve sonra tekrar başa döneriz.

Net sonuç, yanıp sönen bir LED!

Sanırım şimdi "blink" in muhtemelen montaj dilindeki en iyi "merhaba dünya" programı olmadığını kabul edeceksiniz.

Alıştırma 3: Programdaki çeşitli parametreleri, LED'in saniye veya saniyede 4 kez gibi farklı oranlarda yanıp sönmesi için değiştirin. Alıştırma 4: LED'in farklı sürelerde açık ve kapalı olması için değiştirin. Örneğin, 1/4 saniye açık ve ardından 2 saniye veya bunun gibi bir şey kapalı. Alıştırma 5: TCCR0B saat seçme bitlerini 100 olarak değiştirin ve ardından tabloyu yukarı çıkmaya devam edin. Hangi noktada öğretici 1'deki "hello.asm" programımızdan ayırt edilemez hale gelir? Alıştırma 6 (isteğe bağlı): 4 MHz veya 13.5 MHz gibi farklı bir kristal osilatörünüz varsa, 16 MHz osilatörünüzü değiştirin yenisi için breadboard'unuzda ve bunun LED'in yanıp sönme oranını nasıl etkilediğini görün. Artık kesin hesaplamayı yapabilmeli ve oranı nasıl etkileyeceğini tam olarak tahmin edebilmelisiniz.

9. Adım: Sonuç

Buraya kadar gelenler için, Tebrikler!

Kablolama ve deneme yapmaktan daha fazla okuma ve araştırma yaptığınızda, sıkı durmanın oldukça zor olduğunun farkındayım ama umarım aşağıdaki önemli şeyleri öğrenmişsinizdir:

  1. Program Belleği nasıl çalışır?
  2. SRAM nasıl çalışır?
  3. Kayıtlara nasıl bakılır
  4. Talimatlara nasıl bakılır ve ne yaptıklarını bilir
  5. Kesintiler nasıl uygulanır
  6. CP kodu nasıl yürütür, SREG nasıl çalışır ve kesintiler sırasında ne olur?
  7. Kodda döngüler, atlamalar ve zıplamalar nasıl yapılır
  8. Veri sayfasını okumak ne kadar önemli!
  9. Tüm bunları Atmega328p mikrodenetleyici için nasıl yapacağınızı öğrendikten sonra, ilgilendiğiniz yeni denetleyicileri öğrenmek için göreceli bir pasta yürüyüşü olacaktır.
  10. CPU zamanı gerçek zamana nasıl değiştirilir ve gecikme rutinlerinde kullanılır.

Artık daha iyi kod yazabilmemiz ve daha karmaşık şeyleri kontrol edebilmemiz için bir sürü teorimiz var. Yani bir sonraki eğitimde tam da bunu yapacağız. Daha karmaşık, daha ilginç bir devre kuracağız ve eğlenceli yollarla kontrol edeceğiz.

Alıştırma 7: Kodu çeşitli şekillerde "kırın" ve ne olduğunu görün! Bilimsel merak bebeğim! Başka biri bulaşıkları doğru yıkayabilir mi? Alıştırma 8: Bir liste dosyası oluşturmak için "-l" seçeneğini kullanarak kodu birleştirin. yani "avra -l flash.lst flash.asm" ve liste dosyasına bir göz atın. Ekstra Kredi: Başlangıçta verdiğim yorumsuz kod ile daha sonra tartışacağımız yorumlu kod farklıdır! Farklı olan bir kod satırı var. Bulabildin mi? Bu fark neden önemli değil?

Umarım eğlenmişsinizdir! Bir dahaki sefere görüşürüz…

Önerilen: