- Stahuj zápisky z přednášek a ostatní studijní materiály
- Zapisuj si jen kvalitní vyučující (obsáhlá databáze referencí)
- Nastav si své předměty a buď stále v obraze
- Zapoj se svojí aktivitou do soutěže o ceny
- Založ si svůj profil, aby tě tví spolužáci mohli najít
- Najdi své přátele podle místa kde bydlíš nebo školy kterou studuješ
- Diskutuj ve skupinách o tématech, které tě zajímají
Studijní materiály
Hromadně přidat materiály
AWR - úvod
Y36SAP - Struktura a architektura počítačů
Hodnocení materiálu:
Popisek: úvod do AWR
Zjednodušená ukázka:
Stáhnout celý tento materiál, např. takto:
.EQU MojePametoveMisto = 0x0060
STS MojePametoveMisto, R1
Je to sice delší zápis, ale je lepší na zapamatování. Proto používejme snadno zapamatovatelná
jména.
Učební materiál ASM AVR
18
Dalším možným přístupem k SRAM je použití ukazatelů. K tomu potřebujete dva registry,
které budou obsahovat 16-bitovou adresu místa v SRAM V kapitole o registrech jsme si
říkali, že registrové ukazatele jsou dvojice registrů X (XH:XL, R27:R26), Y (YH:YL,
R29:R28) a Z (ZH:ZL, R31:R30). Dovolují přímý přístup k místům v paměti, na které ukazují
(např. pomocí ST X, R1), po předchozí dekrementaci této adresy o jedničku (např. ST -X,
R1) nebo s následnou inkrementací adresy (např. ST X+, R1). Uvedeme si jako příklad
přístup ke třem buňkám pomocí kódu, který vypadá např. takto :
.EQU MojePametoveMisto = 0x0060
.DEF MujRegistr = R1
.DEF DalsiRegistr = R2
.DEF JesteDalsiRegistr = R3
LDI XH, HIGH(MojePametoveMisto)
LDI XL, LOW(MojePametoveMisto)
LD MujRegistr, X+
LD DalsiRegistr, X+
LD JesteDalsiRegistr, X
Další, třetí, způsob přístupu k paměti je vhodný pouze pro zkušenější programátory.
Uvažujme např., že v programu potřebujeme často přistupovat ke třem místům v SRAM .
Dále uvažujme že máme nepoužitou ukazatelovou registrovou dvojici, kterou můžeme použít
výlučně pro náš účel. Když potřebujeme používat instrukce ST/LD , musíme měnit ukazatel
vždy, když budeme přistupovat k dalšímu místu, což není příliš šikovné.
Abychom se tomu vyhnuli byl vymyšlen přístup pomocí posunu (offsetu). Během tohoto
přístupu se nemění hodnota v registru. Adresa se spočítá dočasným přičtením pevného
posunu (offsetu). V předchozím příkladu se přístup k adrese 0x0062 provádí právě takto.
Nejdříve se ukazatel nastaví na naši základní adresu 0x0060:
.EQU MojePametoveMisto = 0x0060
.DEF MujRegistr = R1
LDI YH, HIGH(MojePametoveMisto)
LDI YL, LOW(MojePametoveMisto)
Někdy později v programu bychom rádi měli přístup k adrese 0x0062:
STD Y+2, MujRegistr
Pozn.: 2 je k Y přičtena jen dočasně. Obdobně lze používat i registrová dvojice Z, nedá se
však použít pro ukazatel X!
Odpovídající instrukce pro čtení z SRAM s posunem (offsetem) je
LDD MujRegistr, Y+2
Učební materiál ASM AVR
19
je rovněž možná.
Použití SRAM jako zásobník
Nejobecnější použití SRAM je použít ji jako zásobník. Tato struktura se nazývá Last-In-
First-Out (LIFO) nebo jednodušeji : poslední dovnitř, první ven.
Definice SRAM jako zásobníku
Použití SRAM jako zásobníku vyžaduje nejdřív nastavení ukazatele na zásobník. Ukazatel na
zásobník je 16-bitový ukazatel přístupný jako I/O port. Tento dvojitý registr je pojmenován
SPH:SPL. SPH obsahuje nejvíce významný adresový byt, SPL nejméně významný. Toto je
platné pouze v případě, kdy AVR má méně než 256 bytů SRAM. Když ne, SPH je
nedefinován a nepoužívá se. V následujících příkladech budeme předpokládat více než 256
bytů SRAM.
Při konstrukci zásobníku je do ukazatele zásobníku uložena nejvyšší přístupná adresa SRAM
.
.DEF MujRegistr = R16
LDI MujRegistr, HIGH(RAMEND) ; horní byt
OUT SPH,MujRegistr ; do zásobníku
LDI MujRegistr, LOW(RAMEND) ; dolní byt
OUT SPL,MujRegistr ; do zásobníku
Hodnota RAMEND je daná tím kterým typem procesoru. Je definována v souboru
INCLUDE pro daný typ procesoru. Soubor m8515def.inc má řádek:
.equ RAMEND =$25F ; poslední místo v SRAM na čipu
Soubor m8515def.inc je připojen direktivou asembleru
.INCLUDE "C:\nejakacesta\m8515def.inc"
na začátku asemblerovského zdrojového kódu.
Takže nyní již máme definován zásobník, a dále se již nemusíme starat o manipulaci
s ukazatelem zásobníku, protože k ní dochází automaticky.
Použití zásobníku
Používání zásobníku je jednoduché. Obsah registrů je ukládán do zásobníku takto:
PUSH MujRegistr ; uložení hodnoty
Kam se tato hodnota uloží je nezajímavé. Ukazatel zásobníku byl dekrementován po
provedení push, takže s tím nemáme žádnou práci. Když potřebuje získat hodnotu ,
použijeme jenom následující instrukci:
Učební materiál ASM AVR
20
POP MujRegistr ; zpětné vyjmutí hodnoty
Použitím POP obdržíme hodnotu, která byla poslední uložena na vrchol zásobníku. Ukládání
a vyjímaní hodnot do/z zásobníku má smysl, když
Hodnoty budeme opět potřebovat po několika řádcích kódu,
Všechny registry jsou již použité,
Nemáme možnost je uložit jinde.
Nejsou-li tyto podmínky splněné, je použití zásobníku pro uchování obsahu registrů nevhodné
a náročné na čas procesoru.
Vhodnější je používání zásobníku v podprogramech, pro uložení návratové adresy
z podprogramu do programu, volajícímu podprogram. V tomto případě volající program
nejdříve uloží návratovou adresu (aktuální hodnotu programového čítače) do zásobníku a
provede skok do podprogramu. Po jeho provedení podprogram vyjme návratovou adresu ze
zásobníku a uloží tuto adresu zpět do programového čítače. Program pak pokračuje přesně
jednu instrukci za instrukcí volání podprogramu:
RCALL NejakyPodprogram ; skočí na návěští NejakyPodprogram
[...] zde pokračujeme v programu.
Skok na návěští NejakyPodprogram někde v programovém kódu,
NejakyPodprogram: ; toto je adresa skoku
[...] zde se něco dělá
[...] a po skončení chceme skočit nazpět do místa volání:
RET
Při provádění instrukce RCALL se vždy inkrementuje programový čítač, 16-bitová adresa
je uložena do zásobníku, použitím dvou instrukcí push. Po dosažení instrukce RET se
předchozí obsah programového čítače obnoví pomocí dvou instrukcí pop a provádění
programu pokračuje z tohoto místa.
Nemusíme se starat o adresu zásobníku, kam se ukládá obsah čítače. Tato adresa je
automaticky generována. S pomocí zásobníku dokonce můžeme dokonce v podprogramu
volat další podprogram. Přitom jsou uloženy dvě návratové adresy na vrcholu zásobníku,
vložený podprogram odstraňuje první, nejhořejší, adresu, volající podprogram tu zbývající.
Takto lze mít i několik úrovní volání, pokud ovšem máme dostatek volné SRAM .
Obsluha hardwarových přerušení není možná bez zásobníku. Přerušení zastaví normální
průběh programu v jeho libovolném stavu. Po provedení obslužného podprogramu jako
reakce na toto přerušení programu musí dojít k návratu do předchozí pozice, do stavu, ve
kterém byl program před přerušením. To by nebylo možné bez možnosti uchovávat
v zásobníku návratovou adresu.
Chyby při zásobníkových operacích
Učební materiál ASM AVR
21
Při práci s zásobníkem se může začátečník dopustit řady chyb.
Časté je používání zásobníku bez počátečního nastavení ukazatele zásobníku. Protože při
startu programu je tento ukazatel nastaven na nulu, ukazuje ukazatel na registr R0. Uložením
bytu do zásobníku se tento byt zapíše do tohoto registru, a přepíše tím předchozí obsah. Další
instrukce push zapisuje do 0xFFFF (pozn. 0x0000 – 0x0001), což je nedefinované místo
paměti (pokud nemáme externí SRAM ). Instrukce RCALL a RET budou vracet neznámé
adresy z programové paměti. Bohužel v tom případě assembler neposkytuje nějaké
upozornění.
Další možností vytvoření chyb je zapomenutí instrukce POP pro vyjmutí předtím uložení
hodnoty pomocí PUSH, nebo naopak použití POP bez předchozího PUSH.
Někdy může dojít i k přetečení (stack overflows) zásobníku dosažením prvního místa v
SRAM . K tomu dochází v případě nekonečných rekurzivních volání. Po dosažení nejnižší
adresy SRAM bude další push zapisovat do portů (0x005F až 0x0020), pak do registrů
(0x001F až 0x0000). Pak může dojít záhadným událostem na hardware chipu, dokonce
údajně ke zničení hardware!
Řízení chodu programu
Nyní si řekneme něco o instrukcích řídících provádění programu, pořadí prováděných
instrukcí. Začneme startem programu po připojení k napájení, skoky, přerušeními atd.
Reset
Po připojení k napájení AVR MCU začne pracovat jeho processor. Provede vynulování
hardwarových klopných obvodů. Programový čítač se nastaví na nulu. Na této adrese se vždy
začíná běh programu. Zde je i začátek programového kódu. Tato adresa je použita nejenom po
připojení k napájení:
1. Během přivedení externího signálu na reset pin se provede restart.
2. Když watchdogový čítač dosáhne své maximální hodnoty, spustí se reset. Čítač
watchdogu čítá interní hodinové pulzy a musí být čas od času programově vynulován,
jinak restartuje processor.
3. Můžete také provét přímý skok na počáteční adresu, není to však skutečný reset,
protože není provedeno automatické vymazání registrů a nastavení hodnot portů na
defaultní hodnoty
Pokud jde o watchdog reset, musí být nejdříve programově povolen. Defaultně není
povolen. Povolení vyžaduje zápis do watchdog portu. Vynulování watchdog čítače
vyžaduje provedení instrukce
WDR
aby nedošlo k resetu.
Učební materiál ASM AVR
22
Po provedení resetu, nastavení registrů a portů na defaultní hodnoty, je kód na adrese 0000
čten po slovech do výkonné části procesoru a je prováděn. Během tohoto provádění je
programový čítač vždy inkrementován o jedničku a další slovo kódu je načteno do
instrukčního bufferu. Když prováděná instrukce nevyžaduje skok na jiné místo
v programu, bude jako další instrukce prováděna následující instrukce v programovém
kódu. To je důvod velké rychlosti běhu AVR, při každém hodinovém cyklu se provede
jedna instrukce (pokud nedojde ke skoku).
První prováděná instrukce je vždy umístěná na adrese 0000. K tomu, abychom sdělili
překladači (assembleru) na jaké adrese začíná náš zdrojový, můžeme umístit na začátek
direktivu asembleru, napsanou před prvními instrukcemi v zdrojovém kódu:
.CSEG
.ORG 0000
První direktiva říká kompilátoru, aby zapnul kódovou sekci. Vše následující je přeloženo
jako kód a je zapsáno do programové paměti MCU. Dalším cílovým segmentem může být
EEPROM, do které lze také zapisovat byty nebo slova.
.ESEG
Třetím segmentem v MCU je SRAM sekce.
.DSEG
Direktiva ORG definuje základní adresu a pracuje s adresami v kódovém segmentu, do
něhož se umisťují přeložená slova. Když program vždy začíná na 0x0000 jsou direktivy
CSEG/ORG trivialní, můžeme vynechat tyto direktivy, aniž bychom udělali chybu.
Můžeme začít start na 0x0100, ale nemá to reálný význam. Když chceme umístit tabulku
přesně do jistého místa v kódovém segmentu, můžeme použít ORG. Když chceme, aby
byl zřejmý význam našeho kódu, po prvních definicích definujících řadu věcí pomocí
direktiv .DEF- a .EQU-, použijeme posloupnost CSEG/ORG, i když to není nutné.
První slovo kódu je vždy na adrese nula, toto umístění se nazývá reset vector. Po reset
vectoru jsou na následujících pozicích v programovém prostoru, adresy 0x0001, 0x0002
atd., jsou vektory přerušení. Toto jsou pozice, kde jsou umístěny skoky, když externí nebo
interní přerušení jsou povolena a nastanou. Tyto pozice nazývané vektory jsou specifické
pro každý typ MCU a závisí na vnitřním hardware. Instrukce reagující na taková přerušení
jsou umístěné do vhodných vektorových pozic. Když užíváme přerušení, první kód, v
resetovém vektoru, musí být příkaz skoku, aby přeskočil ostatní vektory. Každý vektor
přerušení musí obsahovat příkaz skoku do příslušné obsluhy přerušení. Typický začátek
programu je obdobný, jako:
Učební materiál ASM AVR
23
.CSEG
.ORG 0000
RJMP Start
RJMP IntServRout1
[...] zde lze umístit příkazy dalších vektorů přerušení
[...] a zde je místo pro obslužné podprogramy přerušení
Start: ; Tde je začátek (start) programu
[...] zde je místo pro náš hlavní (main) program
Výsledkem instrukce RJMP je skok na návěští Start:, umístěné o několik řádků později.
Návěští vždy začíná na prvním sloupci zdrojového kódu a je zakončen :. Návěští
nesplňující tyto podmínky neprojdou kompilátorem. Scházející návěští vyvolají chybovou
zprávu ("Undefined label"), a překlad je přerušen.
Lineární provádění programu a skoky
Provádění programu je vždy lineární, pokud něco nezmění sekvenční běh programu. Tyto
změny jsou způsobeny přerušeními nebo skokovými instrukcemi.
Skoky jsou často závislé na nějakých podmínkách, podmiňující skoky. Jako příklad
uvažujme, že chceme vytvořit 32-bitový čítač použitím registerů R1 až R4. Nejméně
významný byt v R1 je inkrementován o jedničku. Když dojde k přetečení registru během
operace (255 + 1 = 0), provedeme inkrementaci R2 . Když přeteče R2 , inkrementujeme
R3, atd
.
Inkrementace o jedničku se provádí instruckcí INC. Když dojde k přetečení během provádění
instrukce INC R1 , nastaví se příznak nuly ZERO ve stavovém registru na jedničku
(výsledkem operace je nula). Bit přenosu ve stavovém registru, obvykle nastavený při
přetečení, se nemění během INC. Bit přenosu se používá pro jiné účely. K indikaci přetečení
postačuje použití příznaku nuly. Pokud nedojde k přetečení, může už jen dojít k opuštění této
části programu.
Když je nastaven bit,příznak nuly, musíme provést inkrementaci v dalších registrech. Je
poněkud matoucí, že instrukce které využívá tento příznak se nejmenuje BRNZ (BRanch if
Not Zero = skok,když není nula) ale jmenuje se BRNE (BRanch if Not Equal = skok, když
není rovnost).
Celá část programu realizující 32-bitový čítač vypadá např. takto:
INC R1
BRNE KonecCitani
INC R2
BRNE KonecCitani
INC R3
BRNE KonecCitani
Učební materiál ASM AVR
24
INC R4
KonecCitani:
Opačnou instrukcí k BRNE je BREQ - BRanch EQual = skok při rovnosti.
Který ze stavových bitů, příznaků, se změní při provádění nějaké AVR instrukce je popsáno
v seznamu instrukcí, podrobně popsaných v materiálu o HW AVR. Obdobně jako příznak
nuly, se využívají i další příznaky, bity stavového registru. Např.:
BRCC/BRCS ; příznak přenosu je 0 / 1
BRSH ; rovný nebo větší
BRLO ; menší
BRMI ; záporný
BRPL ; kladný
BRGE ; větší nebo rovný (s znaménkovým bitem)
BRLT ; menší (s znaménkovým bitem)
BRHC/BRHS ; příznak přetečení mezi 3 a 4 bitem (užití při BCD) 0 / 1
BRTC/BRTS ; T-Bit 0 nebo 1
BRVC/BRVS ; při přetečení aritmetické operace 0 / 1
BRIE/BRID ; přerušení povoleno nebo zakázáno
reagují na různé podmínky. Ke skoku dojde při splnění příslušné podmínky. Většina těchto
instrukcí se v praxi moc nepoužívá, obvykle vystačíme s instrukcemi závislými na příznaku
nuly a příznaku přenosu.
Časování při běhu programu
Zmínili jsme se, že u AVR MCU je čas potřebný na provedení jedné instrukce rovný jednomu
hodinovému cyklu. Např. je-li kmitočet krystalu 10 MHz, je tento čas pouhých 100 ns. Toho
lze využít, potřebujeme-li pomocí AVR provádět nějaká časování. Pozn. v seznamu instrukcí
vidíme, že jen několik málo instrukcí potřebuje dvě a více hodinových cyklů, např. skokové
instrukce (např. když nastane skok) nebo čtení/zápis do SRAM .
K vytvoření přesného časování je je nejvhodnější použití instrukce NOP, umožňující
vytvoření časové prodlevy při běhu programu. Tato instrukce nic nedělá - No OPeration
NOP. Je to docela užitečná instrukce:
NOP
Tato instrukce nic nedělá, ale spotřebovává čas procesoru. Např. při použití 4 MHz
systémových hodin, potřebujeme čtyři instrukce NOP k vytvoření prodlevy 1 µs. Pro signální
generátor s frekvencí 1 kHz ale nemusíme vkládat 4000 takových instrukcí do zdrojového
kódu, ale použijeme softwarový čítač a několik instrukcí skoku. Pomocí těchto instrukcí
vytvoříme smyčku, kterou proběhne program určitým, přesným počtem krát a tím vytvoří
přesné zpoždění.Čítačem může být 8-bitový registr, který se dekrementuje pomocí instrukce
DEC, např. :
Učební materiál ASM AVR
25
CLR R1
Count:
DEC R1
BRNE Count
16-bitové čítání může být použito k vytvoření přesného zpoždění, např. takto
LDI ZH,HIGH(65535)
LDI ZL,LOW(65535)
Count:
SBIW ZL,1
BRNE Count
Můžeme použít i několik registrů k naprogramování čítačů do sebe vložených (nested) a tím
docílit libovolný časový úsek, zpoždění, které je přitom přesné i bez použití hardwarového
čítače.
Makra a běh programu
Často se stává, že v zdrojovém kódu je několik stejných, nebo podobných, posloupností
instrukcí.Pokud se chceme vyhnout uvádění stejného kódu v zdrojovém kódu několikrát,
můžeme opakující se kód napsat jako podprogram a ten využívat pomocí skoků z hlavního
programu. Další možností je využití makra. Makra jsou posloupnosti instrukcí, napsané a
otestované jednou (tělo makra, definice makra), a vložené do kódu (rozvoj makra) v místech,
do kterých zapíšeme jméno makra. Jako příklad uvažujme program, ve kterém budeme
generovat několikrát zpoždění 1 µs z 4 MHz systémových hodin (krystalu). Pak definujeme
makro kódem:
.MACRO Delay1mikrosekunda
NOP
NOP
NOP
NOP
.ENDMACRO
Tato definice makra se nepřekládá do nějakého kódu, kód je vytvářen překladačem až
v místech uvedení jména makra (rozvoje makra):
[...] nějaký zdrojový kód
Delay1mikrosekunda ;rozvoj makra
[...] další zdrojový kód
Výsledkem jsou čtyři instrukce NOP vložené do zdrojového kódu v místě rozvoje makra.
Další additional Delay1mikrosekunda vloží další čtyři instrukce NOP .
Makra můžeme vytvářet i s parametry s jejichž pomocí pak lze měnit kód v místech rozvoje
Učební materiál ASM AVR
26
makra.
Je dobré si uvědomit jednu věc: každý rozvoj makra zabírá další část programové paměti. Je-li
kód definice makra příliš dlouhý je proto lepší (pro ušetření programové pamětu) nahradit
používání makra použitím podprogramů.
Podprogramy
Na rozdíl od definice maker zabírají podprogramy po překladu paměťový prostor programové
paměti. Příslušná posloupnost instrukcí je v kódu uvedena jen jednou a je volána z jiných
částí kódu. Pro pokračování programu v místě následujícím za voláním podprogramu je
potřeba v podprogramu zabezpečit návrat (return) z podprogramu. Pro vytvoření zpoždění 10
cyklů se dá napsat takovýto podprogram:
Delay10:
NOP
NOP
NOP
NOP
RET
Podprogram vždy začíná návěštím , v našem příkladu Delay10:, aby při volání podprogramu
bylo definované místo skoku. Dále následují tři instrukce NOPs a instrukce RET. Začátečník
by možná použil 10 instrukcí NOP. Je však nutné si uvědomit, že určitý čas procesoru
spotřebovává i instrukce RET a volající program. Takže 3 cykly spotřebují instrukce NOP, 4
instrukce RET. Zbývající 3 spotřebuje volající program:
[...] nějaký kód
RCALL Delay10
[...] další zdrojový kód
RCALL je relativní volání. Toto volání je zakódované jako relativní skok, odskok – relativní
vzdálenost od volajícího programu spočítá překladač. Instrukce RET provede skok nazpět do
volajícího programu. Před voláním podprogramu je nutné nastavit ukazatel zásobníku,
protože do zásobníku musí instrukce RCALL uložit návratovou adresu.
Lze také provádět skok přímo do nějakého místa v kódu za použití instrukce skoku:
[...] nějaký zdrojový kód
RJMP Delay10
Return:
[...] další místo v kódu
Kód, do něhož se provádí odskok, nemůže obsahovat instrukci RET. Návrat nazpět do
Učební materiál ASM AVR
27
volajícího místa vyžaduje přidat další návěští a provést skok nazpět k tomuto návěští.
Takovéto provádění skoků však není stejné jako volání podprogramu, protože se nedá volat
tento kód z různých míst kódu.
RCALL a RJMP jsou nepodm
Vloženo: 17.05.2009
Velikost: 1,19 MB
Komentáře
Tento materiál neobsahuje žádné komentáře.
Copyright 2025 unium.cz


