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
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.
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.
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
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
voidTim5_init();
4
voidRegelung(void);
5
uint16_tADC_Read(uint8_tChannel);
6
7
8
volatileuint16_tstage=0;
9
volatileuint8_tstatus=1;
10
volatileuint8_tt_cst=100;
11
volatileuint8_tRegelFlag=0;
12
13
uint16_tAdcVal=0;
14
15
// ***main nur zur Kommunikation***
16
voidmain(void)
17
{
18
Tim5_init();
19
20
while(1)
21
{
22
if(RegelFlag==1)Regelung();
23
}
24
}
25
26
/*** Initialisierung von Timer5 (Unvollstandig !!!) */
27
voidTim5_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
voidRegelung(void)
35
{
36
AdcVal=ADC_Read(7);
37
/*
38
Regelung
39
*/
40
RegelFlag=0;
41
}
42
43
uint16_tADC_Read(uint8_tChannel)
44
{
45
return987;// 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...
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.
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
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!
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.
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.
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.
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
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?
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
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.
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!
@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.
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.