Mündəricat:

AVR Assembler Dərsliyi 3: 9 Addımlar
AVR Assembler Dərsliyi 3: 9 Addımlar

Video: AVR Assembler Dərsliyi 3: 9 Addımlar

Video: AVR Assembler Dərsliyi 3: 9 Addımlar
Video: LDmicro 1: Введение (программирование релейной логики ПЛК микроконтроллера с помощью LDmicro) 2024, Iyul
Anonim
AVR Assembler Dərsliyi 3
AVR Assembler Dərsliyi 3

3 nömrəli dərsliyə xoş gəldiniz!

Başlamazdan əvvəl bir fəlsəfi fikir söyləmək istəyirəm. Bu dərsliklərdə qurduğumuz sxemləri və kodu sınamaqdan qorxmayın. Ətrafdakı telləri dəyişdirin, yeni komponentlər əlavə edin, komponentləri çıxarın, kod sətirlərini dəyişdirin, yeni sətirlər əlavə edin, sətirləri silin və nə baş verdiyini görün! Bir şeyi sındırmaq çox çətindir və qırarsan kimin umrunda? Mikro nəzarətçi də daxil olmaqla istifadə etdiyimiz heç bir şey çox bahalı deyil və işlərin necə uğursuz ola biləcəyini görmək həmişə öyrədicidir. Növbəti dəfə nə etməyəcəyinizi öyrənməklə yanaşı, daha da əhəmiyyətlisi, niyə etməməyinizi də biləcəksiniz. Əgər mənim kimi bir şey olsaydınız, uşaqlıqda və yeni bir oyuncaq əldə etdiyiniz zaman onu parçalara ayırdığınızdan çox keçməmiş onu düzgün nişanlamağa nə kömək etdi? Bəzən oyuncaq düzəlməz dərəcədə zədələnir, amma heç bir problem yoxdur. Uşağın öz maraq dairəsini hətta sındırılmış oyuncaqlar qədər araşdırmasına icazə verməsi, onu qabyuyan maşın yerinə elm adamına və ya mühəndisə çevirir.

Bu gün çox sadə bir dövrə bağlayacağıq və sonra nəzəriyyəyə bir az ağırlaşacağıq. Üzr istəyirik, amma alətlərə ehtiyacımız var! Daha ciddi bir dövrə quracağımız 4 -cü dərsdə bunun əvəzini çıxacağımıza söz verirəm və nəticə olduqca sərin olacaq. Bununla birlikdə, bütün bu dərsləri necə etməli olduğunuz çox düşüncə tərzindədir. Sadəcə süründürsəniz, dövrə qurun, kodu kopyalayıb yapışdırın və sonra işlədin, şübhəsiz ki, işləyəcək, amma heç bir şey öyrənməyəcəksiniz. Hər sətir üzərində düşünmək lazımdır. Fasilə. Təcrübə. İxtira et. Bunu belə etsəniz, 5 -ci dərsliyin sonunda sərin şeylər qurmaqdan əl çəkəcəksiniz və artıq repetitorluğa ehtiyacınız olmayacaq. Əks təqdirdə öyrənmək və yaratmaqdan daha çox sadəcə seyr edirsiniz.

Hər halda, kifayət qədər fəlsəfə, başlayaq!

Bu dərslikdə sizə lazım olacaq:

  1. prototip lövhəniz
  2. bir LED
  3. birləşdirən tellər
  4. 220 ilə 330 ohm arasında bir müqavimət
  5. Təlimat dəsti kitabı: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Məlumat cədvəli: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. fərqli bir kristal osilatör (isteğe bağlı)

Dərsliklərin tam toplusuna bir link:

Addım 1: Dövrün qurulması

Dövrün qurulması
Dövrün qurulması

Bu dərslikdəki sxem son dərəcə sadədir. Əsasən "göz qırpma" proqramını yazacağıq, buna görə bizə lazım olan hər şey aşağıdakılardır.

Bir LED -i PD4 -ə, sonra 330 ohm rezistora, sonra isə yerə bağlayın. yəni

PD4 - LED - R (330) - GND

və budur!

Teoriya çətin bir sürüşmə olacaq …

Addım 2: Niyə Şərhlərə və M328Pdef.inc Faylına ehtiyacımız var?

Düşünürəm ki, daxil edilmiş faylın və şərhlərin niyə faydalı olduğunu göstərməklə başlamalıyıq. Onlardan heç biri əslində lazım deyil və kodu onsuz da eyni şəkildə yaza, yığa və yükləyə bilərsiniz və mükəmməl işləyəcək (baxmayaraq ki, fayl daxil edilmədən montajçıdan bəzi şikayətlər ala bilərsiniz - amma heç bir səhv yoxdur)

Bu gün yazacağımız kod, şərhləri və əlavə faylını silməyim istisna olmaqla:

. cihaz ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16, 0x05 0x25, r16 ldi r16, 0x01 sts 0x6e, r16 sei clr r16 out 0x26, r16 sbi 0x0a, 0x04 sbi 0x0b, 0x04 b: 0x04 b: sbi 0b 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

