Mikrokontroléry rady AVR – prerušenia

V ďalšom pokračovaní tohto seriálu by som sa chcel venovať prerušeniam a ich problematike a nastaveniam.
Každý program, ktorý programujeme by nemal pracovať iba správne ale mal by aj efektívne využívať prostriedky a takisto je dôležitý aj čas.

Čo sú to vlastne prerušenia?

V predchádzajúcom článku som ako príklad uviedol tzv. kitt na ledkách. Medzi prepínaním lediek sa muselo počkať určitý čas ak som chcel aby celý efekt blikania bol pomalší. Vyriešené to bolo tzv. časovou slučkou keď procesor bol zaneprázdnený nejakým inkrementovaním registrov. No takto riešený program nie je príliš efektívny. Možno by bolo lepšie ak by počas čakania vykonával niečo užitočnejšie ako len inkrementovať registre. Na to práve slúži jednotka čítača/časovača a jej dôležitá vlastnosť prerušiť beh programu keď príde ten správny čas :). A takisto sa prerušeniami dá krajšie spraviť program aj na to tlačítko. Tu nám poslúžia externé prerušenia kde sa beh programu preruší na zmenu logickej úrovne pinu mikrokontroléra.

Jednoducho prerušenia prerušia beh programu v hlavnej slučke, presmerujú ho na určitú obslužnú rutinu, ktorá sa vykoná a po jej vykonaní sa vráti späť na miesto kde sa program prerušil. Čo ak príde od viacerých zdrojov požiadavka na prerušenie? Takýto prípad je vyriešený prioritou prerušení t.j. ak príde požiadavka na prerušenie od zdroja s vyššou prioritou tak sa najprv obslúži tá a až potom požiadavka s nižšou prioritou.

AVR a prerušenia

MCU rady AVR disponujú niekoľkými zdrojmi prerušenia a sú rozdelené v tabuľke podľa priority. Každé prerušenie má svoj vektor prerušenia t.j. má pridelenú adresu a ak príde požiadavka na prerušenie tak sa „skáče“ na príslušnú adresu prerušenia a na tejto adrese v programe sa musí nachádzať obslužná rutina, ktorá sa má vykonať.

obr1

Obr.1 Vektory prerušení a priorita prerušení u MCU ATMega32 (všimnite si poznámku pod tabuľkou)

Vektory prerušení (ich adresy) je dobré poznať najmä pri programovaní v assembleri, kde na príslušnú adresu (vektor) zapíšeme inštrukciu skoku, ktorá skáče na miesto v programe s obslužnou rutinou. V C-čku je to o čosi jednoduchšie.

Nastavenie vektora ext. prerušenia INT0 v ASM def. konštantou a direktívou .ORG :

.NOLIST
	.INCLUDE "m32def.inc"
	.LIST

	.CSEG

	.ORG 0
	RJMP START		// reset vector - zaciatok

	.ORG INT0Addr		// nastavenie adresy pomocou konstanty a direktívy .ORG
	RJMP INT0_ROUTINE	// sem skoci program pri preruseni INT0

START:
	// inicializacia prerusenia - nastavenie v/v registrov

	SEI  // povolenie preruseni

LOOP:
	// program - beh programu
	RJMP LOOP

INT0_ROUTINE:

	// obsluzna rutina

	RETI //instrukcia pre návrat z rutiny

obr1a

Obr.2 Konštanty vektorov prerušení pre ATMega32 používané v ASM

Nastavenie vektorov ak je viac prerušení v ASM:

.nolist
.include "m32def.inc"
.list

; reset vektor na adrese 0

.org 0
rjmp start

; vektory prerušení

rjmp int0	;prerušenie od INT0	- použije sa
reti		;prerušenie od INT1
reti		;prerušenie od INT2
reti		;prerušenie od OC2
reti		;prerušenie od OVF2
reti		;prerušenie od ICP1
reti		;prerušenie od OC1A
reti		;prerušenie od OC1B
rjmp ovf1	;prerušenie od OVF1  	- použije sa
reti;

; .. z ďalších prerušení už nevyužijeme nič a tak nemusíme
; pokračovať s reti

int0:
	in	r16,sreg
	push r16		;uchovanie obsahu registra SREG do zasobnika

	; obslužná rutina pre INT0

	pop	r16
	out sreg,r16		;obnovenie obsahu SREG zo zásobníka
	reti

ovf1:
	in	r16,sreg
	push r16		;uchovanie obsahu registra SREG do zasobnika

	; obslužná rutina pre OVF1

	pop	r16
	out sreg,r16		;obnovenie obsahu SREG zo zásobníka
	reti

