Forum: Mikrocontroller und Digitale Elektronik Interrupt erwischt nicht immer alle Interrupts.


von Matthias (Gast)


Lesenswert?

Morgen,

vll kann mir einer von Euch helfen.
Und zwar erwischt mein Mega 16 mit 16MHz nicht immer alle Interrupts die 
an INT0 landen. Ich will an INT0 eine Frequenzmessung durchführen.

Werf ich ihn nun 1 MHZ(TTL mit 5 und 0 Volt ) an den Interrrupt erwischt 
er teilweise nicht alle Flanken.
Ich versteh blos nicht warum.

Hier mal der Codeabschnitt:
1
        TIMSK = (0<<TOIE0) | (0<<TICIE1) | (0<<TOIE1); //--Timer0 und 1 abgeschalten
2
        
3
        GICR = (1<<INT0);  //--Interrupt aktivieren
4
        MCUCR = (1<<ISC01);
5
6
        Frequenzzaehler=0; //--Frequenzzähler nochmals auf 0 Setzen
7
8
        SET_BIT(PORTC,2);  //--Reset Loslassen
9
10
        Delay_10ms(50); //--Kurz warten
11
12
        //--Frequenz berechnen
13
        //Periodendauer = (Frequenzzaehler/1000000.0);
14
        //Frequenz = ((1/Periodendauer) / Frequenzanpassungskonstante);
15
        
16
        //Frequenz = (Frequenzzaehler*Frequenzanpassungskonstante);
17
        //Frequenz = (1000000/Frequenz);
18
19
        Frequenz = Frequenzzaehler;
20
              
21
        Frequenzzaehler = 0;
22
23
        CLEAR_BIT(PORTC,2); //--Reset erneut Ziehen
24
    
25
        //--Strom und Spannung hohlen
26
        Strom=get_Strom();
27
        Spannung=get_Spannung();
28
29
        //--R berechnen
30
        
31
        Widerstand = (Spannung/Strom);
32
33
        //--Datenstring vorbereiten und Schicken
34
        DatenStringFR_erstellen(DatenFR);
35
        usart_putsn(DatenFR);
36
37
        GICR = (0<<INT0);  //--Interrupt abschalten
38
         MCUCR = (0<<ISC01);
39
40
        //--Timer Neustarten
41
        TIMSK = (1<<TOIE0) | (1<<TICIE1) | (1<<TOIE1);

Momentan lass ich mir nur den Zählerstand ausgeben.

Die Werte schwanken teilweise richtig Krass oder ergeben gleich 0.
Pfuscht da ein Timer dazwischen oder was?
Normalerweise sollten die ja abgeschaltet sein und der INT0 hat ja 
sowieso höhere Priorität.

In einem Testprojekt welches nur die Frequenz zählen sollte, hauts auch 
nicht hin.

Jemand von euch eine Idee?

MfG
Matthias

von Matthias (Gast)


Lesenswert?

P.S. Ich meinte die Werte des Zählers schwanken sehr stark. Nicht die 
Frequenz die ist konstant.

von Falk B. (falk)


Lesenswert?

@ Matthias (Gast)

>Und zwar erwischt mein Mega 16 mit 16MHz nicht immer alle Interrupts die
>an INT0 landen. Ich will an INT0 eine Frequenzmessung durchführen.

>Werf ich ihn nun 1 MHZ(TTL mit 5 und 0 Volt ) an den Interrrupt erwischt
>er teilweise nicht alle Flanken.
>Ich versteh blos nicht warum.

Nein? Du bist ein Scherzkeks. Dein AVR hat gerade mal 16 Takte Zeit um

- in den Interrupt zu springen
- einen Zähler hochzählen
- zurückspringen.

Allein den Interrupt anspringen und zurückspringen dauert 10 Takte. 
Solche Frequenzen mit einem Interrupt zu zählen ist ziemlicher Unsinn. 
Das macht man mit einem Timer, welcher extern getaktet wird.

MFG
Falk

von Tobi (Gast)


Lesenswert?

Wo ist die Interrupt-Routine?

