Forum: Mikrocontroller und Digitale Elektronik ATmega2560 Interrupt Prioritäten


von Marcel (Gast)


Lesenswert?

Hallo zusammen,

ich bräuchte eine kurze Auskunft bezüglich der Auswirkung von ver. 
Interrupt Prioritäten beim ATmega2560.

Hintergrund ist der, dass ich eine zeitabhängige Regelung in 
Timer-Interrupts ausgelagert habe. Während der ISR, die alle 10ms 
aufgerufen wird, wird über ADC höherer Priorität eine analoge Spg. 
eingelesen.

Unterbricht der A/D Interrupt die Zeit des Timer-Interrupt oder läuft 
die Zeit im Hintergrund weiter, sodass der Aufruf alle 10ms beibehalten 
wird?

Etwas grafisch erklärt:

1..2..3..4..5..6..7..8..9..10
1..2.I---I..5..6..7..8..9..10        oder
1..2.I---I.3..4..5..6..7..8..9..10   ?

Bin mir gerade der Bedeutung von "Unterbrechung durch ver. Prioritäten" 
nicht im Klaren.

Danke und Gruß
Marcel

von Stefan F. (Gast)


Lesenswert?

Wenn der Timer frei läuft kann die Software interrupts verpassen. Der 
Timer läuft einfach weiter.

Man kann den Timer aber auch so programmieren, dass er immer anhält und 
wartet, bis die ISR ihn wieder anschubst. Dann verpasst die Software 
keine Impulse, aber die Zeiten sind nicht mehr so regelmäßig.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Marcel schrieb:
> Unterbricht der A/D Interrupt die Zeit des Timer-Interrupt oder läuft
> die Zeit im Hintergrund weiter, sodass der Aufruf alle 10ms beibehalten
> wird?

 Beim MEGA2560 kann kein Interrupt unterbrochen werden, egal welche
 Priorität der zur Zeit laufende und welche Priorität der Interrupt hat,
 welches gerade gefeuert hat.

 Man kann aber, wenn das so gewollt ist, in der gerade laufenden ISR
 das Global Interrupt Enable ( I ) bit setzen, und dann kann diese
 ISR durch ein anderes Interrupt unterbrochen werden.

Marcel schrieb:
> Bin mir gerade der Bedeutung von "Unterbrechung durch ver. Prioritäten"
> nicht im Klaren.

 Bezieht sich nur auf die Interrupts, die warten, um abgearbeitet zu
 werden.
 Wenn du z.B. in deiner Timer-ISR bist, ADC feuert, gleich danach der
 INT0 kommt, dann wird zuerst die ganze Timer-ISR abgearbeitet und
 danach wird anhand der Priorität entschieden welcher Interrupt zuerst
 abgearbeitet wird.
 In diesem Fall ist es INT0, auch wenn er später gefeuert hat.

 EDIT:
Marcel schrieb:
> Timer-Interrupts ausgelagert habe. Während der ISR, die alle 10ms
> aufgerufen wird, wird über ADC höherer Priorität eine analoge Spg.
> eingelesen.

 ADC hat niedrigere Priorität als Timer.
 Je höher die Nummer, desto niedriger die Priorität.
 RESET hat 0 und somit die höchste Priorität.
 USART3_Tx hat 57 und somit die niedrigste Priorität.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stefan us schrieb:
> Wenn der Timer frei läuft kann die Software interrupts verpassen. Der

 Und wie soll das passieren ?

von Marcel (Gast)


Lesenswert?

Vielen Dank für die raschen Antworten!

Marc Vesely schrieb:
> Bezieht sich nur auf die Interrupts, die warten, um abgearbeitet zu
>  werden.
>  Wenn du z.B. in deiner Timer-ISR bist, ADC feuert, gleich danach der
>  INT0 kommt, dann wird zuerst die ganze Timer-ISR abgearbeitet und
>  danach wird anhand der Priorität entschieden welcher Interrupt zuerst
>  abgearbeitet wird.
>  In diesem Fall ist es INT0, auch wenn er später gefeuert hat.

Danke!

Marc Vesely schrieb:
> ADC hat niedrigere Priorität als Timer.
>  Je höher die Nummer, desto niedriger die Priorität.
>  RESET hat 0 und somit die höchste Priorität.
>  USART3_Tx hat 57 und somit die niedrigste Priorität.

