Čtvrtá část pokračování stránek o základech programování. V tomto pokračování si povíme o tom, co je to přerušení, k čemu slouží a jak pracuje. Vím, bylo slíbené indirect (nepřímé) adresování, ale pro rozumný a funkční příklad musíme nejprve zvládnout přerušení. A samozřejmě přidáme jako obvykle nějakou tu instrukci.

      Takže přerušení, co je ten často zmiňovaný tajemný termín ? Je nejprve dobré, být vyzbrojen alespoň částečnými základy, například ze stránky o vlastnostech přerušení. Pro další objasnění se jako obvykle vrátíme do kuchyně . Zapomeňte nyní na to, co víte o procesorech.





      Představte si, že cosi vaříte na sporáku. Tedy vykonáváte program. Náhle zazvoní telefon. Co uděláte ? Přerušíte vaření a jdete zvednout sluchátko - vida přerušení. Nyní provádíte obsluhu přerušení - telefonujete. Hlavní program (vaření) byl přerušen zvoněním telefonu. Pokud je tohle jasné, budeme pokračovat. Procesor x51 má dvě úrovně priority přerušení - vyšší a nižší. Jaký je v tom rozdíl ? Opět se vraťme k příkladu do okamžiku, kdy telefonujete a jídlo je stále na sporáku. Jídlo se ale začne pálit. Co uděláte ? Odložíte sluchátko (přerušíte hovor) a jdete sundat kastrol ze sporáku. V počítačové mluvě jste tedy přerušili přerušení - resp. došlo k přerušení vykonávání obsluhy prvního přerušení (zvonění telefonu) důležitější činností (sundat hrnec).
     Po vykonání procedury "člověče sundej rychle ten kastrol" se vrátíme (z přerušení s vyšší prioritou) k dokončení původního přerušení (telefonického hovoru). Po dokončení obsluhy tohoto přerušení zavěsíme - obsluha tohoto přerušení ukončena - a vrátíme se do hlavního programu - tedy vrátíme kastrol na sporák, popadneme vařečku a budeme dál cosi kvedlat.

      Pokud to tedy ještě jednou zopakuji, pak přerušení hlavního programu je způsobeno nějakou událostí, kterou je nutno obsloužit. Přerušení může být vyvoláno nějakou událostí na zvoleném pinu procesoru (samozřejmě určeném výrobcem) nebo může být způsobeno příchodem znaku po sériové lince, a nebo třeba přetečením vnitřního časovače. Po dokončení obsluhy se procesor vrací do hlavního programu na místo, kde svoji práci přerušil. (nebudu zde zatím rozebírat podrobnosti) Jak jsem již zmínil, x51 má dvě úrovně priority přerušení. Nižší a vyšší. Jde v podstatě o to, že pokud probíhá obsluha přerušení s nižší prioritou (telefonování) může nastat událost, která vyvolá přerušení s vyšší prioritou (pálí se jídlo). V tomto okamžiku je, jak jsem již uvedl, zastavena obsluha přerušení s nižší prioritou a následuje skok na obsluhu přerušení s prioritou vyšší.
      No dobře, ale co se stane, pokud při obsluze přerušení s nižší prioritou (telefonování) přijde jiné přerušení, které má stejnou prioritu ? Pro příklad zvolme opět stav, kdy vaříme, zazvoní telefon, přerušíme vaření a telefonujeme. Najednou přiběhnou děti, že jim mám jít zkontrolovat domácí úkoly. Protože to nemá vyšší prioritu než můj telefonní hovor, zapamatuji si to (analogie - procesor si nastaví odpovídající příznak) a pokračuji v telefonním hovoru. Po dokončení hovoru se nevrátím do kuchyně k hrnci, ale následuje "obsloužení události" - tedy kontrola úkolů ratolestí. Po dokončení kontroly se vrátím do kuchyně - tedy do vykonávání hlavního programu.

     Nyní se vrátíme zpět k vlastnímu procesoru. Vlastní průběh přerušení musí mít nejaký řád. Funguje to tak, že pokud máme povolené to které přerušení (povoluje se v určitém registru určitým bitem), pak pokud toto přerušení nastane, je nastaven odpovídající příznak. Následně se po dokončení právě prováděné instrukce vyvolá přerušení. U x51 má každé přerušení svoji adresu, na kterou procesor skočí právě po vyvolání přerušení.
     Procesor po dokončení instrukce nejprve uloží adresu následující instrukce do zásobníku. Musí. Proč ? Je to proto, že procesor si musí "pamatovat" kde skončil - tedy lépe řečeno, kde má po návratu z přerušení pokračovat. Následně provede skok na výrobcem pevně danou adresu přiřazenou tomu kterému přerušení (více zde). Protože adresy (vektory) přerušení jsou u sebe celkem blízko, většinou se na tuto adresu pouze vloží instrukce skoku, která převede řízení na obslužnou rutinu přerušení. Příznak, který přerušení vyvolal je většinou (kromě příznaků od sériové linky) hardwarově vynulován (platí pro x51). Po dokončení přerušení program instrukcí RETI vrátíme na vykonávání hlavního programu. V tomto okamžiku je ze zásobníku vybrána uložená adresa (je to adresa následující instrukce v hlavním programu, před kterou bylo přerušení vyvoláno, pamatujete ?) a provede se na ni skok. Tím je obsluha ukončena.
     Až tohle přejde do krve, je nutné si uvědomit ještě několik souvisejících a podstatných věcí. Pokud v obsluze přerušení pracujete s akumulátorem (a že se s ním pracuje téměř vždy) MUSÍTE řešit jednu věc a tou je uschování jeho hodnoty při vstupu do přerušení. Totiž představte si, ze jste v hlavnim programu někde v polovině výpočtu a přijde přerušení. V obsluze přerušení budete potřebovat vypočítat něco jiného a pokud použijete akumulátor, pak v něm při návratu z přerušení bude naprosto jiná hodnota, než jaká tam původně byla. Čili pak bude onen načatý a přerušením přerušený výpočet pokračovat s úplně jiným číslem a Vy máte zaděláno na velmi slušný problém, neboť podle toho, jak přerušení nastane (kdy) bude program fungovat / nefungovat. A to se opravdu velmi dobře hledá. Řešení je naprosto jednoduché. Prostě vždy po vstupu do obsluhy přerušení si do zásobníku uložíme obsah jak akumulátoru, tak stavového slova (stavové slovo obsahuje informace o přetečení, nulovosti apod. a přepsání údajů platí jako pro výše uvedený akumulátor). Ukládání do zásobníku zajišťuje instrukce PUSH, vyjmutí zpět instrukce POP.
     Pamatujte, že musíte vybírat data v obráceném pořadí, než v jakém jste je ukládali. Data se v zásobníku ukládají postupně za sebou, tedy ta, která jsou uložena jako první se vybírají jako poslední !!