Bedenke: Bei 1MHz Eingangssignal hast du 16 Takte Befehl zur Verfügung!
Mit ein paar pushes und pops könnte das knapp werden!

von Matthias (Gast)


Lesenswert?

Hier die Interruptroutine.
1
SIGNAL(SIG_INTERRUPT0)
2
{
3
  Frequenzzaehler++;
4
}

@Falk:

Na sauber...sprich ich kann die Platine wieder umstricken.
Schöne Aussichten.

von Matthias (Gast)


Lesenswert?

Ich seh grad der B Port wo T0 und T1 dran sind, ist voll.

von Bluehorn (Gast)


Lesenswert?

Hi Falk,

Hmm, aus reiner Neugierde, ich suche schon länger nach der Worst-Case 
Latenzzeit eines AVR uC. Wie kommst Du auf die 10 Takte?

Aus einem AVR-Datenblatt (tiny13, wollte nicht gerade so einen Schinken 
runterladen):

* The interrupt execution response for all the enabled AVR interrupts is 
four clock cycles minimum. After four clock cycles the Program Vector 
address for the actual interrupt handling routine is executed.

* The vector is normally a jump to the interrupt routine, and this jump 
takes three clock cycles. (??? Doch nur zwei Taktzyklen mit rjmp?)

* If an interrupt occurs during execution of a multi-cycle instruction, 
this instruction is completed before the interrupt is served.

* A return from an interrupt handling routine takes four clock cycles.

Damit komme ich auf

4 (Sprung zu Vektor) + 2 (Sprung zu Handler) + 3 (längste Instruktion, 
mit ext. Speicher beliebig mehr) + 4 (Rücksprung) = 13 Taktzyklen.

Dann fehlt noch der Hinweis, dass das Statusregister nicht automatisch 
gesichert wird, das darf man also auch noch machen. Wenn man hierfür 
folgenden Code annimmt:
1
        push d1
2
        in   d1,sreg
3
        ...
4
        out  sreg,d1
5
        pop  d1

Dann haben wir nochmal 6 Taktzyklen (push/pop je zwei, in/out je eine). 
Macht schon 19 Taktzyklen. Wohlgemerkt, ohne, dass der Interrupthandler 
irgendetwas machen.

Immerhin, wenn man ein Register über hat, kann man sich push/pop sparen 
und mit 15 Taktzyklen auskommen. Bei kleinen AVRs hat eine Anweisung 
max. 2 Taktzyklen (IIRC), da sind es dann 14 Taktzyklen worst case (wenn 
interrupts gerade erlaubt sind).


Muss man noch etwas berücksichtigen, oder entspricht das den Tatsachen? 
Der Worst-Case würde mich für die eine oder andere Anwendung schon 
interessieren.

Ach, eins noch:

* If an interrupt occurs when the MCU is in sleep mode, the interrupt 
execution response time is increased by four clock cycles. This increase 
comes in addition to the start-up time from the selected sleep mode.


Also, aufwecken aus dem Sleep wird dann noch richtig spannend. Hier wohl 
weniger relevant, wollte es nur der Vollständigkeit halber angeben.

Gruss aus Dresden, Torsten

von Matthias (Gast)


Lesenswert?

Ich frag mich blos warum er dann mal die Werte erwischt und mal 
überhaupt nix. Bei gleichem Code?

In der hälfte der Tests bisher hatte er die Werte. In 25% war er etwas 
falsch und der Rest war immer null.

Wenn ers nun überhaupt nicht packen würde, sollten doch immer Konstant 
Null rauskommen.

Vorallem wenn ichs das erste Mal Abfrage stimmts eigentlich in 99% der 
Fällen immer. Ab dann wirds erst Falsch.

von Johannes M. (johnny-m)


Lesenswert?

@Bluehorn:
Das was im Datenblatt steht, ist nur das, was die µC-Hardware macht, 
also quasi noch ein "best case"! Der Compiler sichert meist noch eine 
ganze Stange Register (v.a. r0 und r1 sowie evtl. verwendete 
Pointer-Register). Und beim ATMega16 wird sowieso automatisch ein jmp 
eingebaut, weil der Mega16 nunmal 16 KiB Flash hat, und da reicht rjmp 
u.U. nicht mehr aus.