Ich habe vergessen zu erwähnen, dass ich Timer4 und Timer5 benutze, die 
in der Vector-Liste zwischen ADC und USART3 TX liegen.

Vielleicht erübrigt sich dann mein Gedanke mit dem ADC Interrupt. 
Prinzipiell soll das Programm folgendermaßen funktionieren:

***main nur zur Kommunikation***

***Timer4 nur zur Initialisierung***
ISR(TIMER4_COMPA_vect)
{
    t_cst = 100;
    TIMSK5 |= (1 << OCIE4A);
    TIMSK4 |= 0;
    status = 1;
}

***Timer5 zur zeitabhängigen Regelung***
ISR(TIMER5_COMPA_vect)
{
  if(stage >= 0 && stage < 200)
  {
      value = ADC_Read(7);
      /*
           Regelung
      */
      status = 1;
      stage++;
  }


  if(stage >= 200 && stage < 200 + t_cst)
  {
      value = ADC_Read(7);
      /*
           Regelung
      */
      status = 2;
      stage++;
  }


  if(stage >= 200 + t_cst && stage < 400 + t_cst)
  {
      value = ADC_Read(7);
      /*
           Regelung
      */
      status = 3;
      stage++;
  }
}

Timer5 wird alle 10ms aufgerufen. Die Regelung basiert auf dem 
jeweiligen ADC-Wert. Weil das Einlesen der analogen Spg. eine endliche 
Zeit dauert, dachte ich, diese Zeit in einem zusätzlichen Interrupt 
auszulagern, der KEINE Auswirkung auf die Wiederholrate alle 10ms hat.

Weiß gerade nicht, wie ich den ADC-Wert mit Vernachlässigung der 10ms 
einlesen soll.

Gruß
Marcel

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Marcel schrieb:
> Timer5 wird alle 10ms aufgerufen. Die Regelung basiert auf dem
> jeweiligen ADC-Wert. Weil das Einlesen der analogen Spg. eine endliche
> Zeit dauert, dachte ich, diese Zeit in einem zusätzlichen Interrupt
> auszulagern, der KEINE Auswirkung auf die Wiederholrate alle 10ms hat.
>
> Weiß gerade nicht, wie ich den ADC-Wert mit Vernachlässigung der 10ms
> einlesen soll.

 Und ich weiss nicht genau ob dir ein neuer ADC-Wert oder die genaue
 Einhaltung der 10ms Zeitfensters wichtiger ist, deshalb ist hier etwas,
 dass die Regelung nur dann ausfuhrt, wenn die vorherige Regelung
 zu Ende ist.
 ADC-Messung und Regelung gehen hier zusammen (muss natürlich nicht
 sein).
1
#include <avr/io.h>
2
3
void Tim5_init();
4
void Regelung(void);
5
uint16_t ADC_Read(uint8_t Channel);
6
7
8
volatile uint16_t stage = 0;
9
volatile uint8_t status = 1;
10
volatile uint8_t t_cst = 100;
11
volatile uint8_t RegelFlag = 0;
12
13
uint16_t AdcVal = 0;
14
15
// ***main nur zur Kommunikation***
16
void main(void)
17
 {
18
    Tim5_init();
19
20
    while(1)
21
    {
22
       if (RegelFlag == 1) Regelung();
23
    }
24
 }
25
26
/*** Initialisierung von Timer5 (Unvollstandig !!!)          */
27
void Tim5_init()
28
{
29
  //* Wenn du mit Timer5 arbeitest, dann benutze auch seine Bezeichnungen
30
    TIMSK5 |= (1 << OCIE4A);        // sollte heissen: TIMSK5 |= (1 << OCIE5A);
31
}
32
33
//*** Deine Routine zur regelung (zusammen mit ADC_Read)
34
void Regelung(void)
35
{
36
    AdcVal = ADC_Read(7);
37
    /*
38
           Regelung
39
    */
40
    RegelFlag = 0;
41
}
42
43
uint16_t ADC_Read(uint8_t Channel)
44
{
45
   return 987;                  // Dummy-Wert
46
}
47
48
// *** Timer5 zur zeitabhängigen Regelung ***
49
ISR(TIMER5_COMPA_vect)
50
{
51
   if(RegelFlag == 0)   //* Wenn die vorherige Regelung() abgearbeitet ist...
52
   {
53
     if(stage >= 0 && stage < 200) status = 1;
54
55
     else if(stage >= 200 && stage < (200 + t_cst)) status = 2;
56
57
     else if(stage >= (200 + t_cst) && stage < (400 + t_cst)) status = 3;
58
59
     else return;
60
     stage++;
61
     RegelFlag = 1;
62
   }
63
}

 Nicht ausprobiert, keine Garantie auf Funktionsfähigkeit.
 Wer Fehler findet, darf diese ruhig behalten.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Marcel schrieb:

> ich bräuchte eine kurze Auskunft bezüglich der Auswirkung von ver.
> Interrupt Prioritäten beim ATmega2560.

Wieso? Es gibt nämlich eigentlich keine.

Prioritäten gibt beim AVR nur für den Fall, daß mehrere 
Interruptanforderungen auftauchen, solange keine angenommen wird bzw. 
werden kann. Dann regeln diese Prioritäten, in welcher Reihenfolge die 
unbehandelten IRQs bearbeitet werden, nachdem eine Interruptbehandlung 
wieder möglich geworden ist.

Das ist was völlig anderes als die übliche Bedeutung von 
Interruptprioritäten. Die gibt es beim AVR einfach nicht. Ohne besondere 
Maßnahmen blockiert jede ISR alle anderen Interrupts über ihre gesamte 
Laufzeit, kann deren Auslösung also beliebig lange verzögern. Ja sogar 
Code in main() kann das, er braucht dazu nur ein "cli" zu machen.

> Unterbricht der A/D Interrupt die Zeit des Timer-Interrupt

Er unterbricht nicht die Timer-ISR. Aber er kann natürlich dafür sorgen, 
daß sie erst verzögert zur Ausführung kommt.

von Marcel (Gast)


Lesenswert?

Vielen Dank für deine Hilfe.

Marc Vesely schrieb:
> Und ich weiss nicht genau ob dir ein neuer ADC-Wert oder die genaue
>  Einhaltung der 10ms Zeitfensters wichtiger ist, deshalb ist hier etwas,
>  dass die Regelung nur dann ausfuhrt, wenn die vorherige Regelung
>  zu Ende ist.

Leider hängen beide unmittelbar zusammen. In jedem der drei 
"Zeitabschnitte" gebe ich verschiedene zeitabhängige Positionen (--> 
Streckenverläufe) vor, die über einen Sensor erfasst und als analoge 
Spg. zurückgegeben werden.
Alle 10ms soll nach einer definierten Rechenvorschrift eine neue 
Position erreicht sein (Lageregelung).
Je nach Abweichung von errechneter Position und tatsächlicher Position 
reagiert die Regelung.

Marc Vesely schrieb:
> volatile uint8_t RegelFlag = 0;

Das ist ein sehr interessanter Ansatz mit dem RegelFlag. Vielen Dank 
dafür! Hier habe ich allerdings das Problem, dass ich bedingt durch das 
Einlesen eine Verlängerung der Zeitabschnitte habe.
Aus ursprünglichen 2s werden u.U. 2s + 200 * A/D-Einleseverzögerung am 
Bsp. des ersten Abschnittes.

Prinzipiell normiere ich mir damit "nur" meine Zeitachse, was für die 
Lageregelung zwar kein Weltuntergang ist, aber dennoch keine saubere 
Lösung darstellt, die perfekt in mein mathematisches Modell hineinpasst.

Eine Unterbrechung der Timer5-ISR durch den ADC Interrupt, die weiterhin 
alle 10ms aufgerufen wird, ist demnach technisch nicht zu realisieren 
denke ich, oder?

Gruß
Marcel

von Marcel (Gast)


Lesenswert?

c-hater schrieb:
> Prioritäten gibt beim AVR nur für den Fall, daß mehrere
> Interruptanforderungen auftauchen, solange keine angenommen wird bzw.
> werden kann. Dann regeln diese Prioritäten, in welcher Reihenfolge die
> unbehandelten IRQs bearbeitet werden, nachdem eine Interruptbehandlung
> wieder möglich geworden ist.
>
> Das ist was völlig anderes als die übliche Bedeutung von
> Interruptprioritäten. Die gibt es beim AVR einfach nicht. Ohne besondere
> Maßnahmen blockiert jede ISR alle anderen Interrupts über ihre gesamte
> Laufzeit, kann deren Auslösung also beliebig lange verzögern. Ja sogar
> Code in main() kann das, er braucht dazu nur ein "cli" zu machen.

Auch dir Danke!

von Thomas (kosmos)


Lesenswert?

Wie wichtig sind dir denn die ADC Werte du musst ja nicht unbedingt 
einen Interrupt nutzen. Du könntest auch regelmäßig nach diesem 
Fertigbit schauen.

von Bastler (Gast)


Lesenswert?

Wissen wir eigentlich irgendwas über die ADC "Abtastrate"?
Wird der ADC-Wert in der 10ms-Regelung verwendet?
Falls ja, dann könnte der TimerInt ja einfach den ADC anwerfen, der, 
wenn fertig, per AdcInt den Wert für den nächsten TimerInt liefert. 
Dabei würde der AdcInt eventuell eben warten müssen, bis der TimerInt 
fertig ist. Das ergibt ein exaktes 10ms-Raster für die Regelung und ein 
definiertes Alter des ADC-Meßwertes. Falls nicht andere Interrupts im 
Weg sind. Auch das wissen wir nicht. Sowas beschreibt man übrigens 
besser als Zeichnung denn als Text.

von tictactoe (Gast)


Lesenswert?

Marcel schrieb:
> Hier habe ich allerdings das Problem, dass ich bedingt durch das
> Einlesen eine Verlängerung der Zeitabschnitte habe.
> Aus ursprünglichen 2s werden u.U. 2s + 200 * A/D-Einleseverzögerung am
> Bsp. des ersten Abschnittes.

Die Verlängerung der Zeitabschnitte kann ich nicht erkennen, sofern der 
Timer-Interrupt weniger als 10ms benötigt. Denn der Timer läuft ja im 
Hintergrund gleich von vorne los, während der Interrupt-Handler 
abgearbeitet wird.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Marcel schrieb:
> Eine Unterbrechung der Timer5-ISR durch den ADC Interrupt, die weiterhin
> alle 10ms aufgerufen wird, ist demnach technisch nicht zu realisieren
> denke ich, oder?
 Doch, sogar recht einfach.
 Nur hat das gewisse Nachteile und ist (meiner Meinung nach) in deinem
 Fall überhaupt nicht notwendig.


Marcel schrieb:
> Hier habe ich allerdings das Problem, dass ich bedingt durch das
> Einlesen eine Verlängerung der Zeitabschnitte habe.
> Aus ursprünglichen 2s werden u.U. 2s + 200 * A/D-Einleseverzögerung am
> Bsp. des ersten Abschnittes.

 Nein, wieso ?
 Alle 10ms wird (auf die us genau) eine ADC-Messung und eine Regelung
 durchgeführt. ADC passt da rein, da solltest du überhaupt keine
 Probleme damit haben. Und ich nehme stark an, dass die Regelung auch
 zeitlich da reinpasst, d.h. weniger als 10ms braucht.

 Eine ADC-Konversion dauert 13-14 ADC Clock Cycles. Selbst bei einer
 Konversionszeit von 100us sollte es da keine Probleme geben.
1
   10ms                            20ms                       30ms
2
    |                               |                          |
3
 ___|_______________________________|__________________________|_____________
4
    |ADC|Regeln|                    |ADC|Regeln|               |ADC|Regeln|
5
    |   |      |                    |   |      |               |   |      |
6
      +100us   |                      +100us   |                 +100us   |
7
             +??us                           +??us                       +??us

 Oder verstehe ich da etwas falsch ?

von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

Moin Moin,

zum besseren Verständnis habe ich eben mal eine kleine Skizze 
angefertigt, aus der das Problem hoffentlich ersichtlich wird. Liegt zu 
Beginn eines Timer Interrupts ein falscher ADC Wert vor, so liefert der 
Regelkreis natürlich falsche Ergebnisse. Der Regelkreis soll alle 10 ms 
auf die neue Position ausregeln.
Die Regelung wird in der Main nach entspr. Bedingungen angestoßen und 
muss dann unabhängig von ihr zeitgenau ablaufen.

Gruß Marcel

von Hubert G. (hubertg)


Lesenswert?

Sollte der Timer dann nicht zuerst den ADC auslesen und den Wert in die 
Regelschleife einfließen lassen?
Wenn ADC und Regelschleife gleichzeitig angestossen werden ist der 
ADC-Wert immer 10ms alt.
Also Timer stösst ADC an und das Ergebnis die Regelschleife. Ich sehe 
hier auch kein Zeitproblem.
Oder sehe ich das vollkommen falsch?

von Marcel (Gast)


Lesenswert?

Marc Vesely schrieb:
> Eine ADC-Konversion dauert 13-14 ADC Clock Cycles. Selbst bei einer
>  Konversionszeit von 100us sollte es da keine Probleme geben.

genau das ist das, was ich mit meiner ersten Frage wissen wollte. Und 
zwar ob der Timer im Hintergrund weiterlauft.. Wenn dem so ist ist das 
schon die Lsg meines Problems. Die Regelung braucht weniger als 10ms.

Gruß Marcel

von Bastler (Gast)


Lesenswert?

Aber sicher tut er das. Solange Du ihn nicht explizit "verstellst" (, 
d.h. anhältst, Compare Reg veränderst, ... ), wird er in exakt 10ms den 
nächsten COMPA_Int auslösen wollen. Ist SREG.I gesetzt und keine Int mit 
kleinerer Vektor-Nummer anhängig, startet die ISR nach wenigen (fixe 
Anzahl) Takten. Gelegentliches CLI/SEI (n Takte lang) führt zu Jitter 
der 10ms von eben jenen n Takten. Der Timer vergist das Zählen nicht, 
selbst wenn die ISR noch auf Freigabe warten muß. Er hat das Ereignis 
"COMPA" als 1 im entsprechenden Bit im TxIF Register abgelegt, das die 
CPU bei nächster Gelegenheit abarbeiten wird. Fast so, wie die beliebte 
"Flag setzen und in main-Schleife behandeln" Methode in Software.
Der ADC kann übrigens auch durch COMP/OVF verschiedener Timer gestartet 
werden, d.h. man könnte ADC-Start mit einem COMP (DB nachzulesen) 
starten und mit dem anderen COMP eine Wandlungszeit später die Regelung 
starten. So arbeitet der Regler mit ganz "frischen" Ist-Werten.

von Peter D. (peda)


Lesenswert?

In der Regel möchte man, daß der ADC-Wert möglichst aktuell ist, d.h. 
die Wandlung unmittelbar vor dem Regelschritt erfolgt.
Dazu läßt man den ADC im free-running Mode vom Timer triggern.
Der ADC-Interrupt ist dann Deine 10ms Zeitbasis und Du kannst sofort 
seinen Wert verarbeiten.
Achtung Fallgrube:
Das Timer-Interrupt Bit muß darin händisch gelöscht werden, sonst gibt 
es keinen weiteren ADC-Interrupt mehr!

von Stefan F. (Gast)


Lesenswert?

@Marc Vesely:

>> Wenn der Timer frei läuft kann die Software interrupts verpassen. Der
> Und wie soll das passieren ?

Zum Beispiel, weil eine priorisierte ISR länger dauert, als zwei 
Timerintervalle. Oder weil die Software Interrupts für einen zu langen 
Zeitraum gesperrt hat.

Anderer Rechner, gleiches Problem:
Lass mal einen Windows 95 (oder 98, oder ME) Rechner bei hoher CPU 
Auslastung ein paar Tage laufen und schau dann mal auf die Uhrzeit in 
der Ecke rechts unten. Die Uhr verpasst Interrupts und läuft daher zu 
langsam.

von Bastler (Gast)


Lesenswert?

Falls auf einem AVR eine ISR länger als 10ms braucht, hat man ein 
Design-Problem! Dann macht die was, was in die main-Schleife gehört, 
oder die HW ist schlicht zu klein.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Falls du wirklich mal eine durch einen anderen Interrupt unterbrechbare 
ISR brauchst, erlaubt das die AVR Lib aber auch.
Du schreibst lediglich:
1
// eine unterbrechbare ADC ISR
2
ISR(ADC_vect, ISR_NOBLOCK)   // unterbrechbare bzw. nicht-blockend
3
{
4
 // Auslesen, Abspeichern, Neu starten
5
}

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.