olduqca sadə, elə deyilmi? Haha. Bu faylı yığsanız və yükləsəniz, LED -in saniyədə 1 dəfə yanıb sönməsinə 1/2 saniyə davam edən yanıb -sönmə arasındakı fasilə 1/2 saniyə davam edəcək.

Ancaq bu koda baxmaq çətin ki, maarifləndiricidir. Əgər belə bir kod yazsaydınız və gələcəkdə onu dəyişdirmək və ya yenidən təyin etmək istəsəniz, çox çətinlik çəkərdiniz.

Gəlin şərhləri qoyaq və faylı yenidən daxil edək ki, bunun bir mənasını verə bilək.

Addım 3: Blink.asm

Bu gün müzakirə edəcəyimiz kod budur:

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

; yazan: 1o_o7; Tarix:; versiya: 1.0; fayl belə saxlanılır: blink.asm; AVR üçün: atmega328p; saat tezliyi: 16MHz (isteğe bağlı); **********************************; Proqram funksiyası: ---------------------; bir LED yanıb -sönərək saniyələri sayır;; PD4 - LED - R (330 ohm) - GND;; --------------------------------------.nolist. "" daxil edin./m328Pdef.inc ".list; ==============; Bəyannamələr:.def temp = r16.def daşması = r17.org 0x0000; sıfırlama işləyicisinin yaddaşı (PC) yeri rjmp Sıfırla; jmp 2 cpu dövrünə, rjmp isə cəmi 1 -ə başa gəlir; 8k baytdan çox tullanmaq lazım olmadıqda; yalnız rjmp lazımdır. Buna görə də bəzi mikro nəzarətçilər; rjmp var və jmp deyil.org 0x0020; Timer0 daşma işləyicisinin yaddaş yeri rjmp overflow_handler; bir timer0 daşqın kəsilməsi baş verərsə bura gedin; ============ Sıfırla: ldi temp, 0b00000101 out TCCR0B, temp; CS00, CS01, CS02 Saat Seçici Bitlərini 101 olaraq təyin edin; bu, Taymer Sayacı0, TCNT0 -ı FCPU/1024 rejiminə keçirir; buna görə də CPU freq/1024 ldi temp, 0b00000001 sts TIMSK0, tempi qeyd edir; Taymer Daşması Kəsmə Enable (TOIE0) bitini təyin edin; Timer Interrupt Mask Register (TIMSK0) sei; qlobal fasilələri aktiv edin - "sbi SREG, I" clr temp out TCNT0, temp; Taymer/Sayıcıyı 0 sbi DDRD, 4 -ə işə salın; PD4 -ü çıxışa təyin edin; ====================; Proqramın əsas gövdəsi: yanıb -sönmək: sbi PORTD, 4; LED -i yandırın PD4 rcall gecikmə; gecikmə 1/2 saniyə cbi olacaq PORTD, 4; PD4 -də zəng gecikməsindəki LED -i söndürün; gecikmə 1/2 saniyə rjmp yanıp sönəcək; başlanğıc gecikməsinə geri dönün: clr daşması; daşqınları 0 sec_count olaraq təyin edin: cpi daşqınları, 30; daşqınların sayını və 30 brne sec_count müqayisə; bərabər deyilsə sec_count -a qayıtmaq; 30 daşqın meydana gəlmişsə, yanıb -sönənə qayıdır overflow_handler: inc daşqınlar; daşqınlara dəyişən cpi daşqınlarına 1 əlavə edin, 61; 61 brne PC+2 ilə müqayisə edin; Proqram Counter + 2 (növbəti sətri atla) bərabər deyilsə clr daşır; 61 daşqın baş verərsə sayğacı sıfır reti vəziyyətinə qaytarın; fasilədən qayıtmaq

Gördüyünüz kimi, şərhlərim indi bir az daha qısadır. Təlimat dəstindəki əmrlərin nə olduğunu bildikdən sonra bunu şərhlərdə izah etməyə ehtiyac yoxdur. Proqram baxımından nə baş verdiyini izah etməliyik.

Bütün bunların hissə -hissə nə etdiyini müzakirə edəcəyik, amma əvvəlcə qlobal bir perspektiv əldə etməyə çalışaq. Proqramın əsas hissəsi aşağıdakı kimi işləyir.

Əvvəlcə "sbi PORTD, 4" ilə PORTD -in 4 -cü bitini təyin edirik və bu, pinə gərginliyi 5V -ə qoyan PD4 -ə 1 göndərir. Bu LED -i yandıracaq. Daha sonra 1/2 saniyəni hesablayan "gecikmə" alt proqramına keçirik (bunu daha sonra necə edəcəyimizi izah edəcəyik). Daha sonra PD4 -ü 0V -ə qoyan və buna görə də LED -i söndürən PORTD -də yanıb -sönən 4 -cü bitə qayıdırıq. Daha sonra 1/2 saniyə gecikdiririk və sonra "rjmp yanıp sönmə" ilə yenidən göz qırpımının əvvəlinə qayıdırıq.