Wenn Du in Assembler programmierst, dann kannst Du vielleicht besser 
abschätzen, was wirklich alles gesichert werden muss, aber ein Compiler 
muss nunmal gewisse Annahmen treffen. In Assembler kann man z.B. bei 
ausreichend Reserven dafür sorgen, dass der Interrupt Handler nur 
Rechenregister benutzt, die sonst nicht verwendet werden.

von holger (Gast)


Lesenswert?

>Vorallem wenn ichs das erste Mal Abfrage stimmts eigentlich in 99% der
>Fällen immer. Ab dann wirds erst Falsch.

Der zählt ja auch locker flockig weiter ;)
Du verbietest den INT0 zu spät und setzt den Wert
zu früh auf null.

>        Frequenzzaehler = 0;
>
>        CLEAR_BIT(PORTC,2); //--Reset erneut Ziehen
>
>        //--Strom und Spannung hohlen
>        Strom=get_Strom();
>        Spannung=get_Spannung();
>
>        GICR = (0<<INT0);  //--Interrupt abschalten
>         MCUCR = (0<<ISC01);


       GICR = (0<<INT0);  //--Interrupt abschalten
        MCUCR = (0<<ISC01);

        Frequenzzaehler = 0;

Dürfte zumindest bessere Ergebnisse liefern.

von Falk B. (falk)


Lesenswert?

@ Matthias (Gast)

>Na sauber...sprich ich kann die Platine wieder umstricken.
>Schöne Aussichten.

Tja, Ce'st la vie.

@ Bluehorn (Gast)

>Hmm, aus reiner Neugierde, ich suche schon länger nach der Worst-Case
>Latenzzeit eines AVR uC. Wie kommst Du auf die 10 Takte?

So Pi mal Dauem aus der Erinnerung.

>4 (Sprung zu Vektor) + 2 (Sprung zu Handler) + 3 (längste Instruktion,
>mit ext. Speicher beliebig mehr) + 4 (Rücksprung) = 13 Taktzyklen.

Passt.

>Dann fehlt noch der Hinweis, dass das Statusregister nicht automatisch
>gesichert wird, das darf man also auch noch machen.

Muss man nur, wenn die Befehle in der ISR die Flags ändern. Eine Additon 
tut das aber ;-)

>Macht schon 19 Taktzyklen. Wohlgemerkt, ohne, dass der Interrupthandler
>irgendetwas machen.

Sieht du.

>Immerhin, wenn man ein Register über hat, kann man sich push/pop sparen
>und mit 15 Taktzyklen auskommen. Bei kleinen AVRs hat eine Anweisung
>max. 2 Taktzyklen (IIRC), da sind es dann 14 Taktzyklen worst case (wenn
>interrupts gerade erlaubt sind).

Geht aber nur in ASM. In c sind die Interrupts einen Tick ineffizienter, 
weil der Compiler (zumindest was ich bein GCC gesehen habe) immer bisser 
rumpusht und poppt sowie am Stack fummelt.

>Muss man noch etwas berücksichtigen, oder entspricht das den Tatsachen?

Das passt soweit.

>Also, aufwecken aus dem Sleep wird dann noch richtig spannend. Hier wohl
>weniger relevant, wollte es nur der Vollständigkeit halber angeben.

Sleep Mode

@ Matthias (Gast)

>Ich frag mich blos warum er dann mal die Werte erwischt und mal
>überhaupt nix. Bei gleichem Code?

Ist doch logisch. Dein Programmist ein Zufallsgenerator. dein 
_dlay_ms(10) kannst du bei aktivein interrutps KOMPLETT vergessen, bei 
200% Auslastung durch einen 1 MHz Interrupt sowieso.

>Wenn ers nun überhaupt nicht packen würde, sollten doch immer Konstant
>Null rauskommen.

Nöö, er verschluckt einfach diverse Interrupts.

>Vorallem wenn ichs das erste Mal Abfrage stimmts eigentlich in 99% der
>Fällen immer. Ab dann wirds erst Falsch.

