AVR GCC: čtení stavu PINu portu v subr. ext. přerušení INT4
Moderátor: Moderátoři
Přerušení se nevnořují do sebe. Po vstupu do přerušení se uloží obsah PC do SP, ten se sníží. SREG se automaticky neukládá (proto již vzpomínané volatile). Je zakázáno globální přerušení. Po návratu (RETI) se vyzvedne PC a globální přerušení se povolí. Zkontrolují se příznaky přerušení a případně se obslouží dle priority. Příznaky přerušení se dají (ovšem ne všechny) vymazat zápisem jedničky do příslušného bitu.
frpr666 píše:@lesana87
s tím vnořováním samo do sebe nesouhlasím. Myslím, že tam bylo něco jako že se z ISR vrátí řízení zpět, provede se alespoň jedna instrukce a pak se zase (případně) zavolá ISR.
Ale on hned třetím příkazem v obsluze přerušení povoluje globální přerušení, takže když na vstupu přerušení INT4 vznikne zákmit dřív, než se dokončí obsluha, přerušení se vyvolá znovu a vnoří se do nedokončené obsluhy.FHonza píše:Přerušení se nevnořují do sebe...Je zakázáno globální přerušení.
@lesana87
aha, to je pravda, to je pravda. Pokud si explicitně povolí přerušení během ISR a je nahozený flag, tak nastane rekurze ISR (a přetečení zásobníka...).
Tak takhle tedy NE!
aha, to je pravda, to je pravda. Pokud si explicitně povolí přerušení během ISR a je nahozený flag, tak nastane rekurze ISR (a přetečení zásobníka...).
Kód: Vybrat vše
ISR(INT4_vect)
{
//...
sei(); // fail, tohleto zpusobi vecnou rekurzi ISR.
//...
}
Tak už to krásně chodí, děkuju:
ISR(INT4_vect)
{
CLEAR_BIT(EIMSK,INT4); // zakaž přerušení od INT4
unsigned char stavE3=0;
stavE3=CHECK_BIT(PINE,3);
if (stavE3)
{
NastavovanaHodnota++;
}
else
{
NastavovanaHodnota--;
}
_delay_ms(5);
SET_BIT(EIMSK,INT4); // povol přerušení od INT4 a
SET_BIT(EIFR,INTF4); // a smaž logickou jedničkou příznak INT4
}
začíná ale zlobit samotný enkoder, asi ty kondíky nedělají dobrotu, spínač enkoderu je přeci jen zkratuje, nebo je enkoder nekvalitní (testováno na logickém analyzátoru).
Já si myslím, že vnořit se do probíhajícího přerušení může asi jen přerušení s vyšší prioritou. Jedno samo do sebe ne. Přiklánim se k názoru Honzy. A ikdyž se globální INT zakáže a povolí, není to důvod ke znovuvyvolání tohoto přerušení uvnitř probíhajícího. Tak to aspoň bylo při mých dávných pokusech s 8051, Ale nedokážu to plně posoudit na kolik v tom hraje roli to GCC a na kolik je to dáno samotným procesorem.
edit1: to přerušení se může vyvolat hned několikrát za sebou, tím, že se příznak přerušení během probíhajícího přerušení znovunastaví, ale asi ne, že by došlo ke vnoření
ISR(INT4_vect)
{
CLEAR_BIT(EIMSK,INT4); // zakaž přerušení od INT4
unsigned char stavE3=0;
stavE3=CHECK_BIT(PINE,3);
if (stavE3)
{
NastavovanaHodnota++;
}
else
{
NastavovanaHodnota--;
}
_delay_ms(5);
SET_BIT(EIMSK,INT4); // povol přerušení od INT4 a
SET_BIT(EIFR,INTF4); // a smaž logickou jedničkou příznak INT4
}
začíná ale zlobit samotný enkoder, asi ty kondíky nedělají dobrotu, spínač enkoderu je přeci jen zkratuje, nebo je enkoder nekvalitní (testováno na logickém analyzátoru).
Já si myslím, že vnořit se do probíhajícího přerušení může asi jen přerušení s vyšší prioritou. Jedno samo do sebe ne. Přiklánim se k názoru Honzy. A ikdyž se globální INT zakáže a povolí, není to důvod ke znovuvyvolání tohoto přerušení uvnitř probíhajícího. Tak to aspoň bylo při mých dávných pokusech s 8051, Ale nedokážu to plně posoudit na kolik v tom hraje roli to GCC a na kolik je to dáno samotným procesorem.
edit1: to přerušení se může vyvolat hned několikrát za sebou, tím, že se příznak přerušení během probíhajícího přerušení znovunastaví, ale asi ne, že by došlo ke vnoření
A jak tedy vysvětlíš to, že se ti najednou přestalo připočítávat po dvou? Po sobě se ti ta přerušení mohou vyvolávat i teď, jediné co jsi odstranil, že se ti nemůžou vyvolávat přes sebe. A proč tam to přerušení od INT4 zakazuješ, když se podle tebe nemůžou do sebe vnořovat? ![Smile :)](./images/smilies/icon_smile.gif)
Kondenzátorama přímo na kontaktech enkodéru ho pošleš hodně rychle do kytek.
![Smile :)](./images/smilies/icon_smile.gif)
Kondenzátorama přímo na kontaktech enkodéru ho pošleš hodně rychle do kytek.
to lesana:
(ty globální přerušení jsem smazal už dřív skákalo to ještě po větších skocích s malou pauzou)
Odpověď proč to počítalo dvakrát:
Bylo to asi proto, že se zareaguje na první vzestupnou hranu - spustí se poprvé ISR (INT4_vect) , vzápětí vzniknou zákmity, při těchto zákmitech se znovu nastaví příznak pro obsloužení přerušení, ale podprogram běží dál pauzou, kde se se to časově přenese do doby kdy už žádné zákmity nejsou. Zůstal tam ale požadavek na to přerušení, který se obsloužil tím, že se podprogram přerušení spustil podruhé, proto to počítalo po dvou.
Novou úpravou - zakázáním INT4 po dobu podprogramu přerušení a hlavně shozením požadavku EIFR INTF4 na přerušení, jsem ten druhý požadavek na obsloužení zrušil. (čert ví, proč to nefunguje bez součinnosti s EIMSK - vidíš, mohl bych ještě vyzkoušet)
mohl bych se Tě zeptat, proč to neskákalo po třech![Smile :-)](./images/smilies/icon_smile.gif)
spíš by mě zajímalo, zda je třeba to chování hledat v céčku, nebo na straně hardware. Spíš bych řekl, že tomu diktuje hardware
ošetření zákmitů na spínači se řeší jeho překlenutím kondíkem, otázka je, jak velkým, ale i malý vyvíjí destrukční proud... vybíjí se přeci jen okamžitě z 5 Volt na nulu
(ty globální přerušení jsem smazal už dřív skákalo to ještě po větších skocích s malou pauzou)
Odpověď proč to počítalo dvakrát:
Bylo to asi proto, že se zareaguje na první vzestupnou hranu - spustí se poprvé ISR (INT4_vect) , vzápětí vzniknou zákmity, při těchto zákmitech se znovu nastaví příznak pro obsloužení přerušení, ale podprogram běží dál pauzou, kde se se to časově přenese do doby kdy už žádné zákmity nejsou. Zůstal tam ale požadavek na to přerušení, který se obsloužil tím, že se podprogram přerušení spustil podruhé, proto to počítalo po dvou.
Novou úpravou - zakázáním INT4 po dobu podprogramu přerušení a hlavně shozením požadavku EIFR INTF4 na přerušení, jsem ten druhý požadavek na obsloužení zrušil. (čert ví, proč to nefunguje bez součinnosti s EIMSK - vidíš, mohl bych ještě vyzkoušet)
mohl bych se Tě zeptat, proč to neskákalo po třech
![Smile :-)](./images/smilies/icon_smile.gif)
spíš by mě zajímalo, zda je třeba to chování hledat v céčku, nebo na straně hardware. Spíš bych řekl, že tomu diktuje hardware
ošetření zákmitů na spínači se řeší jeho překlenutím kondíkem, otázka je, jak velkým, ale i malý vyvíjí destrukční proud... vybíjí se přeci jen okamžitě z 5 Volt na nulu
tak jsem vyzkoušel odebrat
na začátku:
CLEAR_BIT(EIMSK,INT4); // zakaž přerušení od INT4
a na konci :
SET_BIT(EIMSK,INT4); // povol přerušení od INT4
a chodí to taky
(občas vynechá enkodér jednu polohu, nebude moc kvalitní)
Hlavní nedostatek byl pravděpodobně v tom, že jsem požadavek na druhé přerušení nenuloval jedničkou takto:
SET_BIT(EIFR,INTF4); // smaž logickou jedničkou příznak INTF4
(jak na to upozornil Honza)
na začátku:
CLEAR_BIT(EIMSK,INT4); // zakaž přerušení od INT4
a na konci :
SET_BIT(EIMSK,INT4); // povol přerušení od INT4
a chodí to taky
(občas vynechá enkodér jednu polohu, nebude moc kvalitní)
Hlavní nedostatek byl pravděpodobně v tom, že jsem požadavek na druhé přerušení nenuloval jedničkou takto:
SET_BIT(EIFR,INTF4); // smaž logickou jedničkou příznak INTF4
(jak na to upozornil Honza)
Zakazovat a poté povolovat přerušení v obsluze přerušení je zbytečné. Při vstupu do obsluhy přerušení je automaticky zakázáno, při návratu povoleno. Jediné co má smysl je mazat požadavek na další přerušení.
Doslova:
The I-bit is cleared by hardware after an interrupt has occurred, and is set by
the RETI instruction to enable subsequent interrupts.
Doslova:
The I-bit is cleared by hardware after an interrupt has occurred, and is set by
the RETI instruction to enable subsequent interrupts.
To je sice také možné, ale ne dost dobré. Ošetření zákmitů se dělá tak, že se v přerušení poznačí nová úroveň na vstupu a spustí se časovač, dejme tomu 3 ms. Po jeho vypršení se zkontroluje, jestli úroveň na daném vstupu zůstala zachována a když ano, vezme se signál na vstupu jako platný. Když ne, byl to planý impuls. Přijde-li tedy na vstup série zákmitů, časovač se opakovaně restartuje, naposledy s poslední hranou. Po 3 ms se pak přečte výsledná úroveň.Enkoder píše:ošetření zákmitů na spínači se řeší jeho překlenutím kondíkem ...
Enkoder s potlačením rušení pomocí časovače:
Mimo to: hardwarově se přidává RC článek na každý vstup: http://www.electroschematics.com/wp-con ... wiring.png
Kód: Vybrat vše
// file: main.c
//* chip: attiny13a
#include <avr/interrupt.h>
#include <avr/io.h>
#define BCLR(n,bit) (n)&=~(_BV(bit))
#define BSET(n,bit) (n)|=(_BV(bit))
// Disable INT0 interrupt
#define INT0_STOP {\
BCLR(GIMSK,INT0);\
}
// Enable INT0 interrupt
#define INT0_START {\
BSET(GIFR,INTF0);\
BSET(GIMSK,INT0);\
}
// Start timer, preset 100 ticks
#define TIM_START {\
TCNT0=(256-100);\
TCCR0B=_BV(CS00);\
}
// Stop timer
#define TIM_STOP {\
TCCR0B=0;\
}
//-------------------------
volatile uint16_t enc_value;
uint16_t enc_print;
//-------------------------
ISR(INT0_vect) //PB1
{
if(bit_is_set(PINB, 0)) // PB0
{
enc_value++;
}
else
{
if(enc_value > 0)
{
enc_value--;
}
}
INT0_STOP;
TIM_START;
}
//-------------------------
ISR(TIM0_OVF_vect)
{
TIM_STOP;
INT0_START;
}
//-------------------------
void setup( void)
{
// The low level selected for INT0_vect
MCUCR = 0x00;
// Enable TIM0_OVF interrupt
BSET(TIMSK0, TOIE0);
TIM_START;
sei();
}
//-------------------------
void loop( void)
{
cli();
enc_print = enc_value;
sei();
// ...
// print(enc_print);
// ...
}
//-------------------------
int main( void)
{
setup();
for(;;)
{
loop();
}
}
//-------------------------
//EOF