Bu kodu işə salmalı və lazım olanı etdiyini görməlisiniz.

Və orada var! Bütün bu kod fiziki olaraq edir. Mikrodenetleyicinin etdiklərinin daxili mexanikası bir az daha çox iştirak edir və buna görə də bu dərsliyi edirik. Beləliklə, hər bölməni növbə ilə müzakirə edək.

Addım 4:.org Assembler Direktivləri

Əvvəlki dərslərimizdən.nolist,.list,.include və.def assembler direktivlərinin nə etdiyini artıq bilirik, buna görə də bundan sonra gələn 4 kod xəttinə nəzər salaq:

.org 0x0000

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

. Org ifadəsi, assemblerə "Proqram Yaddaşı" nda növbəti ifadəni harada yazacağını bildirir. Proqramınız icra edildikdə, "Proqram Sayğacı" (PC olaraq qısaldılmış) icra olunan cari xəttin ünvanını ehtiva edir. Bu halda, kompüter 0x0000 -də olduqda, bu yaddaş yerində "jmp sıfırla" əmrini görür. Jmp Sıfırlamanı o yerə qoymaq istəməyimizin səbəbi, proqram başladıqda və ya çip sıfırlandıqda, kompüterin bu nöqtədə kodu icra etməyə başlamasıdır. Gördüyümüz kimi, dərhal "Sıfırla" etiketli hissəyə "atlamağı" söylədik. Niyə bunu etdik? Bu o deməkdir ki, yuxarıdakı son iki sətir sadəcə atlanır! Niyə?

Yaxşı, burada maraqlı şeylər olur. İndi bu təlimatın ilk səhifəsində göstərdiyim tam ATmega328p məlumat cədvəli olan bir pdf görüntüleyicisini açmalı olacaqsınız (bu səbəbdən "ehtiyacınız olacaq" bölməsində 4 -cü maddədir). Ekranınız çox kiçikdirsə və ya artıq çoxlu pəncərələriniz varsa (mənim vəziyyətimdə olduğu kimi) etdiyimi edə bilərəm və bunu Ereader -ə və ya Android telefonunuza qoya bilərsiniz. Montaj kodu yazmağı planlaşdırırsınızsa, hər zaman istifadə edəcəksiniz. Ən maraqlısı odur ki, bütün mikro nəzarətçilər çox oxşar şəkildə qurulmuşdur və buna görə də məlumat cədvəllərini oxumağa və onlardan kod yazmağa alışdıqda fərqli bir mikro nəzarətçi üçün eyni şeyi etməyi demək olar ki, əhəmiyyətsiz hesab edəcəksiniz. Beləliklə, əslində bütün mikrokontrolörləri bir mənada necə istifadə edəcəyimizi öyrənirik, nəinki atmega328p.

Tamam, məlumat cədvəlindəki 18-ci səhifəyə keçin və Şəkil 8-2-yə baxın.

Mikro nəzarətçidəki Proqram Yaddaşı belə qurulur. 0x0000 ünvanı ilə başladığını və iki hissəyə ayrıldığını görə bilərsiniz; bir tətbiq flaş bölməsi və bir önyük flash bölməsi. Səhifə 277 cədvəl 27-14-ə qısaca müraciət etsəniz, tətbiqin flaş bölməsinin 0x0000-dən 0x37FF-ə qədər yerləri və açılış flaş hissəsinin 0x3800-dən 0x3FFF-ə qədər qalan yerləri tutduğunu görəcəksiniz.

Məşq 1: Proqram yaddaşında neçə yer var? Yəni 3FFF -ni onluğa çevirin və 0 -da saymağa başladığımız üçün 1 əlavə edin. Hər bir yaddaş yeri 16 bit (və ya 2 bayt) geniş olduğundan yaddaşın ümumi bayt sayı nədir? İndi kilobaytda 2^10 = 1024 bayt olduğunu xatırlayaraq bunu kilobayta çevirin. Yükləmə flash bölməsi 0x3800 -dən 0x37FF -ə qədər gedir, bu neçə kilobaytdır? Proqramımızı saxlamaq üçün istifadə etməyimiz üçün neçə kilobayt yaddaş qalıb? Başqa sözlə, proqramımız nə qədər böyük ola bilər? Nəhayət, neçə kod xəttimiz ola bilər?

Tamam, indi flash proqram yaddaşının təşkili haqqında hər şeyi bildiyimiz üçün.org ifadələrini müzakirə etməyə davam edək. İlk yaddaş yerinin 0x0000 -də, Sıfırlama etiketli bölməmizə keçmək üçün təlimatımız olduğunu görürük. İndi ".org 0x0020" ifadəsinin nə etdiyini görürük. Növbəti sətirdəki təlimatın 0x0020 yaddaş yerinə yerləşdirilməsini istədiyimizi söyləyir. Orada yerləşdirdiyimiz təlimat, kodumuzda "overflow_handler" etiketli bir hissəyə keçiddir … indi niyə bu atlamanın 0x0020 yaddaş yerinə yerləşdirilməsini tələb edirik? Bunu öyrənmək üçün məlumat cədvəlindəki 65-ci səhifəyə baxırıq və Cədvəl 12-6-ya nəzər salırıq.