Zufall.

MfG
Falk

von holger (Gast)


Lesenswert?

>       GICR = (0<<INT0);  //--Interrupt abschalten
>        MCUCR = (0<<ISC01);
>
>        Frequenzzaehler = 0;

Obige Antwort ignorieren oder löschen :)
Ich war so frei den Code darüber zu ignorieren. AARgh.

von Severino R. (severino)


Lesenswert?

Falk Brunner wrote:

> Tja, Ce'st la vie.
       ^^^^^
Error (orthography)! Try "C'est la vie." instead.

von Falk B. (falk)


Lesenswert?

@ Severino R. (severino)

>> Tja, Ce'st la vie.
>       ^^^^^
>Error (orthography)! Try "C'est la vie." instead.

Jaja, Französisch kann ich ganz gut, nur mit der Sprache haperts . . . 
;-)

duckundwech
Falk

von Harry U. (harryup)


Lesenswert?

hi,
ist schon 'ne ganze weile her, ich hab' auch mal frequenzzähler 
'gespielt' mit den avrs. mit demselben problem, mal hat die anzeige 
gestimmt, mal nicht, auffällig war aber, dass die differenz zum 
erwarteten wert (die frequenz war bekannt und wurde durch einen anderen 
'richtigen' frequenzzähler kontrolliert) und ausgegebenen wert immer die 
gleiche war. ursächlich dafür war die tatsache, dass während der 
abarbeitung eines ints manchmal schon der nächste ausgelöst wurde, aber 
nicht abgearbeitet. das konnte dann per abfrage des interrupt flag 
register 'getürkt' werden, wenn ein unbearbeiteter (kann man das so 
schreiben?) isr anstand, war das ergebnis schrott, dafür das flag 
gesetzt, ergo korrekturmöglichkeit gegeben.
die ganze problematik ging aber wohl davon aus, dass isrs eben synchron 
zum systemtakt erfolgen, die zu messende freq. aber nicht.
habe dann den counter verwendet, der erlaubt asynchrone verwendung, 
damit konnte ich dann immerhin bis knapp zur halben betriebsfrequenz des 
avrs messen, mit immer richtigen ergebnissen.
grüssens, harry

von Dietmar E (Gast)


Lesenswert?

> Na sauber...sprich ich kann die Platine wieder umstricken.

Nicht unbedingt. Soll das ein Messgerät werden? Wenn es nur am 
ISR-Overhead scheitert, dann lass die ISR doch weg und werte den Pin bei 
abgeschalteten Interrupts aus. Dafür gibt es mehrere Möglichkeite.

1. Beispiel: Schreibe ein exakt <x>us langes Codefragment (Länge 
ausrechnen) mit Port-Abfragen. Ohne Sprünge und mit einer 
Ausführungszeit unabhängig vom Pin-Zustand. Etwa so:

reading = PINn & MASK; counter += (reading ^ state); state = reading;
reading = PINn & MASK; counter += (reading ^ state); state = reading;
reading = PINn & MASK; counter += (reading ^ state); state = reading;
...

Nach dessen Durchlauf wird die Anzahl der Pegelwechsel in einer 
Variablen stehen. Kann man dann über die bekannte Dauer des Codes in 
Frequenz umrechnen.

2. Eine andere Möglichkeit wäre, einen Timer mit F_CPU loslaufen zu 
lassen. 100 Pegelwechsel per Busy-Pooling des Pins zählen. Dann den 
neuen Timerstand auslesen und daraus die Frequenz berechnen:

...  // start timer

loops = 100;
start = TCNT0;

while (loops--)
{
    while ((PINA & MASK) == 0)
        ;
    while  (PINA & MASK)
        ;
}

stop = TCNT0;

Gefahr hier ist, dass der Code hängt, wenn das Signal ausbleibt.

von Severino R. (severino)


Lesenswert?

Falk Brunner wrote:

> Jaja, Französisch kann ich ganz gut, nur mit der Sprache haperts . . .
> ;-)

Und mit der Schrift erst recht, was ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.