AVR асемблер за начинаещи (трета стъпка) - Моите статии - Каталог със статии - Личен сайт
И така, мои съвестни читатели, надявам се, че сте усвоили успешно материала, представен по-рано, и сте се справили с предложените от мен задачи. Време е да се обогатите с нови знания.
Последния път ви казах, че всички действия, които контролерът извършва, се определят от така наречените регистри и дори говорих за три от тях: PORTB, DDRB, PINB.
Всъщност има много повече от тези регистри. Освен това има две основни разновидности. Разгледаните по-горе се отнасят до така наречените входно-изходни регистри (IRV). Този тип регистри определя функционирането на различни възли и модули на контролера: входно-изходни портове, таймери, прекъсвания, енергонезависима памет, ADC, компаратор, цифрови интерфейси и др. За всеки от тези модули е дефиниран един и по-често няколко RVV. Тъй като материалът е представен, ще говоря за тях по-подробно. Днес се нуждаем само от вече познатия ни RVV от последния път.
В допълнение към RVV, чийто брой и състав се различават за различните микроконтролери, има и така наречените регистри с общо предназначение (RON). Всички AVR микроконтролери имат 32 RONs, които се наричат много просто: r0, r1, r2,. r31. Гледайки напред, ще кажа, че не всички от тези регистри са еквивалентни, но за повечето команди няма значение кой от тях да се използва. Трябва да се разбере и помни, че повечето от операциите в контролера се извършват на RON. Всъщност командите, които прегледахме последния път, съставляват по-голямата част от наличните команди за работа директно с RBB. Всички RON са еднобайтови регистри, които могат да бъдат достъпни в асемблер чрез посочване на тяхното име.
Но всичко това са думи. Нека сега да преминем към практиката. Нека напишем програма, която независимо ще мига с LED2определена честота. Създайте нова папка, наречена step3, и променете файла build.bat, за да се сглобява от тази папка. Отворете ASM_Ed и напишете следния текст в него:
.include "F:\Prog\AVR\asm\Appnotes\tn13def.inc" sbi DDRB, 4 ;PB4 линия - изход main: ;Програмиране на главния цикъл sbic PINB, 4 ;Ако PB4=0 (LED включен), пропуснете следващия. линия cbi PORTB,4 ;Настройте PB4 на 0 (LED ВКЛ.) sbis PINB, 4 ;Ако PB4=1 (LED изключен), пропуснете следващия. линия sbi PORTB,4 ;Настройте PB4 на 1 (светодиодът е изключен) ldi r16, 255 ;Заредете стойност в регистър r16 ldi r17, 255 ;Заредете стойност в регистър r17 закъснение: ;Забавете цикъл subi r16, 1 ;Извадете 1 от регистър r16 sbci r17, 0 ;S ubtract с пренасяне от регистър r17 brcc delay ;Ако не е имало пренасяне, връщане към delay rjmp main ;Връщане към main
Тази програма безспорно е по-голяма от предишната. Внимателният читател може да види в него както познати преди това команди, така и напълно нови. Първо, по традиция, ще говоря за иновациите в програмата, а след това за алгоритъма на програмата.
Сега за новите команди.
Командата ldi има два операнда: първият е RON, а вторият е всяка числова константа в диапазона от 0 до 255 (тъй като всички RON са еднобайтови, в тях могат да бъдат записани само числа в посочения диапазон, ако се опитате да напишете по-голямо число, компилаторът ще издаде предупреждение). В резултат на изпълнение на тази команда, посочената константа се зарежда в посочения RON. Тоест, ако направим аналогия с езиците от високо ниво, тази команда е подобна на операцията за присвояване (RON = k).
Командата subi е подобна по синтаксис на горната. В резултат на изпълнението му, зададената константа се изважда от посочения RON и резултатът се въвежда в същиярегистър (RON = RON - k).
Инструкцията brcc е инструкция за условен скок. Единственият му аргумент е етикетът, към който се премества програмният брояч, ако не е настъпило пренасяне в резултат на предишната операция, т.е. ако C битът на регистъра на състоянието е бил нулиран. Ако прехвърлянето е станало, се изпълнява следващата команда (Ако C=0, тогава отидете на етикета, в противен случай отидете на следващата команда).
Командата rjmp е безусловна команда за прескачане. Единственият му аргумент също е етикетът, към който се премества програмният брояч, но преместването се извършва винаги, без условия. По този начин инструкцията след rjmp никога няма да бъде изпълнена, освен ако там не е зададен друг етикет и към него се прескача от друга инструкция за прескачане.
Е, нашата база от знания се обогати с още 5 отбора. Сега имаме на разположение цели 9 броя.
Помислете за алгоритъма на програмата. Тези редове, които съдържат команди, които вече са ни известни, ще опиша накратко, ако приемем, че читателят е запознат с тях.
1 ред. Свързване на файла tn13def.inc към нашия асемблерен файл.
2 линия. Запишете "1" в 4-ия бит на регистъра DDRB, за да прехвърлите линията PB4 към изхода
3 линия. Етикетът "главен". По принцип този етикет не може да бъде зададен, но използването му е добра практика, тъй като не е необходимо всеки път да се инициализират входно-изходните портове. Щом имаме само една команда, това не е толкова страшно, но в реалните програми има десетки команди и някои от тях нулират използваните модули до първоначалното им състояние. И защо ни трябва? Така че го направете свое правило. Първо - инициализация, след това - зацикляне на програмата навънраздел за инициализация.
4 и 5 редове. Ако бит 4 в регистъра PINB е изчистен (т.е. светодиодът е изключен, команда sbic), след това отидете на ред 6, в противен случай изчистваме този бит в регистъра PORTB (команда cbi).
редове 6 и 7. Ако бит 4 в регистъра PINB е зададен (т.е. светодиодът свети, команда sbis), след това отидете на ред 8, в противен случай задайте този бит в регистъра PORTB (команда sbi).
По този начин линиите 4 - 7 извършват независимо превключване на светодиода (състоянието на светодиода се проверява и обръща). Ако оставите програмата по този начин, ще се окаже, че това превключване ще се случи с много висока скорост и окото няма да го види, освен че ще забележи, че яркостта на светодиода е намаляла. За да накарате светодиода да мига с приемлива скорост, е необходимо да забавите изпълнението на програмата. Това обикновено се прави чрез многократно изваждане или добавяне. Тъй като всяка операция изисква определено време, изпълнението на един и същи тип команди няколко хиляди пъти подред ще ни осигури необходимото забавяне. Нека сега да разгледаме как го внедрихме.
Ред 8. С помощта на командата ldi в RON r16 се зарежда максимално възможното число 255. Защо r16, а не r0? Факт е, че команди, които имат константа като втори операнд, не работят с регистри r0-r15. Ако се опитате да посочите такъв регистър като първи операнд, преводачът ще даде грешка и ще откаже да работи по-нататък.
Ред 9. Подобно на ред 8, само че сега 255 се зарежда в регистър r17. Стойностите, заредени в тези регистри, ще определят нашето забавяне.
Ред 10. Етикет "закъснение". Това е началото на цикъла на изваждане. Заредихме в два регистъра числото 255. Сега нашата задача е да извадим единици от тях, докато и двата станат равни на 0. Когато товасе случи, можем да напуснем този цикъл.
Ред 11. С командата subi изваждаме единица от регистър r16. Изваждането е просто, без условия и прехвърляния.
Ред 12. На пръв поглед изглежда безсмислено. Изваждаме нула от регистър r17. Не забравяйте обаче, че командата sbci изважда числото, като взема предвид пренасянето. Как работи всичко в комплекс, ще разкажа по-нататък.
Ред 13. Проверка на бита за пренасяне C. Тъй като тази команда следва командата за изваждане от регистър r17, пренасянето се проверява от този регистър. Докато съдържанието на регистър r17 е по-голямо или равно на 0, командата brcc ще ни изпрати към етикета за забавяне. Веднага щом има опит за изваждане от нула в r17, веднага отиваме на ред 14.
Сега помислете за работата на линии 10-13 в комплекса. Първо изваждаме 1 от регистър r16. В следващия ред извадете 0, като вземете предвид прехвърлянето от регистър r17. Докато съдържанието на регистър r16 е по-голямо или равно на 0, съдържанието на регистър r17 няма да се промени. При изваждане на единица от нула в регистър r16 възникват следните събития: стойността 255 е зададена в самия r16, освен това е зададен флагът за пренасяне C. Сега, когато изпълнявате командата sbci, от r17 се изважда 1. По този начин изваждането на единица от r17 възниква само когато се направи опит да се извади единица от нула в регистър r16. Когато регистър r17 се извади от нула, флагът за пренасяне също ще бъде зададен, който, когато бъде уловен от командата brcc, ще прекрати цикъла.
Поради такава проста конструкция се оказва, че целият цикъл ще бъде изпълнен 256 x 256 = 65536 пъти. Ако вземем предвид, че всяка команда се изпълнява един цикъл на контролера и друг цикъл се изразходва за прехода към началото на цикъла, тогава всеки цикъл преминава в четири цикъла. ТакаТака имаме забавяне от 4 x 65536 = 262144 цикъла. Ако вземем предвид, че честотата на контролера по подразбиране е 1 MHz, тогава забавянето ще бъде равно на 262144 / 1000000 = 0,26 s. Така светодиодът ще променя състоянието си на всеки четвърт секунда, което ще ни даде честота на мигане от приблизително 2 Hz.
И накрая ред 14, за който вече сме забравили. Той безусловно прескача към главния етикет, така че изключваме повторното инициализиране на изхода PB4.
Ето как работи нашата програма. Отбелязвам, че чрез задаване на различни стойности, записани в регистрите r16 - r17, можете да получите различни закъснения, по-малки от 0,26, като използвате горните изчисления. Но какво ще стане, ако трябва да направите голямо забавяне? След това трябва да добавите друг регистър, например r18. И изпълнението на забавянето ще изглежда така:
ldi r16, 255 ;Зареждане на стойност в регистър r16 ldi r17, 255 ;Зареждане на стойност в регистър r17 ldi r18, 2 ;Зареждане на стойност в регистър r18закъснение: ;Закъснение на цикъл subi r16, 1 ;Изваждане на 1 от регистър r16 sbci r17, 0 ;S изваждане с пренасяне от регистър r17 sbci r18, 0 ;Изваждане с пренасяне от регистър r18brcc забавяне ;Ако не е имало пренасяне, връщане към забавяне на етикета
Общият брой преминавания на цикъла вече ще бъде равен на 256x256x3 = 196608 и като се вземе предвид фактът, че в цикъла се е появила още една команда, сега той ще бъде изпълнен в 5 цикъла. Общият брой цикли на забавяне ще бъде 196608x5=983040, а периодът на забавяне: 983040/1000000 = 0,98 s.
Може би внимателен читател ще попита защо зареждаме 255 и 2, но умножаваме 256 и 3. Работата е там, че акаунтът се въвежда не от 1, както сме свикнали, а от 0 и поради това се получава един допълнителен цикъл. Ето колко лесно се обяснява всичко.
надежда,Всички мои аргументи и обяснения са ясни на читателя. Във връзка с това смятам, че стъпка 3 може да се счита за извършена. Научихме нови команди, запознахме се с различни видове регистри на контролери.
Като независима работа предлагам следната задача: да организирам алтернативно превключване на светодиоди LED1 и LED2 с честота възможно най-близка до 0,5 Hz, т.е. с период от 2 s.