Cədvəl 12-6 "Vektorları Sıfırla və Kəsmə" cədvəlidir və "kəsilmə" aldıqda PC-nin hara gedəcəyini dəqiq göstərir. Məsələn, Vektor nömrəsi 1-ə baxırsınızsa, kəsilmənin "mənbəyi" "Sıfırla" dir, "Xarici Pin, Açıq Sıfırlama, Qəhvəyi Sıfırlama və Gözətçi Sistemi sıfırlama" kimi təyin olunur, əgər varsa mikro nəzarətçimizin başına gələnlər, PC proqram yaddaş yerimiz 0x0000 -də icra etməyə başlayacaq. Bəs onda bizim.org direktivimiz? Yaxşı, 0x0020 yaddaş yerinə bir əmr verdik və cədvələ baxsanız bir Taymer/Counter0 daşması baş verərsə (TIMER0 OVF -dən gəlir) 0x0020 yerində olan hər şeyi yerinə yetirəcəyini görəcəksiniz. Beləliklə, bu baş verdikdə, kompüter "overflow_handler" etiketlədiyimiz yerə atlayacaq. Sərin, düzdür? Niyə bunu etdiyimizi bir dəqiqə sonra görəcəksiniz, amma əvvəlcə bu təlimatın bir addımını bir kənara qoyaraq bitirək.

Kodumuzu daha səliqəli və səliqəli etmək istəyirsinizsə, hal -hazırda müzakirə etdiyimiz 4 sətri aşağıdakılarla əvəz etməliyik (bax: səhifə 66):

.org 0x0000

rjmp sıfırlayın; 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; PC = 0x0032

Beləliklə, müəyyən bir kəsmə baş verərsə, "kəsilmədən qayıtmaq" mənasını verən "reti" olacaq və başqa heç nə olmur. Ancaq bu müxtəlif fasilələri heç vaxt "Enable" etməsək, onlar istifadə edilməyəcək və bu nöqtələrə proqram kodu qoya bilərik. Mövcud "blink.asm" proqramımızda yalnız timer0 daşqın kəsilməsini (və əlbəttə ki, həmişə aktiv olan sıfırlama kəsilməsini) təmin edəcəyik və buna görə də digərləri ilə narahat olmayacağıq.

O zaman timer0 daşqın kəsilməsini necə "aktivləşdirə" bilərik? … bu dərsdə növbəti addımımızın mövzusudur.

Addım 5: Taymer/Sayıcı 0

Taymer/Sayıcı 0
Taymer/Sayıcı 0

Yuxarıdakı şəklə baxın. Bu, bəzi kənar təsirlər proqramımızın gedişatını "kəsdikdə" "PC" nin qərar vermə prosesidir. Bir fasilənin meydana gəldiyinə dair kənardan bir siqnal aldıqda etdiyi ilk şey, bu kəsilmə növü üçün "ara vermə" bitini təyin edib etmədiyimizi yoxlamaqdır. Əgər etməmişiksə, o zaman növbəti kod xəttimizi icra etməyə davam edir. Xüsusi kəsilmə imkan bitini təyin etsək (o bit yerində 0 deyil, 1 olsun), "qlobal fasilələri" aktiv edib -etmədiyimizi yoxlayacaq, yoxsa yenidən növbəti sətrə gedəcək. kodu yazın və davam edin. Qlobal fasilələri də aktiv etsək, o zaman bu cür kəsilmənin Proqram Yaddaşı yerinə gedəcək (Cədvəl 12-6-da göstərildiyi kimi) və orda verdiyimiz hər hansı bir əmri yerinə yetirəcək. Beləliklə, bütün bunları kodumuzda necə tətbiq etdiyimizi görək.

Kodumuzun etiketli sıfırlama bölməsi aşağıdakı iki sətirdən başlayır:

Sıfırla:

ldi temp, 0b00000101 TCCR0B, temp

Bildiyimiz kimi, bu, tempdən (yəni R16) dərhal sonra gələn nömrəni yükləyir, yəni 0b00000101. Sonra bu nömrəni "out" əmrindən istifadə edərək TCCR0B adlı reyestrə yazır. Bu qeyd nədir? Yaxşı, məlumat cədvəlinin 614 -cü səhifəsinə keçək. Bu, bütün qeydləri ümumiləşdirən cədvəlin ortasındadır. 0x25 ünvanında TCCR0B tapa bilərsiniz. (İndi kodun şərh edilməmiş versiyamda "0x25, r16" xəttinin haradan gəldiyini bilirsiniz). Yuxarıdakı kod seqmentində 0 -cı biti və 2 -ci biti qoyduğumuzu və qalanların hamısını təmizlədiyimizi görürük. Masaya baxaraq bunun CS00 və CS02 qurduğumuzu görə biləcəyinizi görə bilərsiniz. İndi məlumat cədvəlindəki "PWM ilə 8-bit Timer/Counter0" adlı fəslə keçək. Xüsusilə, həmin fəslin 107 -ci səhifəsinə keçin. Qeydiyyat xülasəsi cədvəlində gördüyümüz "Taymer/Sayğac Nəzarət Qeydiyyatı B" (TCCR0B) qeydinin eyni təsvirini görəcəksiniz (buna görə birbaşa bura gələ bilərdik, amma xülasə cədvəllərindən necə istifadə edəcəyinizi görmək istədim. gələcək istinad üçün). Məlumat cədvəli həmin reyestrdəki hər bitin və nə etdiklərinin təsvirini verməyə davam edir. Hələlik bütün bunları atlayacağıq və səhifəni Cədvəl 15-9-a çevirəcəyik. Bu cədvəldə "Saat Seçimi Bit Təsviri" göstərilir. İndi həmin qeyddə qurduğumuz bitlərə uyğun olan xətti tapana qədər o cədvələ baxın. Satırda "clk/1024 (prescalerdən)" yazılır. Bunun mənası odur ki, Timer/Counter0 (TCNT0) -in CPU tezliyi 1024 -ə bölünən bir sürətlə keçməsini istəyirik. Mikro nəzarətçimiz 16 MHz kristal osilatorla qidalanır. Saniyədə 16 milyon təlimat. Beləliklə, TCNT0 sayğacımızın qeyd edəcəyi nisbət saniyədə 16 milyon/1024 = 15625 dəfədir (fərqli saat seçmə bitləri ilə sınayın və nə olduğunu görün - fəlsəfəmizi xatırlayın?). Gəlin 15625 nömrəsini sonradan ağlımızın arxasında saxlayaq və növbəti iki kod sətrinə keçək:

ldi tempi, 0b00000001

sts TIMSK0, temp

Bu, TIMSK0 adlı bir qeydin 0 -cı bitini təyin edir və qalanların hamısını təmizləyir. Məlumat cədvəlində 109 -cu səhifəyə baxsanız, TIMSK0 -ın "Timer/Counter Interrupt Mask Register 0" və kodumuzun "Timer/Counter0 Overflow Interrupt Enable" mənasını verən TOIE0 adlı 0 -cu biti təyin etdiyini görəcəksiniz. … Orada! İndi bunun nə olduğunu görürsən. İndi yuxarıdakı şəklimizdəki ilk qərardan istədiyimiz kimi "ara vermə bit dəsti" ə sahibik. Beləliklə, indi etməli olduğumuz tək şey "qlobal fasilələri" təmin etməkdir və proqramımız bu cür kəsilmələrə cavab verə biləcək. Qısa müddətdə qlobal fasilələri təmin edəcəyik, amma bunu etməzdən əvvəl bir şeylə qarışıq ola bilərsiniz.. niyə adi "out" yerinə TIMSK0 reyestrinə kopyalamaq üçün "sts" əmrindən istifadə etdim?

Məni əvvəl görmədiyiniz bir təlimatdan istifadə etdiyimi gördüyünüz zaman etməli olduğunuz ilk şey məlumat cədvəlində 616 -cı səhifəyə müraciət etməkdir. Bu "Təlimat Seti Xülasəsi" dir. İndi istifadə etdiyim "STS" təlimatını tapın. Bir R reyestrindən (R16 istifadə etdik) və "SRAM -a birbaşa saxla" k yerindən (bizim vəziyyətimizdə TIMSK0 tərəfindən verilmişdir) bir nömrə götürüldüyünü söyləyir. Bəs niyə TIMSK0 -da saxlamaq üçün 2 saat dövrü (cədvəldəki son sütuna baxın) çəkən "sts" istifadə etməliyik və əvvəllər TCCR0B -də saxlamaq üçün yalnız bir saat dövrü tələb edən "çıxmaq" lazım idi? Bu suala cavab vermək üçün 614 -cü səhifədəki reyestr xülasə cədvəlimizə qayıtmalıyıq. Görürsünüz ki, TCCR0B reyestri 0x25 ünvanındadır, həm də (0x45)? Bu o deməkdir ki, bu SRAM -də bir qeyddir, eyni zamanda "port" (və ya i/o reyestri) adlanan müəyyən bir qeyd növüdür. "Çıxış" əmrinin yanındakı təlimat xülasə cədvəlinə baxsanız, bunun R16 kimi "işləyən qeydlərdən" dəyərlər götürdüyünü və bir PORT -a göndərdiyini görəcəksiniz. Beləliklə, TCCR0B -ə yazarkən "out" dan istifadə edə bilərik və özümüzü bir saat dövründən xilas edə bilərik. Ancaq indi qeyd cədvəlində TIMSK0 -a baxın. 0x6e ünvanına sahib olduğunu görürsünüz. Bu liman aralığının xaricindədir (SRAM -ın yalnız ilk 0x3F yerləridir) və buna görə sts əmrindən istifadə etmək və bunu etmək üçün iki CPU saat dövrü çəkmək lazımdır. Zəhmət olmasa 615 -ci səhifədəki təlimat xülasə cədvəlinin sonundakı Qeyd 4 -ü oxuyun. PORTD kimi bütün giriş və çıxış limanlarımızın masanın altında yerləşdiyini də unutmayın. Məsələn, 0x0b ünvanında PD4 bit 4-dir (indi şərh edilməmiş kodumda bütün 0x0b şeylərinin haradan gəldiyini görürsünüz!).. tamam, tez bir sual: "sts" sözünü "out" olaraq dəyişdiniz və nə oldu olur? Fəlsəfəmizi xatırlayın! qır! sadəcə sözlərimi qəbul etməyin.

Tamam, davam etməzdən əvvəl bir dəqiqəlik məlumat cədvəlindəki 19 -cu səhifəyə keçin. Məlumat yaddaşının (SRAM) bir şəklini görürsünüz. SRAM -dakı ilk 32 qeyd (0x0000 -dən 0x001F -ə qədər) hər zaman kodumuzda dəyişən olaraq istifadə etdiyimiz R0 ilə R31 arasındakı "ümumi təyinatlı iş qeydləri" dir. Növbəti 64 qeyd, 0x005f-ə qədər olan I/O portlarıdır (yəni dediklərimiz qeyd cədvəlində yanında "sts" yerinə "out" əmrindən istifadə edə biləcəyimiz mötərizəsiz ünvanlar var) SRAM -ın növbəti bölməsi 0x00FF ünvanına qədər xülasə cədvəlindəki bütün digər qeydləri ehtiva edir və nəhayət qalanı daxili SRAM -dır. İndi bir anlıq 12 -ci səhifəyə keçək. Orada həmişə dəyişənlərimiz kimi istifadə etdiyimiz "ümumi təyinatlı iş qeydləri" cədvəlini görürsünüz. R0 ilə R15 arasında, sonra R16 ilə R31 arasındakı qalın xətti görürsünüzmü? Bu səbəbdən həmişə R16-dan ən kiçik biri olaraq istifadə edirik və X, Y və Z üç 16-bit dolayı ünvan qeydlərinə ehtiyac duyacağımız növbəti dərsdə bir az daha çox məlumat əldə edəcəyəm. hələ buna ehtiyac yoxdur və burada kifayət qədər bataqlığa düşdüyümüz üçün buna hələ də daxil olun.

Məlumat cədvəlinin bir səhifəsini 11 -ci səhifəyə geri çevirin. Sağ üstdəki SREG reyestrinin diaqramını görəcəksiniz? Görürsünüz ki, həmin reyestrin 7 -ci bitinə "Mən" deyilir. İndi səhifəyə enin və Bit 7 -nin təsvirini oxuyun …. yay! Global Interrupt Enable bitidir. Yuxarıdakı diaqramımızdakı ikinci qərardan keçmək və proqramımızda taymer/sayğacın daşmasına mane olmaq üçün təyin etməli olduğumuz budur. Beləliklə, proqramımızın növbəti sətri belə olmalıdır:

sbi SREG, mən

SREG reyestrində "I" adlanan biti təyin edir. Ancaq bunun əvəzinə təlimatı istifadə etdik

sei

əvəzinə Bu bit proqramlarda o qədər tez qurulur ki, bunu etmək üçün daha sadə bir yol tapdılar.

Tamam! İndi daşqın kəsilmələrini hazır vəziyyətə gətirdik ki, "jmp overflow_handler" bir baş verəndə icra olunsun.

Davam etməzdən əvvəl, SREG reyestrinə (Status Register) bir göz atın, çünki bu çox vacibdir. Bayraqların hər birinin nəyi təmsil etdiyini oxuyun. Xüsusilə, istifadə etdiyimiz bir çox təlimat bu bayraqları hər zaman təyin edəcək və yoxlayacaq. Məsələn, daha sonra "dərhal müqayisə et" mənasını verən "CPI" əmrindən istifadə edəcəyik. Bu təlimat üçün təlimat xülasə cədvəlinə baxın və "bayraqlar" sütununda neçə bayraq qurduğuna diqqət yetirin. Bunların hamısı SREG -dəki bayraqlardır və kodumuz onları təyin edəcək və daim yoxlayacaq. Tezliklə nümunələri görəcəksiniz. Nəhayət kodun bu hissəsinin son biti:

clr temp

TCNT0, temp sbi DDRD, 4

Buradakı son xətt olduqca aydındır. Yalnız PD4 -ün Çıxışa səbəb olan PortD üçün Məlumat Yönləndirmə Reyestrinin 4 -cü bitini təyin edir.

Birincisi, dəyişən tempi sıfıra qoyur və sonra onu TCNT0 reyestrinə kopyalayır. TCNT0 bizim Timer/Counter0 -dir. Bu onu sıfıra qoyur. PC bu xətti icra edən kimi timer0 sıfırdan başlayacaq və hər saniyədə 15625 dəfə hesablanacaq. Problem budur: TCNT0 "8 bitlik" bir qeyddir, elə deyilmi? Bəs 8 bitlik reyestrin saxlaya biləcəyi ən böyük rəqəm hansıdır? Yaxşı 0b11111111. Bu 0xFF rəqəmidir. Hansı 255 -dir. Görürsən nə baş verir? Taymer saniyədə 15625 dəfə artır və hər dəfə 255 -ə çatanda "daşır" və yenidən 0 -a qayıdır. Sıfıra qayıtdıqda eyni zamanda Taymer Daşması Kəsmə siqnalı göndərir. PC bunu alır və indi nə etdiyini bilirsinizmi? Bəli. 0x0020 Proqram Yaddaşına gedir və orada tapdığı təlimatı yerinə yetirir.

Əla! Hələ də yanımdasansa, yorulmaz bir super qəhrəmansan! Davam edək…

Addım 6: Taşma İşləyicisi

Beləliklə, timer/counter0 reyestrinin yalnız daşdığını düşünək. İndi bilirik ki, proqram bir kəsmə siqnalı alır və 0x0020 proqramını yerinə yetirir, bu da Proqram Sayğacına, "overflow_handler" etiketinə keçməsini bildirir ki, bu etiketdən sonra yazdığımız kod budur:

overflow_handler:

inc daşır cpi daşır, 61 brne PC+2 clr daşır reti

Etdiyi ilk şey, "daşqınlar" dəyişənini artırmaqdır (bu, ümumi məqsədli işçi reyestri R17 üçün bizim adımızdır), sonra daşqınların məzmununu 61 rəqəmi ilə "müqayisə edir. iki ədəd və nəticə sıfır olarsa, SREG reyestrində Z bayrağını təyin edir (bu reyestri hər zaman görəcəyimizi söylədim). İki ədəd bərabərdirsə, Z bayrağı 1 olacaq, iki ədəd bərabər deyilsə 0 olacaq.

Növbəti sətirdə "brne PC+2" deyilir ki, bu da "bərabər deyilsə filial" deməkdir. Əslində, SREG -də Z bayrağını yoxlayır və bir deyilsə (yəni iki ədəd bərabər deyilsə, bərabər olsaydı sıfır bayrağı təyin ediləcəkdi) PC, PC+2 -yə bölünür, yəni növbəti atlayır. xətti və fasilə gəldikdə kodda olduğu yerə kəsilmədən qayıdan "reti" yə gedir. Brne təlimatı sıfır bayraq bitində 1 tapsaydı, dallanmazdı və bunun əvəzinə clr daşqınlarını sıfırlayaraq növbəti sətrə davam edərdi.

Bütün bunların net nəticəsi nədir?

Yaxşı görürük ki, hər dəfə bir taymer daşması olduqda, bu işləyici "daşqınların" dəyərini bir dəfə artırır. Beləliklə, "daşqınlar" dəyişən daşqınların sayını meydana gəldikcə sayır. Nömrə 61 -ə çatanda sıfıra endiririk.

İndi niyə dünyada bunu edək?

Görək. Xatırladaq ki, CPU üçün saat sürətimiz 16MHz -dir və TCCR0B -dən istifadə edərək "əvvəlcədən ölçdük" ki, taymer yalnız saniyədə 15625 sayma sayar? Və hər dəfə sayğac 255 sayına çatanda daşır. Yəni, saniyədə 15625/256 = 61.04 dəfə aşır. Dəyişən "daşqınlarımız" ilə daşqınların sayını izləyirik və bu rəqəmi 61 ilə müqayisə edirik. Beləliklə, "daşqınların" hər saniyədə 61 -ə bərabər olacağını görürük! Beləliklə, idarəçimiz hər saniyədə bir dəfə "daşqınları" sıfıra sıfırlayacaq. Dəyişən "daşqınları" sadəcə izləsək və hər dəfə sıfıra endirildiyini nəzərə alsaydıq, real vaxtda saniyə-saniyə sayarıq (Növbəti dərsdə daha dəqiq necə əldə edəcəyimizi göstərəcəyik. Arduino "gecikmə" rutininin işlədiyi kimi millisekundlarda gecikmə).

İndi timer daşması kəsilmələrini "idarə etdik". Bunun necə işlədiyini anladığınızdan əmin olun və sonra bu həqiqəti istifadə etdiyimiz növbəti mərhələyə keçin.

Addım 7: Gecikmə

İndi gördük ki, "overflow_handler" timer daşqın kəsmə işləmə rejimimiz "daşqınlar" dəyişənini saniyədə bir dəfə sıfıra qoyacaq və bu faktı "gecikmə" alt proqramını tərtib etmək üçün istifadə edə bilərik.

Gecikməmizin altından aşağıdakı kodu nəzərdən keçirin: etiket

gecikmə:

clr daşması sec_count: cpi daşması, 30 sn sec_count ret

Proqramımızda hər dəfə gecikmə lazım olanda bu alt proqramı çağıracağıq. İşləmə üsulu əvvəlcə "daşmalar" dəyişənini sıfıra qoyur. Daha sonra "sec_count" etiketli bir sahəyə daxil olur və daşqınları 30 ilə müqayisə edir, bərabər deyilsə, sec_count etiketinə qayıdır və nəhayət bərabər olana qədər yenidən və s. timer -də kəsmə işləyicisi dəyişən daşqınları artırmağa davam edir və buna görə də hər dəfə bura getdiyimiz zaman dəyişir. Nəhayət daşqınlar 30 -a bərabər olduqda döngədən çıxır və gecikmə dediyimiz yerə qayıdır: xalis nəticə 1/2 saniyə gecikmə

