Prerušenia: Rozdiel medzi revíziami
Zo stránky SensorWiki
Vytvorená stránka „ === Časovač T1 === Pozri AVR ExampleT1pooled.c '''Úlohy''' # Program doplňte podľa pokynov asistenta a odsimulujte.<BR>Pozn.: Treba vypnúť optimalizáci…“ |
Bez shrnutí editace |
||
(22 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 1: | Riadok 1: | ||
=== Prerušenia v AVR-GCC === | === Prerušenia v AVR-GCC === | ||
Riadok 33: | Riadok 22: | ||
External Interrupt Request 1 INT1_vect | External Interrupt Request 1 INT1_vect | ||
External Interrupt Request 2 INT2_vect | External Interrupt Request 2 INT2_vect | ||
Pin Change Group 0 Interrupt PCINT0_vect | |||
Pin Change Group 1 Interrupt PCINT1_vect | |||
Pin Change Group 2 Interrupt PCINT2_vect | |||
Timer/Counter1 Overflow TIMER1_OVF_vect | Timer/Counter1 Overflow TIMER1_OVF_vect | ||
USART, Rx Complete USART_RXC_vect | USART, Rx Complete USART_RXC_vect | ||
USART, Tx Complete USART_TXC_vect | USART, Tx Complete USART_TXC_vect | ||
Ak potrebujete prerušenia povoliť, resp. zakázať, máte k dispozícii funkcie | Ak potrebujete prerušenia povoliť, resp. zakázať, máte k dispozícii funkcie | ||
Riadok 45: | Riadok 36: | ||
Obe funkcie sa preložia do jedinej asm inštrukcie, bez zbytočného pridaného kódu. | Obe funkcie sa preložia do jedinej asm inštrukcie, bez zbytočného pridaného kódu. | ||
''' | == Prerušenie pri zmene stavu niektorého pinu procesora == | ||
<!-- Prerušenie INT0/1 vyvolané priamo na vstupoch PD2/PD3 žiaľ nemôžeme použiť, pretože sme ich obsadili LCD displejom. --> | |||
Na cvičení použijeme prerušenie vyvolané zmenou (log.0 -> 1 aj log. 1 -> 0) na vstupe PD5. Tomuto vstupu je priradené prerušenie PCINT21, ktoré patrí do skupiny 2. Na povolenie tohto prerušenia musíme okrem globálneho bitu <code>SREG: I</code> nastaviť aj bity <code>PCICR: PCIE2</code> a <code>PCMSK2: PCINT21</code>. | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_PreruseniePCINT.png]]<BR> | |||
''Schéma prerušovacieho systému externých prerušení.'' | |||
</div> | |||
Nižšie máte uvedený ako príklad program pre ovládanie LED diódy na výstupe PD7 tlačidlom na PD5. Podobný program sme už robili na druhom cvičení. Upravený program nastaví stav LED diódy poľa tlačidla v obsluhe prerušenia, takže v hlavnej programovej slučke už nič neostalo. | |||
<tabs> | |||
<tab name="a) bez prerušenia"><source lang="c++" style="background: LightYellow;"> | |||
#include <avr/io.h> | |||
/* Pripojenie periferii k vyvojovej doske Arduino: */ | |||
#define LED2 PD7 // externa LED dioda | |||
#define SW2 PD5 // externe tlacitko | |||
#define SW2_ON bit_is_clear(PIND, SW2) | |||
#define LED2_ON set_bit(PORTD,LED2) | |||
#define LED2_OFF clear_bit(PORTD,LED2) | |||
int main(void) | |||
{ | |||
/* SETUP */ | |||
/* Konfiguracia I/O: portD.7 je vystupny (LED2) a portD.5 je vstup (SW2) * | |||
* naviac je PortD.5 so zapnutym pull-up rezistorom cez reg. PORTD */ | |||
DDRD = 0b11011111; // PORTD: LED2 na PD7 je output, SW2 (PD5) input | |||
PORTD = 0b00100000; // LED Active low, LED off, pull-up ON | |||
/* LOOP */ | |||
while(1) | |||
{ | |||
if ( SW2_ON ) | |||
LED2_ON; | |||
else | |||
LED2_OFF; | |||
} | |||
return(0); | |||
} | |||
</source> | |||
</tab> | |||
<tab name="b) s prerušením"><source lang="c++" style="background: LightYellow;" highlight="2,13-19,34-39,45" line> | |||
#include <avr/io.h> | |||
#include <avr/interrupt.h> | |||
/* Pripojenie periferii k vyvojovej doske Arduino: */ | |||
#define LED2 PD7 // externa LED dioda | |||
#define SW2 PD5 // externe tlacitko | |||
#define SW2_ON bit_is_clear(PIND, SW2) | |||
#define LED2_ON set_bit(PORTD,LED2) | |||
#define LED2_OFF clear_bit(PORTD,LED2) | |||
ISR (PCINT2_vect) | |||
{ | |||
if ( SW2_ON ) | |||
LED2_ON; | |||
else | |||
LED2_OFF; | |||
} | |||
int main(void) | |||
{ | |||
/* SETUP */ | |||
/* Konfiguracia I/O: portD.7 je vystupny (LED2) a portD.5 je vstup (SW2) * | |||
* naviac je PortD.5 so zapnutym pull-up rezistorom cez reg. PORTD */ | |||
DDRD = 0b11011111; // PORTD: LED2 na PD7 je output, SW2 (PD5) input | |||
PORTD = 0b00100000; // LED Active low, LED off, pull-up ON | |||
/* ******* Konfiguracia prerusovacieho systemu **************** */ | |||
PCMSK2 |= (1<<PCINT21); | |||
PCICR |= (1<<PCIE2); | |||
sei(); // t.j. SREG |= (1<<I); | |||
/* LOOP */ | |||
while(1) | |||
{ | |||
asm("nop"); | |||
} | |||
return(0); | |||
} | |||
</source> | |||
</tab> | |||
</tabs> | |||
=== Časovač T1 === | |||
S časovačom T1 sme pracovali na minulom cvičení. Príznak pretečenia počítadla TOV1 sme testovali ''"ručne"'', metódou tzv. ''pooling'' - dopytovania. Je to neefektívny spôsob, pretože procesor využívame len na otrocké testovanie jedného bitu stále dookola. Preto je vašou úlohou doplniť vzorový (avšak nekompletný) program tak, aby sa pri pretečení T1 po jednej sekunde vyvolalo prerušenie a zmena stavu LED diódy bude tiež vykonaná v obsluhe prerušenia. | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_InterruptT1.png|700px]]<BR> | |||
''Pri povolení prerušenia nezabudnite okrem samotného prerušenia povoliť aj globálny príznak...'' | |||
</div> | |||
<tabs> | |||
<tab name="bez prerušenia"><source lang="c++" style="background: LightYellow;"> | |||
#include <avr/io.h> | |||
#define SW1 PD5 | |||
#define LED1 PB5 | |||
int main(void) | |||
{ | |||
DDRB = (1<<LED1); // PORTB: LED1 on PB5 is output | |||
TCNT1 = 0x????; // initialize (CLEAR)counter | |||
TCCR1B = 0x05; // T1 clk = internal clock source + prescaler 1:1024 | |||
TIFR1 = (1<<TOV1); // clear Timer 1 Overflow Flag (yes, writing 1 will clear it) | |||
while(1) // do forever this: | |||
{ | |||
if ( TIFR1 & (1<<TOV1) ) // if Timer 1 Overflow Flag is set, then | |||
{ | |||
PORTB = ????; // toggle LED1 | |||
TCNT1 = ????; // re-initialize counter | |||
TIFR1 = (1<<TOV1); // Clear Timer Overflow Flag | |||
} | |||
} | |||
return(0); // this will never happen | |||
} | |||
</source></tab> | |||
<tab name="s prerušením"><source lang="c++" style="background: LightYellow;"> | |||
#include <avr/io.h> | |||
#include <avr/interrupt.h> | |||
// if ( (TIFR1 & 0x01) == 0x01) // If the overflow flag is set | |||
// ... then following Interrupt routine is called | |||
ISR (TIMER1_OVF_vect) | |||
{ | |||
PORTB = PORTB ^ 0b????????; // Toggle the LED | |||
TCNT1 = 0x????; // Restart T/C1 - reload | |||
// Following is not necessary as it is cleared automatically | |||
// TIFR1 = 0x01; // Clear the overflow flag | |||
} | |||
int main( void ) | |||
{ | |||
/* *********************** Init device ************************************ */ | |||
... | |||
// Enable interrupts: | |||
TIMSK1 = (1<<TOIE1); // Timer 1 overflow interrupt enable | |||
sei(); // Assembler macro for global int. enable | |||
/* *********************** Main Loop ************************************** */ | |||
do { | |||
asm("nop"); // Do nothing | |||
} while(1); // And do this forever | |||
return(0); | |||
} | |||
</source></tab></tabs> | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_InterruptT1-registre.png]]<BR> | |||
''Prehľad registrov pre počítadlo T1.'' | |||
</div> | |||
'''Úloha''' | '''Úloha''' | ||
<!-- | |||
# Program doplňte podľa pokynov asistenta a odsimulujte.<BR>Pozn.: Treba vypnúť optimalizáciu! (Project/Project options [[Médiá:AVRstudioOptimize0.png|Pozri obr.]]) | |||
--> | |||
# Doplňte chýbajúce časti programu s časovačom T1 tak, aby LED bola ovládaná cez prerušenie a blikala s frekvenciou 1 s. Ak to zvládnete, pridajte si do programu nejakú premennú, ktorej hodnotu budete raz za sekundu inkrementovať, najlepšie tiež v obsluhe prerušenia. V termináli potom zobrazíte nejaký text a čas od zapnutia procesora v sekundách, popritom bude prehrávať nejakú melódiu a blikať LED diódou v 1 sekundovom intervale. Výsledkom bude program, ktorý dokáže realizovať tri veci "paralelne". | |||
{| style="padding:0 0.5em;"| | |||
| style="width:70%; vertical-align:top; border:1px solid #fad67d; background:#f3f0c6;"| | |||
<div style="border-bottom:1px solid #fad67d; background:#faecc8; padding:0.2em 0.5em; font-size:110%; font-weight:bold;">'''Poznámka'''</div> | |||
<div style="border-bottom:1px solid #fad67d; padding:0.4em 1em 1em;"> | |||
Ak chcete používať v obsluhe premenné, ktorých obsah chcete uchovať napr. až do ďalšieho prerušenia, musia byť deklarované ako <code>static</code>. Ak chcete mať v obsluhe prerušenia prístup aj k nejakej premennej v hlavnom programe, musí byť deklarovaná ako globálna. Navyše, ak kompilátor pri preklade nenájde miesto, kde sa premenná mení, vyhodí ju z programu úplne. Nie je taký múdry, | |||
aby zistil, že s ňou manipulujete v ISR nezistí, preto použite pri deklarácii <code>volatile</code>. | |||
|} | |||
Literatúra: | '''Literatúra:''' | ||
2021 | |||
* [https://microchipdeveloper.com/8avr:int AVR Interrupts] Microchip Developer Help | |||
* [https://microchipdeveloper.com/8avr:pin-change-interrupts AVR: Using Pin Change Interrupts] Microchip Developer Help | |||
* [http://ww1.microchip.com/downloads/en/Appnotes/Atmel-8468-Using-External-Interrupts-for-megaAVR-Devices_ApplicationNote_AVR1200.pdf AVR 8-bit Microcontroller AVR1200: Using External Interrupts for megaAVRDevices] Atmel Corporation, 2016. | |||
2016 | |||
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106 Newbie's Guide to AVR Timers] | * [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106 Newbie's Guide to AVR Timers] | ||
* [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=55347 The traps when using interrupts] | * [http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=55347 The traps when using interrupts] | ||
* [http://www. | * [http://www.senzor.robotika.sk/mmp/pdf/AVR-C-Timers.pdf Ako používať časovače v AVR C] | ||
* [http://www. | * [http://www.senzor.robotika.sk/predmety/mmp/pdf/PreruseniaAVR.pdf Prednáška o prerušeniach] (Ing. Chamraz) | ||
Aktuálna revízia z 08:03, 18. marec 2024
Prerušenia v AVR-GCC
Kompilátor AVR-GCC má obsluhu prerušení vyriešenú tak, že tabuľka s vektormi jednotlivých prerušení ukazuje na obslužné rutiny s preddefinovanými názvami. Pri výskyte niektorého z povolených prerušení sa vykoná rutina so zodpovedajúcim názvom.
Vo vašom kóde použijete obslužné funkcie pre prerušenie napr. takto (obsluha prerušenia z AD prevodníka):
#include <avr/interrupt.h>
ISR(ADC_vect)
{
// user code here
}
Takáto obsluha prerušenia sa potom spustí so globálnym zákazom prerušení (jednoúrovňové), ktoré sa po skončení prípadne zasa obnovia. Obsluha sa skončí špeciálnou inštrukciiou RETI, preto sa nedá zavolať z programu ako bežná funkcia.
Niektoré názvy vektorov:
ADC Conversion Complete ADC_vect External Interrupt Request 0 INT0_vect External Interrupt Request 1 INT1_vect External Interrupt Request 2 INT2_vect Pin Change Group 0 Interrupt PCINT0_vect Pin Change Group 1 Interrupt PCINT1_vect Pin Change Group 2 Interrupt PCINT2_vect Timer/Counter1 Overflow TIMER1_OVF_vect USART, Rx Complete USART_RXC_vect USART, Tx Complete USART_TXC_vect
Ak potrebujete prerušenia povoliť, resp. zakázať, máte k dispozícii funkcie
void sei(void); // Enables interrupts by setting the global interrupt mask.
void cli(void); // Disables all interrupts by clearing the global interrupt mask.
Obe funkcie sa preložia do jedinej asm inštrukcie, bez zbytočného pridaného kódu.
Prerušenie pri zmene stavu niektorého pinu procesora
Na cvičení použijeme prerušenie vyvolané zmenou (log.0 -> 1 aj log. 1 -> 0) na vstupe PD5. Tomuto vstupu je priradené prerušenie PCINT21, ktoré patrí do skupiny 2. Na povolenie tohto prerušenia musíme okrem globálneho bitu SREG: I
nastaviť aj bity PCICR: PCIE2
a PCMSK2: PCINT21
.
Nižšie máte uvedený ako príklad program pre ovládanie LED diódy na výstupe PD7 tlačidlom na PD5. Podobný program sme už robili na druhom cvičení. Upravený program nastaví stav LED diódy poľa tlačidla v obsluhe prerušenia, takže v hlavnej programovej slučke už nič neostalo.
#include <avr/io.h>
/* Pripojenie periferii k vyvojovej doske Arduino: */
#define LED2 PD7 // externa LED dioda
#define SW2 PD5 // externe tlacitko
#define SW2_ON bit_is_clear(PIND, SW2)
#define LED2_ON set_bit(PORTD,LED2)
#define LED2_OFF clear_bit(PORTD,LED2)
int main(void)
{
/* SETUP */
/* Konfiguracia I/O: portD.7 je vystupny (LED2) a portD.5 je vstup (SW2) *
* naviac je PortD.5 so zapnutym pull-up rezistorom cez reg. PORTD */
DDRD = 0b11011111; // PORTD: LED2 na PD7 je output, SW2 (PD5) input
PORTD = 0b00100000; // LED Active low, LED off, pull-up ON
/* LOOP */
while(1)
{
if ( SW2_ON )
LED2_ON;
else
LED2_OFF;
}
return(0);
}
#include <avr/io.h>
#include <avr/interrupt.h>
/* Pripojenie periferii k vyvojovej doske Arduino: */
#define LED2 PD7 // externa LED dioda
#define SW2 PD5 // externe tlacitko
#define SW2_ON bit_is_clear(PIND, SW2)
#define LED2_ON set_bit(PORTD,LED2)
#define LED2_OFF clear_bit(PORTD,LED2)
ISR (PCINT2_vect)
{
if ( SW2_ON )
LED2_ON;
else
LED2_OFF;
}
int main(void)
{
/* SETUP */
/* Konfiguracia I/O: portD.7 je vystupny (LED2) a portD.5 je vstup (SW2) *
* naviac je PortD.5 so zapnutym pull-up rezistorom cez reg. PORTD */
DDRD = 0b11011111; // PORTD: LED2 na PD7 je output, SW2 (PD5) input
PORTD = 0b00100000; // LED Active low, LED off, pull-up ON
/* ******* Konfiguracia prerusovacieho systemu **************** */
PCMSK2 |= (1<<PCINT21);
PCICR |= (1<<PCIE2);
sei(); // t.j. SREG |= (1<<I);
/* LOOP */
while(1)
{
asm("nop");
}
return(0);
}
Časovač T1
S časovačom T1 sme pracovali na minulom cvičení. Príznak pretečenia počítadla TOV1 sme testovali "ručne", metódou tzv. pooling - dopytovania. Je to neefektívny spôsob, pretože procesor využívame len na otrocké testovanie jedného bitu stále dookola. Preto je vašou úlohou doplniť vzorový (avšak nekompletný) program tak, aby sa pri pretečení T1 po jednej sekunde vyvolalo prerušenie a zmena stavu LED diódy bude tiež vykonaná v obsluhe prerušenia.
#include <avr/io.h>
#define SW1 PD5
#define LED1 PB5
int main(void)
{
DDRB = (1<<LED1); // PORTB: LED1 on PB5 is output
TCNT1 = 0x????; // initialize (CLEAR)counter
TCCR1B = 0x05; // T1 clk = internal clock source + prescaler 1:1024
TIFR1 = (1<<TOV1); // clear Timer 1 Overflow Flag (yes, writing 1 will clear it)
while(1) // do forever this:
{
if ( TIFR1 & (1<<TOV1) ) // if Timer 1 Overflow Flag is set, then
{
PORTB = ????; // toggle LED1
TCNT1 = ????; // re-initialize counter
TIFR1 = (1<<TOV1); // Clear Timer Overflow Flag
}
}
return(0); // this will never happen
}
#include <avr/io.h>
#include <avr/interrupt.h>
// if ( (TIFR1 & 0x01) == 0x01) // If the overflow flag is set
// ... then following Interrupt routine is called
ISR (TIMER1_OVF_vect)
{
PORTB = PORTB ^ 0b????????; // Toggle the LED
TCNT1 = 0x????; // Restart T/C1 - reload
// Following is not necessary as it is cleared automatically
// TIFR1 = 0x01; // Clear the overflow flag
}
int main( void )
{
/* *********************** Init device ************************************ */
...
// Enable interrupts:
TIMSK1 = (1<<TOIE1); // Timer 1 overflow interrupt enable
sei(); // Assembler macro for global int. enable
/* *********************** Main Loop ************************************** */
do {
asm("nop"); // Do nothing
} while(1); // And do this forever
return(0);
}
Úloha
- Doplňte chýbajúce časti programu s časovačom T1 tak, aby LED bola ovládaná cez prerušenie a blikala s frekvenciou 1 s. Ak to zvládnete, pridajte si do programu nejakú premennú, ktorej hodnotu budete raz za sekundu inkrementovať, najlepšie tiež v obsluhe prerušenia. V termináli potom zobrazíte nejaký text a čas od zapnutia procesora v sekundách, popritom bude prehrávať nejakú melódiu a blikať LED diódou v 1 sekundovom intervale. Výsledkom bude program, ktorý dokáže realizovať tri veci "paralelne".
Poznámka
Ak chcete používať v obsluhe premenné, ktorých obsah chcete uchovať napr. až do ďalšieho prerušenia, musia byť deklarované ako |
Literatúra:
2021
- AVR Interrupts Microchip Developer Help
- AVR: Using Pin Change Interrupts Microchip Developer Help
- AVR 8-bit Microcontroller AVR1200: Using External Interrupts for megaAVRDevices Atmel Corporation, 2016.
2016
- Newbie's Guide to AVR Timers
- The traps when using interrupts
- Ako používať časovače v AVR C
- Prednáška o prerušeniach (Ing. Chamraz)