Pro názornost připojme příklad, jak to vypadá v praxi:
;************Obsluha preruseni***********************

prerus_t0:    	push	psw			;uschovej stavove slovo programu >---
		push	acc			;uschovej akumulator >--	     |
		.							|	     |
     		.							|	     |			
		;obsluha preruseni					|            |
		.							|            |
		.							|            |
konprerus:	pop	acc			;obnov akumulator  <----	     |
		pop	psw			;obnov stavove slovo programu <------
		reti				;navrat z preruseni

No co dodat ? Snad že je prozatím konec suchopárného vysvětlování, které ale bohužel předcházet musí a přejděme k příkladu.

     Vrátíme se k vůbec prvnímu příkladu, a tím bylo blikání LED diodou. Schéma ponecháme beze změny, ale změníme program. LED diodou nebude blikat hlavní smyčka programu, ale podprogram v přerušení, tím pádem bude moci procesor v hlavní smyčce vykonávat nějakou jinou činnost.
Popis programu

$MOD52
$TITLE(BYTE SIGNED MULTIPLY)
$PAGEWIDTH(132)
$DEBUG
$OBJECT
$NOPAGING

org 00
AJMP start			;cold START

org 	000Bh			;preruseni TMR0
jmp 	prerus_t0


;************ Zacatek programu ***********************

prerus_t0:    	push	psw		;uschovej pouzivane
		push	acc

     		mov TH0,#03CH		;po preteceni nova hodnota
		mov TL0,#0B0H

		djnz	pocitadlo,konprerus	;sniz pocitadlo preteceni
		mov	pocitadlo,#0AH		
		cpl	LED


konprerus:	pop	acc		;obnov pouzita v preruseni
		pop	psw	
		reti

;**********************************************
bseg at 30
LED	bit P1.2
pocitadlo	equ	30H

;**********************************************
;          H L A V N I   S M Y C K A
;**********************************************
                        cseg at 60h
