AVR GCC: čtení stavu PINu portu v subr. ext. přerušení INT4
Moderátor: Moderátoři
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.
FHonza píše:Přerušení se nevnořují do sebe...Je zakázáno globální přerušení.
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.
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 takhle tedy NE!
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í
Kondenzátorama přímo na kontaktech enkodéru ho pošleš hodně rychle do kytek.
(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
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
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)
Doslova:
The I-bit is cleared by hardware after an interrupt has occurred, and is set by
the RETI instruction to enable subsequent interrupts.
Enkoder píše:ošetření zákmitů na spínači se řeší jeho překlenutím kondíkem ...
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ň.
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
Mimo to: hardwarově se přidává RC článek na každý vstup: http://www.electroschematics.com/wp-con ... wiring.png