start:
	ldi	temp,low(ramend)  ;nastavenie ukazatela na zasobnik na koniec SRAM
	out	spl,temp	  ;zasobnik sa vyuziva na ulozenie obsahu PCcountera
	ldi	temp,high(ramend) ;pri volaniach podprogramov (RCALL)
	out	sreg,temp	  ;a takisto skokov na rutiny prerusenia prip. push, pop

	; inicializácia prerušení a programu
	sei			;povolenie prerušení - zakázanie cez inštrukciu cli

loop:
	; beh programu
	rjmp	loop

Nastavenie vektora ext. prerušenia v C:

#include <avr/io.h>
#include <avr/interrupt.h>

int main()
{
	//sem sa zapíše inicializácia prerušení
	//od určitého zdroja

	sei(); //povolenie prerušení - zakázať sa dá funkciou cli();

	while(1)
	{
		//vykonávaný program
	}

	return 0;
}

ISR (vektor)  //obslužná rutina prerušenia
{

}

Konštanty pre vektory prerušení v jazyku C:

INT0_vect
INT1_vect
INT2_vect
TIMER2_COMP_vect
TIMER2_OVF_vect
TIMER1_CAPT_vect
TIMER1_COMPA_vect
TIMER1_COMPB_vect
TIMER1_OVF_vect
TIMER0_COMP_vect
TIMER0_OVF_vect
SPI_STC_vect
USART_RXC_vect
USART_UDRE_vect
USART_TXC_vect
ADC_vect
EE_RDY_vect
ANA_COMP_vect
TWI_vect
SPM_RDY_vect

V každej rutine obsluhy prerušenia by sa mal uchovať obsah registra SREG (najčastejšie do zásobníka, ktorého adresa sa nastaví na koniec SRAM) a pred návratom do programu znovu obnoviť na pôvodnú hodnotu (pozri zdrojak 2 hore). V C-čku to za nás urobí kompilátor takže sa nemusíme o nič starať. Všetky nastavené prerušenia je potrebné povoliť (tzv. globálne) zápisom bitu I do registra SREG. V ASM cez inštrukciu SEI (CLI naopak, zakáže prerušenia) a v C-čku na to slúži knižnica avr/interrupt.h a jej funkcie sei() a cli().

Externé prerušenia

Často používaným prerušením sú extérne prerušenia. Ak potrebujeme vykonať nejakú časť programu pri inom prerušení ako od zabudovaných periférií v MCU napr. najčastejšie tlačidlo, môžeme využiť práve externé prerušenie.

K tomuto prerušeniu dochádza pri zmene logickej úrovne na určitom pine mikrokontroléra. ATMega32 obsahuje tri takéto zdroje prerušení:

a) INT0 – pin PD2
b) INT1 – pin PD3
c) INT2 – pin PB2

MCU môže reagovať na štyri stavy na v/v pine:

a) na log 0
b) ľubovoľnú zmenu úrovne
c) dobežnú hranu signálu
d) nábežnú hranu signálu

Zdroj extérneho prerušenia si zvolíme zápisom log1 na príslušné miesto do v/v registra GICR.

obr3

Obr.3 Register GICR

A reakciu na zmenu stavu nastavíme v registri MCUCR (pre INT0 a INT1):

obr4

Obr.4 Register MCUCR

Nastavenie bitov:

obr5

obr5a

Obr.5 Nastavenie typu prerušenia od INT

Reakcia na zmenu pre INT2 sa nastavuje v registri MCUCSR kde si môžeme vybrať iba medzi nábežnou hranou – log1 alebo dobežnou hranou – log0:

obr6

Obr.6 Register MCUCSR

Pritom musíme ešte nastaviť príslušný pin ako vstupný (DDRx register) a aj na príslušnú logickú úroveň.

Funkcia tlačidla sa realizuje formou prerušenia na dobežnú hranu. Na vstup je trvale cez pull-up odpor privedená logická jednotka. Po stlačení tlačidla sa vstupný pin pripojí na zem čím sa vykoná prechod z vysokej do nízkej úrovne – dobežná hrana – falling edge. Pull-up rezistor zabezpečuje aby sa po pripnutí vstupného pinu na zem nedosiahol skrat, ale prúd sa rezistorom obmedzí.

Príklady

Prikladám na stiahnutie upravené projekty z minulej časti s použitím prerušenia od čítača/časovača a externého prerušenia. Treba nastaviť vnútorný oscilátor na 2MHz. To ako pracuje časovač vás ešte nemusí trápiť. Porozprávam o tom v najbližších dieloch tohto seriálu :)

Download kitt_int
Download tlacidlo_int

 

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *

Môžete použiť tieto HTML značky a atribúty: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>