start:
	mov  	IE,#10000010b        ; preruseni od t0 a globalne
        mov  	TMOD,#00000001b      ; t0 - 16bit s predvolbou
      	mov TH0,#03CH		     ;predvolba 15536 horni byte
	mov TL0,#0B0H		     ;predvolba casovace dolni byte	
	
	mov	pocitadlo,#0AH

	setb	tr0		;spust casovac               	      

	jmp	$

 END
       Nejprve klasická hlavička. Následuje direktiva ORG00, která označuje nultou pozici paměti - zde procesor po resetu začíná pracovat. Na této direktivě máme skok na vlastní skutečný start programu (AJMP start). Na adrese 00BH je skok na adresu, na kterou procesor skočí, pokud je generováno přerušení přetečením vnitřního čítače / časovače 0 procesoru. O těchto adresách naleznete více zde.
     Dále máme pomocí registru TMOD nakonfigurován časovač 0 jako 16 bitový, který má jako zdroj hodin jádro procesoru (může čítat i pulsy z vnějšího pinu, ale o tom až jindy). Je inkrementovaný každým strojovým taktem procesoru. To znamená, že se do časovače přičte jednička každou mikrosekundu (při taktování procesoru 12MHz). K přetečení 16 bitového čítače tedy dojde po 65535 cyklech - tedy po 65,535 msec. (milisekundách). Dále máme v registru IE povoleno vyvolání přerušení po přetečení tohoto časovače a máme povoleno přerušení jako takové vůbec (bit EA).
A jak to tedy pracuje ? Chceme blikat LED diodou po 0,5 sekundě (0,5 sec. svítí, 0,5 tma). To znamená, načítat 500 milisekund, změnit stav a opakovat. Protože ale časovač může čítat do max. 65535 (65 msec.) a pak přeteče do 00000, musíme si pomoci jinak. Nastavíme si časovač vždy tak, aby načítal 50 milisekund (50 000). Pak přeteče a vyvolá přerušení. My si vezmeme jeden byte RAM označený jako pocitadlo, které nám bude čítat počet přetečení hardwarového časovače (resp. bude počítat počet přerušení). My si tedy nastavíme hodnotu do časovače (registry časovače jsou přístupné i pro zápis) tak, aby k jeho přetečení došlo po 50 msec. Hodnota tedy bude 65536 usec. - 50000 usec. = 15536 usec (3CB0 v hexa). Jestliže tedy tuto hodnotu zadáme do časovače pak k přetečení dojde po 50 msec (počítá od 15536 nahoru, po 65536 se nuluje a volá přerušení).
Jestliže dojde k přetečení, je vyvoláno přerušení. Tedy následuje skok na adresu 00BH. Tam je řízení předáno na adresu prerus_t0. Zde se nejprve uloží stav akumulátoru a stavové slovo programu do zásobníku. Dále se znova zadá hodnota 15536 do časovače (jinak by čítal od 00 00) a snížíme o jednu hodnotu softwarového počitadla - a zároveň porovnáme, zda bylo dosaženo nuly (DJNZ). Pokud ne, následuje skok na konec přerušení (konprerus) kde se vybere zpět stavové slovo a hodnota akumulátoru před přerušením.
     Pokud bylo dosaženo nuly (desátý vstup do přerušení) zadá se do tohoto počitadla nový stav (opět 10 čili 0AH) a změní se stav LED pomocí instrukce CPL (neguj). A následuje opět výstup přerušení (tedy přes výběr ACC a PSW). A protože nic jiného nemáme na práci, program v hlavní smyčce neustále běhá kolem dokola na instrukci jmp $ (skok na sebe sama), samozřejmě jinak může řešit cokoliv jiného, jen je z této činnosti vždy po 50 msec. "vyrušen" obsluhou časovače. Samozřejmě, že časovač je periferie, běžící naprosto samostatně a nezávisle, mimo jádro procesoru. Jinak v tomto příkladu by se samozřejmě ACC nemusel uschovávat v přerušení, neboť se s ním nepracuje, ale je dobré si na to zvyknout a dělat odkládání automaticky, člověk si pak ušetří horké chvilky.


      Mám pocit, že to pro dnešek stačí. Pokud byste chtěli přesto něco sami a aktivně vyzkoušet, můžete si schválně zkusit upravit program tak, aby blikal LED diodou ne po 0,5 sekundy, ale po jedné sekundě. Jak ? To je právě to, co musíte vymyslet.      Nápověda.



Ke stažení jsou tyto soubory:


Velikost 10 kByte  Příklad - ASM i HEX soubor a schéma


Doufám, že tato dnešní kapitola byla k pochopení. Pokud něčemu nerozumíte, podívejte se po odkazech v textu a nebo i samozřejmě jinde na internetu. Příště se již dostaneme na nepřímé adresování v paměti a na zobrazení výsledků na multiplexovaném LED sedmisegmentovém displeji.
     Děkuji za pozornost a kladné ohlasy na tento miniseriál.


Vytisknout stránku

Zpátky Zpátky
© DH servis 2002 -