Məşq 2: overflow_handler rutinini aşağıdakı kimi dəyişdirin:

overflow_handler:

inc reti daşır

və proqramı işə salın. Fərqli bir şey varmı? Niyə və ya niyə yox?

Adım 8: Gözünü qırp

Sonda göz qırpma rejiminə baxaq:

göz qırpmaq:

sbi PORTD, 4 rcall gecikmə cbi PORTD, 4 rcall gecikmə rjmp yanıp sönmə

Əvvəlcə PD4 -ü işə salırıq, sonra gecikmə proqramını çağırırıq. Rcall -dan istifadə edirik ki, kompüter "ret" ifadəsinə çatanda rcall -ı izləyən xəttə qayıtsın. Daha sonra gördüyümüz kimi daşqın dəyişənində 30 say üçün gecikmə rutini gecikir və bu demək olar ki, 1/2 saniyədir, sonra PD4'ü söndürürük, 1/2 saniyəni daha gecikdiririk və sonra yenidən əvvəlinə qayıdırıq.

Xalis nəticə yanıb -sönən LED -dir!

Düşünürəm ki, indi "göz qırpma" nın montaj dilində ən yaxşı "salam dünya" proqramı olmadığı ilə razılaşacaqsınız.

Məşq 3: Proqramdakı müxtəlif parametrləri dəyişdirin ki, LED bir saniyə kimi fərqli sürətlə yanıb -sönsün və ya saniyədə 4 dəfə, və s. Məsələn, 1/4 saniyə yandırın və sonra 2 saniyə söndürün və ya buna bənzər bir şey edin. 5 -ci məşq: TCCR0B saatının bitlərini 100 -ə dəyişin və sonra masaya qalxmağa davam edin. Dərslik 1 -dən "hello.asm" proqramımızdan hansı məqamda fərqlənmir? 6 -cı məşq (isteğe bağlı): 4 MHz və ya 13,5 MHz və ya daha çox fərqli bir kristal osilatörünüz varsa, 16 MHz osilatörünüzü dəyişdirin. Çörək taxtanızda yenisini tapın və bunun LED -in yanıp sönmə sürətinə necə təsir etdiyini görün. İndi dəqiq hesablamadan keçməli və dərəcəyə necə təsir edəcəyini dəqiq təxmin etməlisiniz.

Addım 9: Nəticə

Bu günə qədər uğur qazananlarınız üçün təbrik edirik!

Kabel bağladığınızdan və sınadığınızdan daha çox oxuduğunuzda və yuxarı baxdığınızda slogging etməyin çox çətin olduğunu başa düşürəm, amma ümid edirəm ki, aşağıdakı vacib şeyləri öyrənmisiniz:

  1. Proqram Yaddaşı necə işləyir
  2. SRAM necə işləyir
  3. Qeydləri necə axtarmaq olar
  4. Təlimatlara necə baxmaq və nə etdiklərini bilmək
  5. Fasilələri necə tətbiq etmək olar
  6. CP kodu necə yerinə yetirir, SREG necə işləyir və fasilələr zamanı nə baş verir
  7. Kodda necə döngələr, atlamalar və sıçrayışlar etmək olar
  8. Məlumat cədvəlini oxumaq nə qədər vacibdir!
  9. Bütün bunları Atmega328p mikro nəzarətçisi üçün necə edəcəyinizi bildiyiniz zaman, maraqlandığınız hər hansı bir yeni nəzarətçi öyrənmək nisbi bir tort gəzintisi olacaq.
  10. CPU vaxtını real vaxta necə dəyişdirmək və gecikmə rutinlərində istifadə etmək olar.

İndi daha yaxşı bir kod yaza biləcəyimiz və daha mürəkkəb şeyləri idarə edə biləcəyimiz bir çox nəzəriyyəmiz var. Beləliklə, növbəti dərsimizdə bunu edəcəyik. Daha mürəkkəb, daha maraqlı bir sxem quracağıq və əyləncəli şəkildə idarə edəcəyik.

Məşq 7: Kodu müxtəlif yollarla "sındır" və nə olduğunu gör! Elmi maraq körpə! Başqa kimsə qabları düzgün yuya bilər? Təcrübə 8: Siyahı faylı yaratmaq üçün "-l" seçimindən istifadə edərək kodu yığın. Yəni "avra -l blink.lst blink.asm" və siyahı faylına baxın. Əlavə Kredit: Başında verdiyim şərh edilməmiş kod və sonradan müzakirə etdiyimiz şərh olunan kod fərqlidir! Fərqli bir kod xətti var. Tapa bilərsiniz? Niyə bu fərqin əhəmiyyəti yoxdur?

Ümid edirəm əyləndiniz! Gələn dəfə görüşərik…

Tövsiyə: