Мазмуну:

AVR Assembler үйрөткүчү 3: 9 кадамдары
AVR Assembler үйрөткүчү 3: 9 кадамдары

Video: AVR Assembler үйрөткүчү 3: 9 кадамдары

Video: AVR Assembler үйрөткүчү 3: 9 кадамдары
Video: Румба - A4988 Шаговый двигатель 2024, Июль
Anonim
AVR Assembler үйрөткүчү 3
AVR Assembler үйрөткүчү 3

№3 үйрөткүчкө кош келиңиз!

Баштоодон мурун мен философиялык ойду айткым келет. Бул окуу куралдарында биз куруп жаткан схемалар жана коддор менен эксперимент жасоодон коркпоңуз. Зымдарды алмаштырыңыз, жаңы компоненттерди кошуңуз, компоненттерди алып салыңыз, коддун линияларын өзгөртүңүз, жаңы саптарды кошуңуз, линияларды жок кылыңыз жана эмне болорун көрүңүз! Бир нерсени сындыруу өтө кыйын жана эгерде аны бузсаңыз, анда кимдин иши бар? Биз колдонгон эч нерсе, анын ичинде микроконтроллер, өтө кымбат эмес жана иштердин кандайча ийгиликсиз болуп кетерин көрүү дайыма билимдүү. Кийинки жолу эмне кылбаш керектигин гана эмес, андан да маанилүүсү, эмне үчүн мындай кылбаш керек экенин билесиз. Эгерде сиз мага окшош болсоңуз, кичинекей кезиңизде жаңы оюнчукка ээ болгонуңузда, аны көпкө чейин бөлүктөргө бөлүп койгонуңузга, анын туура белгилегенине эмне себеп болду? Кээде оюнчук оңдолбос зыянга учурады, бирок анча деле маанилүү эмес. Балага өзүнүн кызыгуусун сындырган оюнчуктарына чейин изилдөөгө уруксат берүү, аны идиш жуугучтун ордуна илимпозго же инженерге айлантат.

Бүгүн биз өтө жөнөкөй схеманы өткөрөбүз, анан теорияга бир аз оорлошобуз. Кечиресиз, бирок бизге куралдар керек! Мен муну 4 -үйрөткүчтө толтурабыз деп убада берем, анда биз дагы бир кыйла олуттуу райондук курулушту жасайбыз жана натыйжасы абдан сонун болот. Ошентсе да, бул үйрөткүчтөрдүн бардыгын жасооңуз өтө жай, ойлонулган түрдө. Эгерде сиз жөн гана жер айдап, схеманы куруп, кодду көчүрүп чаптап, анан иштетсеңиз, анда ал иштейт, бирок сиз эч нерсени үйрөнбөйсүз. Сиз ар бир сап жөнүндө ойлонушуңуз керек. Пауза. Эксперимент. Ойлоп табуу. Эгер сиз муну ушундай кылсаңыз, анда 5 -сабактын аягында сиз сонун нерселерди куруп каласыз жана мындан ары үйрөтүүнүн кажети жок. Болбосо үйрөнүп, жараткандан көрө жөн эле карап жатасыз.

Кандай болгон күндө да, философия жетиштүү, баштайлы!

Бул окуу куралында сизге керек болот:

  1. сиздин прототипдөө тактаңыз
  2. бир LED
  3. туташтыруучу зымдар
  4. каршылыгы 220-330 Ом
  5. Колдонмо инструкциясы: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. Маалымат баракчасы: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. башка кристалл осциллятору (милдеттүү эмес)

Бул жерде окуу куралдарынын толук жыйнагына шилтеме бар:

1 -кадам: Районду куруу

Circuit куруу
Circuit куруу

Бул үйрөткүчтөгү схема өтө жөнөкөй. Биз негизи "ирмөө" программасын жазабыз, андыктан бизге төмөнкүлөр гана керек.

LEDди PD4ке, анан 330 омдук резисторго, анан жерге туташтырыңыз. б.а.

PD4 - LED - R (330) - GND

жана бул!

Теория өтө катаал болуп калат …

2 -кадам: Эмне үчүн бизге комментарийлер жана M328Pdef.inc файлы керек?

Файлды жана комментарийлерди эмне үчүн пайдалуу экенин көрсөтүү менен баштоо керек деп ойлойм. Алардын эч бири чындыгында керек эмес жана сиз кодду ошол эле жол менен жаза, чогултуп жана жүктөй аласыз жана ал эң сонун иштейт (кошуу файлысыз монтаждоочудан кээ бир даттанууларды алсаңыз болот - бирок каталар жок)

Бул жерде биз бүгүн жаза турган код, мен комментарийлерди жана кошуу файлын алып салганымдан башка:

.diceice 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 0x04 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

абдан жөнөкөй, туурабы? Хаха. Эгер сиз бул файлды чогултуп жана жүктөсөңүз, анда LED диоду секундасына 1 ирмемде 1/2 секундага чейин жана 1/2 секундага чейин тыныгууга алып келет.

Бирок, бул кодду кароо дээрлик жарык эмес. Эгерде сиз ушундай код жазсаңыз жана аны өзгөрткүңүз келсе же келечекте аны башка жакка өзгөрткүңүз келсе, анда сизге кыйын болот.

Андыктан келгиле, комментарийлерди коюп, файлды кайра кошуп алалы, ошондо биз аны түшүнүшүбүз керек.

3 -кадам: Blink.asm

Мына бүгүн биз талкуулай турган код:

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

; жазган: 1o_o7; дата:; версия: 1.0; файл сакталды: blink.asm; AVR үчүн: atmega328p; саат жыштыгы: 16MHz (милдеттүү эмес); **********************************; Программанын функциясы: ---------------------; светодиодду өчүрүү менен секунддарды эсептейт;; PD4 - LED - R (330 ом) - GND;; --------------------------------------.nolist.include "./m328Pdef.inc".list; ==============; Декларациялар:.def temp = r16.def overflows = r17.org 0x0000; эстутум (PC) баштапкы абалга келтирүү иштетүүчү rjmp Reset; jmp 2 cpu циклине жана rjmp 1ге гана турат; ошондуктан 8к байттан ашык секирүү керек болбосо; сизге жөн гана rjmp керек. Ошондуктан кээ бир микроконтроллерлер; rjmp жана jmp эмес.org 0x0020; Timer0 толтуруу иштетүүчүсүнүн эс тутуму rjmp overflow_handler; timer0 ашуусу үзгүлтүккө учураса бул жерге барыңыз; ============= Reset: ldi temp, 0b00000101 out TCCR0B, temp; Clock Selector Bits CS00, CS01, CS02ди 101ге коюңуз; бул таймер Counter0, TCNT0 FCPU/1024 режимине коет; Ошентип, ал CPU freq/1024 лди темп, 0b00000001 STS TIMSK0, темп; Таймердин ашып кетүүсүн үзгүлтүккө учуратууну иштетүү (TOIE0) битин коюу; Timer Interrupt Mask Register (TIMSK0) sei; глобалдык үзгүлтүктөрдү иштетүү - "sbi SREG, I" clr temp out TCNT0, temp; таймерди/эсептегичти 0 sbi DDRD, 4ке баштоо; PD4 чыгарууга коюу; =====================; Программанын негизги бөлүгү: ирмөө: sbi PORTD, 4; LEDди күйгүзүү PD4 rcall кечигүү; кечигүү 1/2 секунда болот cbi PORTD, 4; LEDди өчүрүү PD4 rcall кечигүү; кечигүү 1/2 секунд rjmp ирмөө болот; баштоо кечигүүсүнө кайтуу: clr overflows; толуп кетүүнү 0 sec_count деп коюңуз: cpi overflows, 30; ашуулардын санын салыштыруу жана 30 brne sec_count; тармак тең эмес болсо, кайра sec_countка кайтуу; эгер 30 толуп кетүү пайда болсо кайтып келүү overflow_handler: inc overflows; толуп кетүүлөргө 1ди кошуңуз cpi overflows, 61; 61 brne PC+2 менен салыштыруу; Программа Counter + 2 (кийинки сапты өткөрүп жиберүү), эгерде бирдей эмес clr толуп кетсе; эгерде 61 толуп кетүү орун алса, эсептегичти нөлгө кайтаруу; үзгүлтүккө кайтуу

Көрүнүп тургандай, менин комментарийлерим азыр бир аз кыска. Көрсөтмө топтомунда кандай буйруктар бар экенин билгенден кийин, биз муну комментарийлерде түшүндүрүүнүн кажети жок. Биз эмне болуп жатканын программанын көз карашынан гана түшүндүрүшүбүз керек.

Биз мунун баары бөлүк -бөлүк эмне кылаарын талкуулайбыз, бирок адегенде глобалдык көз карашка ээ болууга аракет кылалы. Программанын негизги бөлүгү төмөнкүдөй иштейт.

Адегенде биз PORTDдин 4 битин "sbi PORTD, 4" менен койдук, бул 1 пин PD4ге жөнөтөт, ал чыңалууну 5Vга коет. Бул LED күйгүзүлөт. Андан кийин биз секундасынын 1/2 бөлүгүн эсептеген "кечигүү" чакан программасына өтөбүз (муну кийинчерээк түшүндүрөбүз). Биз андан кийин PORTDдеги PD4тү 0Vге орноткон 4 -ирмемге тазалап, LEDди өчүрөбүз. Андан кийин дагы бир 1/2 секундга кечиктиребиз, анан кайра "rjmp blink" менен кайра көз ирмемдин башына секиребиз.

Сиз бул кодду иштетип, ал эмне кылыш керек экенин көрүшүңүз керек.

Жана сенде бар! Бул коддун баары физикалык жактан жасалат. Микроконтроллердин ички механикасы бир аз көбүрөөк тартылган, ошондуктан биз бул окуу куралын аткарып жатабыз. Андыктан ар бир бөлүмдү кезек менен талкуулайлы.

4 -кадам:.org Assembler директивалары

Биз мурунтан үйрөткүчтөрүбүздөн.nolist,.list,.include жана.def assembler директивалары эмне кылаарын билебиз, андыктан андан кийин келген 4 коддуу линияны карап көрөлү:

.org 0x0000

jmp Reset.org 0x0020 jmp overflow_handler

. Org билдирүүсү ассемблерге "Программанын эс тутумунда" кийинки билдирүүнү коюуну айтат. Сиздин программа аткарылып жатканда, "Программалык эсептегич" (PC катары кыскартылган) аткарылып жаткан учурдагы саптын дарегин камтыйт. Демек, бул учурда компьютер 0x0000 турганда, ал ошол жерде жайгашкан "jmp Reset" буйругун көрөт. Ошол жерге jmp Reset коюуну каалаганыбыздын себеби, программа башталганда же чип баштапкы абалга келгенде, компьютер бул жерде кодду аткара баштайт. Ошентип, биз көрүп тургандай, биз жөн эле "Кайра коюу" деп аталган бөлүмгө дароо "секирүүнү" айттык. Эмнеге мындай кылдык? Бул жогорудагы акыркы эки сап жөн эле аттап өтүлүп жатканын билдирет! Неге?

Ооба, бул жерде нерселер кызыктуу болот. Сиз азыр бул окуу куралынын биринчи бетинде көрсөткөн ATmega328p маалымат барагын камтыган pdf көрүүчүсүн ачышыңыз керек (ошондуктан "сизге керек болот" бөлүмүнүн 4 -пункту). Эгерде сиздин экраныңыз өтө кичине болсо же сизде өтө эле көп терезелер ачылса (менде болгон сыяктуу), мен эмне кылсам, аны Ereaderге же Android телефонуңузга койсоңуз болот. Эгерде сиз монтаждоо кодун жазууну пландасаңыз, аны дайыма колдонуп турасыз. Эң сонун нерсе, бардык микроконтроллерлер абдан окшош түрдө уюштурулган, андыктан сиз маалымат барагын окууга жана алардан коддоого көнгөнүңүздөн кийин, башка микроконтроллер үчүн да муну жасоо дээрлик мааниге ээ эмес. Ошентип, биз чындыгында бардык микроконтроллерлерди атмега328п менен эмес, кандайдыр бир мааниде колдонууну үйрөнүп жатабыз.

Макул, маалымат барагындагы 18-бетке кайрылып, 8-2-сүрөттү караңыз.

Микроконтроллердеги Программанын эс тутуму ушундай орнотулат. Сиз 0x0000 дареги менен башталып, эки бөлүккө бөлүнгөнүн көрө аласыз; колдонмонун флеш бөлүмү жана жүктөө флеши бөлүмү. Эгерде сиз 277-столго 27-14-столго кыскача кайрылсаңыз, анда колдонмонун флеш бөлүмү 0x0000дөн 0x37FFке чейин жана жүктөө жарыгы бөлүмү 0x3800дөн 0x3FFFке чейин калган жерлерди ээлеп турганын көрөсүз.

Exercise 1: Программанын эс тутумунда канча жер бар? Башкача айтканда 3FFFти ондукка которуңуз жана 0 менен эсептей баштаганыбыз үчүн 1ди кошуңуз. Ар бир эстутумдун орду 16 бит (же 2 байт) болгондуктан, эстутумдун байттарынын жалпы саны канча? Эми муну килобайтка айлантыңыз, бир килобайтта 2^10 = 1024 байт бар экенин унутпаңыз. Жүктөө флэш бөлүмү 0x3800дөн 0x37FFке чейин барат, бул канча килобайт? Программаны сактоо үчүн канча килобайт эс калды? Башкача айтканда, биздин программа канчалык чоң болушу мүмкүн? Акыры, бизде канча сап код болушу мүмкүн?

Жарайт, азыр биз флеш -программанын эс тутумун уюштуруу жөнүндө баардыгын билгендиктен,.org билдирүүлөрүн талкуулоону уланталы. Биз 0x0000 эс тутумунун биринчи жайгашуусунда биздин бөлүмгө өтүү боюнча көрсөтмөлөрүбүз бар экенин көрүп жатабыз. Эми биз ".org 0x0020" билдирүүсү эмне кылганын көрөбүз. Анда биз кийинки саптагы көрсөтмөнү 0x0020 эс тутумуна жайгаштырууну каалайбыз деп айтылат. Биз берген көрсөтмө биздин коддун "overflow_handler" деп атаган бөлүмүнө өтүү … эми эмне үчүн бул секирикти 0x0020 эс тутумуна жайгаштырууну талап кылабыз? Муну билүү үчүн, биз маалымат барагындагы 65-бетке кайрылып, 12-6-таблицаны карайбыз.

12-6-таблица "Векторлорду баштапкы абалга келтирүү" таблицасы жана ал "үзгүлтүккө" жеткенде ЖК кайда барарын так көрсөтөт. Мисалы, эгер сиз Вектордук номер 1ди карасаңыз, үзгүлтүктүн "булагы" "ТҮЗӨТҮҮ" болуп саналат, ал "Тышкы пин, Күйгүзүү-Жөнгө салуу, Браун-Жөнгө салуу жана Күзөтчү системасын баштапкы абалга келтирүү" деген мааниге ээ, эгер болсо Мындай нерселер биздин микроконтроллерибизде болот, ЖК биздин программаны 0x0000 программасынын эс тутумунда аткара баштайт. Анда биздин.org директивасы жөнүндө эмне айтууга болот? Ооба, биз 0x0020 эс тутумуна буйрук бердик жана столду карасаңыз, эгерде Таймер/Counter0 толуп кетсе (TIMER0 OVFден келсе), 0x0020 жайгашкан жердин бардыгын аткарарын көрөсүз. Ошентип, качан болбосун, компьютер биз "overflow_handler" деп белгиленген жерге секирет. Туурабы? Сиз муну эмне үчүн кылгандыгыбызды бир мүнөттөн кийин көрөсүз, бирок алгач үйрөткүчтүн бул кадамын четке кагып бүтүрөлү.

Эгерде биз кодубузду тыкан жана тыкан кылгыбыз келсе, анда биз азыр талкуулап жаткан 4 сапты төмөнкүлөр менен алмаштырышыбыз керек (66 -бетти караңыз):

.org 0x0000

rjmp баштапкы абалга келтирүү; PC = 0x0000 рети; PC = 0x0002 рети; PC = 0x0004 рети; PC = 0x0006 рети; PC = 0x0008 рети; PC = 0x000A… reti; PC = 0x001E jmp overflow_handler: PC = 0x0020 reti: PC = 0x0022… reti; PC = 0x0030 рети; PC = 0x0032

Ошентип, эгерде үзгүлтүккө учураса, ал "үзгүлтүккө кайтуу" дегенди түшүндүрөт жана башка эч нерсе болбойт. Бирок эгер биз бул ар кандай үзгүлтүктөрдү эч качан "иштетпесек", анда алар колдонулбайт жана биз бул жерлерге программанын кодун кое алабыз. Учурдагы "blink.asm" программабызда биз timer0 толуп кетүүсүн гана иштетебиз (жана, албетте, ар дайым иштетилип турган баштапкы абалга келтирүү үзгүлтүккө учурайт), ошондуктан биз башкалар менен убара болбойбуз.

Анда кантип timer0 толуп кетүүсүн "иштетебиз"? … бул биздин үйрөткүчтөгү кийинки кадамыбыздын темасы.

5 -кадам: Таймер/Саноочу 0

Таймер/эсептегич 0
Таймер/эсептегич 0

Жогорудагы сүрөттү карап көрүңүз. Бул кандайдыр бир сырткы таасир биздин программанын агымын "үзгүлтүккө учуратканда" "ЖКнын" чечим кабыл алуу процесси. Сырттан үзгүлтүк болгондугу жөнүндө сигнал келгенде биринчи кылган нерсе - бул үзгүлтүккө учуроо үчүн "үзгүлтүккө учуратуу" битин койгонубузду текшерүү. Эгер бизде жок болсо, анда ал кийинки кодубузду аткарууну улантууда. Эгерде биз ошол үзгүлтүккө иштетүү битин орноткон болсок (ал жерде 0 эмес, 1 бит жайгашкан жерде), анда биз "глобалдык үзгүлтүктөрдү" иштеткенибизди же иштетпегенибизди текшерет, антпесе ал дагы кийинки сапка өтөт кодду жана улантыңыз. Эгерде биз глобалдык үзгүлтүктөрдү иштеткен болсок, анда ал ошол түрдөгү Программанын эс тутумуна барат (12-6-таблицада көрсөтүлгөндөй) жана биз ал жакка койгон бардык буйруктарды аткарат. Келгиле, мунун баарын кодубузга кантип киргизгенибизди карап көрөлү.

Биздин коддун белгиленген абалга келтирилген бөлүмү төмөнкү эки саптан башталат:

Баштапкы абалга келтирүү:

ldi temp, 0b00000101 TCCR0B, темп

Биз билгендей, бул темпке (б.а. R16) дароо кийинки номер жүктөлөт, бул 0b00000101. Андан кийин бул номер TCCR0B деп аталган реестрге "чыгуу" командасын колдонуп жазат. Бул эмне деген регистр? Келгиле, маалымат барагынын 614 -бетине өтөлү. Бул бардык реестрлерди камтыган столдун ортосунда. 0x25 дареги боюнча сиз TCCR0B таба аласыз. (Эми сиз коддун комментарийсиз версиясында "0x25, r16" сабы кайдан келгенин билесиз). Биз жогорудагы код сегментинде 0 -битти жана 2 -битти коюп, калганын тазалап койгонубузду көрөбүз. Таблицага карап, бул CS00 жана CS02 орнотулганын көрө аласыз. Эми маалымат барагындагы "8-бит таймер/PWM менен Counter0" деп аталган бөлүмгө өтөлү. Атап айтканда, ошол бөлүмдүн 107 -бетине өтүңүз. Сиз жөн эле реестрдин корутунду таблицасында көргөн "Таймер/Эсептөөчүнүн Реестри В" (TCCR0B) реестринин ошол эле сүрөттөмөсүн көрөсүз (демек, биз бул жерге түз келишибиз мүмкүн болчу, бирок мен жыйынды таблицаларды кантип колдонуу керек экенин көргүм келген. келечектеги маалымат үчүн). Маалыматтар барагы ошол реестрдеги ар бир битти жана алар эмне кылаарын сүрөттөп берүүнү улантууда. Азырынча мунун баарын өткөрүп жиберип, баракты 15-9-таблицага бурабыз. Бул таблицада "Clock Select Bit Description" көрсөтүлгөн. Эми ошол реестрде биз орноткон биттерге туура келген сызыкты тапмайынча, ошол таблицаны караңыз. Сапта "clk/1024 (prescalerден)" деп жазылган. Бул эмнени билдирет, биз Timer/Counter0 (TCNT0) 1024кө бөлүнгөн CPU жыштыгы боюнча ылдамдыкта өтүшүн каалайбыз. Бизде микроконтроллер 16 МГц кристалл осциллятору менен азыктангандыктан, бул биздин CPU көрсөтмөлөрдү аткаруучу ылдамдыкты билдирет Секундуна 16 миллион көрсөтмө. Ошентип, биздин TCNT0 эсептегичтин ылдамдыгы секундасына 16 миллион/1024 = 15625 эсе болот (сааттын тандалган биттери менен сынап көрүңүз жана эмне болорун көрүңүз - биздин философияны эстейсизби?). Келгиле, 15625 санын кийинчерээк эсибизде сактап, коддун кийинки эки сабына өтөлү:

ldi temp, 0b00000001

sts TIMSK0, темп

Бул TIMSK0 деп аталган реестрдин 0 -битин орнотот жана калганын тазалайт. Эгерде сиз маалымат барагындагы 109 -бетти карасаңыз, анда TIMSK0 "Timer/Counter Interrupt Mask Register 0" дегенди билдирет жана биздин код TOIE0 деп аталган 0 -битти "Таймер/Counter0 Overflow Interrupt Enable" дегенди билдирет. … Ал жерде! Эми мунун баары эмне экенин көрүп турасыз. Азыр бизде "үзгүлтүккө учуратууну иштетүү бит" орнотулду, биз каалагандай, биздин сүрөттүн үстү жагында. Ошентип, азыр биз "глобалдык үзгүлтүктөрдү" иштетүүбүз керек жана биздин программа мындай үзгүлтүктөргө жооп бере алат. Биз жакында глобалдык үзгүлтүктөрдү иштетебиз, бирок муну жасоодон мурун сизди бир нерсе түшүнбөй калышы мүмкүн.. эмне үчүн мен "sts" буйругун кадимки "чыгып кетүүнүн" ордуна TIMSK0 реестрине көчүрүү үчүн колдондум?

Мени көргөн сайын, сиз көрө элек инструкцияны колдонушуңуз керек, биринчи кезекте маалымат барагындагы 616 -бетке кайрылыңыз. Бул "Нускамалар жыйындысы". Эми мен колдонгон "STS" нускамасын табыңыз. Анда R реестринен (биз R16 колдонгонбуз) жана "SRAMге түз сактоо" к жайгашкан жерден номер талап кылынары айтылат (биздин учурда TIMSK0 тарабынан берилген). Анда эмне үчүн биз TIMSK0до сактоо үчүн 2 сааттык циклди (столдун акыркы тилкесин караңыз) талап кылган "sts" колдонушубуз керек болчу жана бизге TCCR0Bде сактоо үчүн бир гана сааттык циклди талап кылган "out" керек болчу? Бул суроого жооп берүү үчүн биз 614 -беттеги реестрдин корутунду таблицасына кайтып барышыбыз керек. Сиз TCCR0B регистринин 0x25 дареги боюнча экенин көрөсүзбү? (0x45) туурабы? Бул SRAMдагы реестр дегенди билдирет, бирок ал дагы "порт" (же киргизүү реестри) деп аталган реестрдин белгилүү бир түрү. Эгерде сиз "чыгуу" командасынын жанындагы көрсөтмөлөрдүн жыйынды таблицасын карасаңыз, анда ал R16 сыяктуу "жумушчу регистрлерден" маанилерди алып, аларды ПОРТко жөнөтөт. Ошентип, биз TCCR0Bге жазууда "out" колдонуп, өзүбүздү саат циклинен сактай алабыз. Бирок азыр каттоо столунан TIMSK0 издеңиз. Сиз анын 0x6e дареги бар экенин көрөсүз. Бул порттордун чегинен тышкары (бул SRAMдын биринчи 0x3F жерлери), ошондуктан сиз sts буйругун колдонуп, аны жасоо үчүн CPU процессинин эки циклине өтүшүңүз керек. Сураныч, азыр 615 -беттеги көрсөтмөлөрдүн жыйынды столунун аягындагы 4 -эскертмени окуңуз. Ошондой эле PORTD сыяктуу бардык киргизүү жана чыгаруу портторубуз столдун ылдый жагында жайгашканын байкаңыз. Мисалы, PD4 0x0b дареги боюнча 4 бит (азыр менин комментарийсиз кодумда 0x0b нерселеринин баары кайдан келгенин көрүп жатасыз!).. макул, тез суроо: сиз "sts" "out" га өзгөртүп, эмнени көрдүңүз болот? Биздин философияны унутпаңыз! сындыр! жөн эле нерсеге менин сөзүмдү албаңыз.

Макул, биз өтүүдөн мурун, маалымат барагындагы 19 -бетке бир мүнөткө кайрылыңыз. Сиз маалымат эсинин (SRAM) сүрөтүн көрөсүз. SRAMдагы биринчи 32 регистр (0x0000дөн 0x001Fке чейин) - бул "жалпы иштөөчү регистрлер", R0ден R31ге чейин, биз дайыма кодубуздун өзгөрмөлөрү катары колдонобуз. Кийинки 64 реестр-бул 0x005fке чейинки I/O порттору (б.а. биз айткан "стс" ордуна "out" буйругун колдоно ала турган реестр таблицасында ошол брекетсиз даректери бар) SRAMдын кийинки бөлүмү 0x00FF дарегине чейин кыскача таблицада калган бардык реестрлерди камтыйт, ал эми калган бөлүгү ички SRAM. Эми тезирээк, бир секундага 12 -бетке кайрылалы. Ал жерден биз дайыма өзгөрмөлөрүбүз катары колдонгон "жалпы иштөөчү регистрлердин" таблицасын көрөсүз. Сиз R0ден R15ке чейин, андан кийин R16дан R31ге чейинки сандардын ортосундагы жоон сызыкты көрөсүзбү? Ошол себептен улам биз R16ны эң кичинекей катары колдонобуз жана мен кийинки үйрөткүчтө ага бир аз көбүрөөк кирем, анда бизге 16 биттик кыйыр даректердин үч реестри, X, Y жана Z керек болот. буга киргиле, бирок азыр бизге кереги жок жана биз бул жерде жетишерлик тыгылып жатабыз.

Маалыматтар барагынын бир барагын 11 -бетке артка буруңуз. Сиз SREG реестринин диаграммасын жогорку оң бурчта көрөсүзбү? Сиз бул реестрдин 7 -битинин "I" деп аталганын көрөсүз. Эми баракка түшүп, Bit 7дин сүрөттөмөсүн окуңуз …. эй! Бул Global Interrupt Enable бит. Бул биздин диаграммабыздагы экинчи чечимди кабыл алуу жана программабызда таймердин/эсептегичтин толуп кетишине жол берүү үчүн биз муну орнотушубуз керек. Ошентип, биздин программанын кийинки сабы мындай болушу керек:

sbi SREG, I

ал SREG реестринде "I" деп аталган битти орнотот. Бирок, тескерисинче, биз инструкцияны колдондук

sei

анын ордуна Бул бит программаларда ушунчалык көп орнотулгандыктан, алар муну жөнөкөйлөтүшкөн.

Болуптур! Эми биз "jmp overflow_handler" качан гана болбосун аткарылышы үчүн, ташып кетүү даярдыгын алдык.

Биз өтүүдөн мурун, SREG реестрине (Статус реестрине) тез караңыз, анткени бул абдан маанилүү. Желектердин ар бири эмнени билдирерин окугула. Тактап айтканда, биз колдонгон көптөгөн көрсөтмөлөр бул желектерди дайыма орнотуп, текшерип турат. Мисалы, кийинчерээк биз "CPI" буйругун колдонобуз, бул "дароо салыштыруу" дегенди билдирет. Бул нускаманын көрсөтмөлөрүнүн кыскача таблицасын карап көрүңүз жана "желектер" тилкесинде канча желек орнотулганын байкаңыз. Булардын бардыгы SREGдеги желектер жана биздин код аларды орнотуп, дайыма текшерип турат. Сиз жакында мисалдарды көрөсүз. Акырында коддун бул бөлүмүнүн акыркы бөлүгү:

clr temp

TCNT0 чыгып, DDRD темп, 4

Бул жерде акыркы сызык абдан ачык. Бул PDDдин ЧЫГЫП кетишине алып келүүчү PortD үчүн Маалыматтар Багыты Реестринин 4 -битин гана орнотот.

Биринчиси, өзгөрмө темпти нөлгө коюп, анан аны TCNT0 реестрине көчүрөт. TCNT0 биздин Таймерибиз/Counter0. Бул аны нөлгө коёт. ЖК бул линияны аткараар замат таймер0 нөлдөн башталып, секундуна 15625 эсе ылдамдыкта эсептелет. Көйгөй бул: TCNT0-бул "8-бит" реестри, туурабы? Ошентип, 8-биттик реестрде эң көп сан кайсы? Мейли 0b11111111. Бул 0xFF саны. Кайсы 255. Демек, эмне болуп жатканын көрүп жатасызбы? Таймер секундасына 15625 эсе көбөйөт жана 255ке жеткен сайын "толуп", кайра 0ге кайтат. Нөлгө кайтып баратканда, ал Таймердин Ашып кетүүсүн токтотуу сигналын жөнөтөт. ЖК муну алат жана сиз азыр эмне кылып жатканын билесизби? Ооба. Ал 0x0020 Программалык эс тутумуна барат жана ал жерден тапкан көрсөтмөнү аткарат.

Абдан жакшы! Эгерде сиз дагы эле жанымда болсоңуз, анда сиз талыкпаган супер баатырсыз! Уланта берели…

6 -кадам: Толуп кетүү иштеткичи

Ошентип, таймер/counter0 реестри жаңы эле толуп кетти деп коёлу. Биз азыр программанын үзгүлтүккө учуроо сигналын алганын жана 0x0020 программасын аткарарын билебиз, ал Программаны эсептегичке, компьютерге "overflow_handler" этикеткасына өтүүнү айтат, биз бул белгиден кийин жазган код:

overflow_handler:

inc overflows cpi overflows, 61 brne PC+2 clr overgss reti

Биринчи нерсе - бул "толуп кетүү" өзгөрмөсүн көбөйтүү (бул жалпы аталыштагы жумушчу реестр R17 үчүн биздин атыбыз), андан кийин ашуунун мазмунун 61 саны менен "салыштырат. эки сан, эгерде натыйжа нөлгө барабар болсо, ал Z байрагын SREG реестрине коёт (мен сизге бул реестрди дайыма көрүп турарыбызды айттым). Эгерде эки сан барабар болсо, анда Z желеги 1 болот, эгер эки сан барабар болбосо, анда 0 болот.

Кийинки сапта "brne PC+2" деп айтылат, бул "бирдей болбосо, бутак" дегенди билдирет. Негизинен, ал SREGдеги Z желегин текшерет жана эгерде ал ЭМЕС болсо (б.а. эки сан бирдей эмес, эгерде алар тең болсо, нөлдүк желек орнотулат) PC бутактарын PC+2ге которот, демек ал кийинки өткөрүп жиберет линия жана түз "reti" ге барат, ал үзгүлтүк келгенде коддо кайсы жерге болсо, ошол жерге кайтып келет. Эгерде brne көрсөтмөсү нөлдүк желектин битинде 1ди тапса, ал бутактанбайт жана анын ордуна кийинки сапка уланат, аны clr overflows баштапкы абалга келтирет.

Мунун баарынын таза натыйжасы кандай?

Көрөбүз, таймердин толуп кетиши болгондо, бул иштетүүчү "толуп кетүүлөрдүн" маанисин бирден жогорулатат. Ошентип, "толуп кетүү" өзгөрмөсү толуп кетүүлөрдүн санын эсептеп жатат. 61ге жеткенде биз аны нөлгө кайтарабыз.

Эми эмне үчүн биз дүйнөдө мындай кылмак элек?

Көрөлү. Эске салсак, биздин CPU үчүн сааттын ылдамдыгы 16 МГц жана биз аны TCCR0B аркылуу "алдын ала масштабдадык", ошондо таймер секундуна 15625 эсептөө ылдамдыгы менен эсептейт? Ал эми таймер 255ке жеткенде ал толуп кетет. Демек, ал секундасына 15625/256 = 61.04 жолу ашып кетет. Биз "толуп кетүү" өзгөрмөлөрү менен толуп кетүүлөрдүн санын көзөмөлдөп жатабыз жана бул санды 61 менен салыштырып жатабыз. Ошентип, биз "ашуулар" секундасына бир жолу 61ге барабар экенин көрөбүз! Ошентип, биздин иштетүүчү "толуп кетүүнү" секундасына бир жолу нөлгө кайтарат. Ошентип, эгерде биз "толуп кетүү" өзгөрмөсүн жөн гана көзөмөлдөп, анын нөлгө кайра келгенин эске алсак, биз реалдуу убакытта секундадан секундага чейин санап чыкмакпыз. миллисекундтарда кечиктирүү, Arduino "кечиктирүү" тартиби иштейт).

Эми биз таймердин толуп кетүүсүн "иштеттик". Мунун кантип иштээрин түшүнүп, анан бул фактыны колдонуп жаткан кийинки кадамга өтүңүз.

7 -кадам: Кечиктирүү

Эми биз таймердин толуп кетүүсүн иштетүүчү "overflow_handler" процедурасы "overflows" өзгөрмөсүн секундасына бир жолу нөлгө койорун көрдүк, биз муну "кечиктирүү" подрограммасын иштеп чыгуу үчүн колдоно алабыз.

Кечигүүбүздүн астынан төмөнкү кодду карап көрүңүз: label

кечигүү:

clr overlocs sec_count: cpi overflows, 30 brne sec_count ret

Биз программабызды кечиктирүү керек болгондо, бул чакан программаны чакырабыз. Анын иштөө ыкмасы - бул адегенде "толуп кетүү" өзгөрмөсүн нөлгө орнотот. Андан кийин ал "sec_count" деп аталган аймакка кирет жана ашууну 30 менен салыштырат, эгерде алар бирдей болбосо, сек_сана энбелгисине кайтып келип, дагы бир жолу, жана дагы бир жолу салыштырып көрүшөт. биздин таймерде үзгүлтүккө учуроочу иштетүүчү өзгөрмөлөрдүн толуп кетишин көбөйтүүнү улантууда, ошондуктан биз бул жакты айланып өткөн сайын өзгөрүп турат. Акыры 30га барабар болгондо, ал циклден чыгып, биз кечигүү деп атаган жерге кайтып келет: таза жыйынтык 1/2 секундга кечигүү

Exercise 2: overflow_handler тартибин төмөнкүлөргө өзгөртүңүз:

overflow_handler:

inc толуп кетет

жана программаны иштетүү. Башка нерсе барбы? Эмнеге же эмне үчүн?

8 -кадам: Көзүңдү жум

Акыры, көз ирмөө тартибин карап көрөлү:

ирмөө:

sbi PORTD, 4 rcall кечигүү cbi PORTD, 4 rcall кечигүү rjmp blink

Адегенде биз PD4ти күйгүзөбүз, андан кийин кечиктирүү программасын чакырабыз. Биз rcall колдонобуз, ошондо ЖК "ret" билдирүүсүнө жеткенде, ал rcall кийинки линияга кайтып келет. Андан кийин, биз көргөндөй, толуп кетүүчү өзгөрмөнүн 30 эсебине кечиктирүү тартиби кечиктирилет жана бул дээрлик 1/2 секунд, андан кийин биз PD4'ду өчүрүп, дагы 1/2 секундду кечиктирип, анан кайра башына кайтабыз.

Таза натыйжа - бул жаркыраган LED!

Менин оюмча, азыр сиз "көздү ирмөө" ассемблер тилиндеги эң жакшы "салам дүйнө" программасы эмес экенине кошуласыз.

Exercise 3: Программанын ар кандай параметрлерин өзгөртүңүз, светодиод секундасына же ар кандай ылдамдыкта секундасына 4 эсе жыпылықтайт ж.б. Exercise 4: LED ар кандай убакытта күйүп жана өчүп турушу үчүн аны өзгөртүңүз. Мисалы, 1/4 секундга, андан кийин 2 секундага өчүрүп коюңуз. 5 -көнүгүү: TCCR0B саатын тандоо биттерин 100гө өзгөртүп, анан үстөлдүн үстүнө чыгууну улантыңыз. Кайсы учурда ал биздин "hello.asm" программасынан 1 -үйрөткүчтөн айырмаланбай калат? 6 -көнүгүү (милдеттүү эмес): Эгерде сизде 4 МГц же 13,5 МГц сыяктуу башка кристалл осциллятору болсо, 16 МГц осцилляторун алмаштырыңыз. жаңы панелиңизде жана бул LEDдин жаркыроо ылдамдыгына кандай таасир этерин көрүңүз. Сиз азыр так эсептөөдөн өтүп, курска кандай таасир этерин алдын ала билишиңиз керек.

9 -кадам: Жыйынтык

Ушул убакка чейин жеткендер үчүн, куттуктайбыз!

Түшүнүп турам, сиз өткөргүчтөргө жана эксперименттерге караганда көбүрөөк окуп, өйдө карап жатканда, бул абдан кыйын, бирок мен сиз төмөнкү маанилүү нерселерди үйрөндүңүз деп үмүттөнөм:

  1. Программанын эс тутуму кантип иштейт
  2. SRAM кантип иштейт
  3. Регистрлерди кантип издөө керек
  4. Көрсөтмөлөрдү кантип издеп, эмне кылып жатканын билүү
  5. Үзгүлтүктөрдү кантип ишке ашыруу керек
  6. КП кодду кантип аткарат, SREG кандай иштейт жана үзгүлтүктөр учурунда эмне болот
  7. Кантип циклдарды жана секирүүлөрдү жасоо жана коддо секирүү
  8. Маалымат баракчасын окуу канчалык маанилүү!
  9. Мунун баарын Atmega328p микроконтроллери үчүн кантип жасоону билгенден кийин, сизди кызыктырган жаңы контроллерлерди үйрөнүү үчүн салыштырмалуу торт жүрүшү болот.
  10. Кантип CPU убактысын реалдуу убакытка өзгөртүп, кечигүү тартибинде колдонсо болот.

Эми бизде көптөгөн теория бар болгондуктан, биз жакшыраак код жаза алабыз жана татаал нерселерди башкара алабыз. Ошентип, кийинки үйрөткүч биз дал ушундай кылабыз. Биз бир кыйла татаал, кызыктуу, схеманы куруп, аны кызыктуу жолдор менен башкарабыз.

Exercise 7: Кодду ар кандай жолдор менен "бузуп", эмне болорун көрүңүз! Илимге кызыккан бала! Башка кимдир бирөө идиш-аякты туура жууй алабы? Exercise 8: Тизме файлын түзүү үчүн "-l" опциясын колдонуп кодду чогултуп алыңыз. Башкача айтканда "avra -l blink.lst blink.asm" жана тизме файлын карап көрүңүз. Кошумча насыя: Мен башында берген комментарийсиз код менен кийинчерээк талкууланган комментарийлердин коду айырмаланат! Коддун бир сабы башкача. Сиз таба аласызбы? Эмне үчүн бул айырмачылыктын мааниси жок?

Сиз кызыктуу болду деп үмүттөнөбүз! Кийинки жолу көрүшкөнчө…

Сунушталууда: