Interrupts ausscalten

Gast #400950
Lesenswert?

Hi,

wie kann ich während des UART-Empfangs alles Interrupts ausschalten?

Also wie ich die Interrupts ein und ausschalte weiß ich. Nur nicht an
welcher Stelle.

Also alle 300ms empfängt mein UART 16Bytes. Die empfangen und die
verarbeitung dieser 16 Bytes dauert c.a. 15ms.

Während dieser 15ms sollen alles anderen Interrupts ausgeschaltet
werden.

Nur wie?

Atmega48 in C


Gruß  Björn
Gast #400955
Lesenswert?

wenn mich nicht alles täuscht könnte man das so machen:

cli();  //alle interrupts abschalten
deine 15 ms code ausführen..
sei();  //interrupts freigeben

bitte nicht schlagen wenn falsch..

wenn mich nciht alles täuscht wird doch so oder so immer nur ein
interrupt ausgeführt.. oder machst du die verarbeitung ausserhalb des
interrupts?

g Markus
Gast #400956
Lesenswert?

@Markus:
> wenn mich nciht alles täuscht wird doch so oder so immer nur ein
> interrupt ausgeführt..

Im Prinzip täuschst Du Dich. Wenn das I-Bit im SREG mit "cli"
gelöscht wird, wird gar kein Interrupt mehr bearbeitet.

@Bjoern:
> ...an welcher Stelle.

In Zeile 42...
Gast #400957
Lesenswert?

Sowas löst man mit einem (Ring-)Puffer, nicht damit, dass man Interrupts
ein- oder ausschaltet.
Da der AVR beim Auftreten eines Interrupts die anderen eh alle sperrt,
kann man die oben gewünschte Methode eigentlich nur dadurch
realisieren, indem man in der ISR die restlichen Bytes per Polling
abruft und dann erst die ISR wieder verlässt, zwischendurch muß
vermutlich noch das RXCIE-Bit gelöscht werden.
Schön ist es nicht...
Gast #400960
Lesenswert?

@johnny.m:
>@Markus:
>> wenn mich nciht alles täuscht wird doch so oder so immer nur ein
>> interrupt ausgeführt..
>
>Im Prinzip täuschst Du Dich. Wenn das I-Bit im SREG mit "cli"
>gelöscht wird, wird gar kein Interrupt mehr bearbeitet.

ääähm..
das iss mir auch klar.. kein I-Bit = kein Interrupt..
meine unsicherheit ging eher darum ob überhaupt ein zweiter interrupt
ausgeführt werden kann wenn grad schon einer ausgeführt wird..
I-Bit iss hierbei ne zwingende voraussetzung

>@Bjoern:
>> ...an welcher Stelle.
>
>In Zeile 42...

hast du im Gegensatz zu allen anderen den code?? wunder

@Björn & inoffizieller WM-Rahul
Stimmt schon.. besser löst man das mit nem ringbuffer..
dazu gibts ne schöne lib die das erledigt..
einfach mal nach suchen

freundlichste Grüße
Markus
Gast #400961
Lesenswert?

>Ob schön oder nicht, Hauptsache das klappt.

Wie sehen denn die Daten aus? Sind das immer 16 Bytes? Haben die
vielleicht auch Start- und Endemarkierungen?

Nachteil deiner Methode ist nämlich, wenn nur 15 Bytes gesendet werden,
weil die Kommunikation unterbrochen wurde, hängt dein Controller in der
ISR fest. Da kommt man dann höchstens per Watchdog heraus...
Wenn man eine "schöne" Methode wählt, kann man das Paket notfalls
nach einem Timeout entsorgen. Und der Controller könnte noch
sinnvollere Sachen machen, als auf eine ziemlich lahme Datenübertragung
zu warten.
Das kann man sogar in der Hauptschleife machen...
Gast #400962
Lesenswert?

> meine unsicherheit ging eher darum ob überhaupt ein zweiter
> interrupt ausgeführt werden kann wenn grad schon einer ausgeführt
> wird..
> I-Bit iss hierbei ne zwingende voraussetzung

Nein, genau das I-Bit macht's aus. Egal, ob du nun in einer
Interrupt-Routine bist, oder nicht. Wenn ein neuer Interrupt auftritt,
wird der nur dann abgearbeitet, wenn das I-Bit gesetzt ist. Wenn in
deiner gerade laufenden Interrupt-Routine also da I-Bit gesetzt ist,
wird sie durch den neuen Interrupt auch unterbrochen. Allerdigs löscht
der Prozessor das I-Bit beim Eintritt in eine Interrupt-Routine
automatisch.
Gast #400963
Lesenswert?

>meine unsicherheit ging eher darum ob überhaupt ein zweiter interrupt
>ausgeführt werden kann wenn grad schon einer ausgeführt wird..

Wird er nicht.
der GCC hatte zwar mal die ISR-Variante "INTERRUPT", was aber
eigentlich eine ISR vom Typ "SIGNAL" mit integriertem "sei" am
Anfang war. Die ist aber (zum Glück) abgeschafft worden.
Der AVR schaltet sämtliche Interrupts aus, sobald er in eine ISR
gesprungen ist und gibt sie erst durch/nach "reti" (verlassen der
ISR) wieder frei.
Somit muß man zu Beginn der ISR das RXCIE-bit löschen und am Ende
wieder setzen, damit am Ende der ISR kein USART-Interrupt von einer
schon abgearbeiteten Übertragung auftreten.
Gast #400964
Lesenswert?

@Rolf Magnus
ahhhh...
nu iss der Groschen(0,05€) gefallen..
genau darüber war ich mir net so sicher..
meine wie das genau abläuft..
getz iss das etwas klarer..

Setzt der Controller auch nach dem Ausführen des Interrupt das I-Bit
wieder? Iss anzunehmen oder?

danke fürs aufklären..
so lernt man jeden tag was dazu.. freu

Gruß Markus
Gast #400966
Lesenswert?

Das I-Bit sollte gesetzt bleiben. Bei USART - RX würde ich mit Interrupt
schaffen, bei TX mit Polling. Den Rest des Codes musste dann natürlich
entsprechend der Interrupt-Prioritäten aufbauen. Hatte diesbezüglich
noch kein Problem, da der UART-RX-Interrupt ja erst dann ausgeführt
wird, wenn das UDR voll ist.
Gast #400967
Lesenswert?

>Wie sehen denn die Daten aus? Sind das immer 16 Bytes? Haben die
>vielleicht auch Start- und Endemarkierungen?

16 Bytes sind das immer, haben aber keine Start- und Endmarkierungen.

Hatte das mit einer state- Funktion realisiert. Waren 8 Erkennungsbytes
und 8 Datenbytes.

Also müsste ich beim Polling nur noch 8 Bytes übertragen.

>Wenn man eine "schöne" Methode wählt, kann man das Paket notfalls
>nach einem Timeout entsorgen. Und der Controller könnte noch
>sinnvollere Sachen machen, als auf eine ziemlich lahme
>Datenübertragung
>zu warten.
>Das kann man sogar in der Hauptschleife machen...

Versteh ich nicht?
Gast #400971
Lesenswert?

Ich hab das so gemacht:

ISR(USART_RX_vect)
{
  c = UDR0;

  tmp = 3;
        for( i = 1; i; i *= 2 )
        {
          Array_Servo1[tmp] = (c&i) ? 54 : 35;
          tmp += 2;
        }

        while(!(UCSR0A & 1<<RXC0)){}
        c = UDR0;

    tmp2 = 3;
        for( i = 1; i; i *= 2 )
        {
          Array_Servo2[tmp2] = (c&i) ? 54 : 35;
          tmp2 += 2;
        }
}

Hab da das Problem dass es nur klappt wenn ich Daten vom PC schicke.
Wenn ich Daten von einem anderen Mikrocontroller schicke haut es nicht
hin.

Was ist falsch an dem Code?
#400975
Lesenswert?

"Ob schön oder nicht, Hauptsache das klappt."


Die schönen Sachen klappen in der Regel auch schön.

Die nicht schönen Sachen (wahnsinnig elendlange 15ms alle Interrupts
blockieren) fallen einem früher oder später auf die Füße.


Also lieber gleich schön machen.


Peter
#400976
Lesenswert?

Wi schon weiter oben gesagt loest man sowas mit einem
zwischengeschalteten Buffer. Ob das jetzt ein Ringbuffer
ist oder nicht spielt erst mal keine Rolle.

Wie funktioniert das Prinzip:

In der ISR passiert so gut wie gar nichts. Das empfangene
Zeichen wird in einen Buffer gestellt und noch ein Zaehler
für die Anzahl der empfangenen Bytes erhöht. Und das wars
dann auch schon, mehr macht die ISR bei dem Empfang eines
Zeichens nicht.

volatile char Buffer[20];  // sicher ist sicher
volatile char BuffCount;

ISR(USART_RX_vect)
{
  Buffer[BuffCount] = UDR0;
  BuffCount++;
}

(Eine Fehlerabfrage gegen Bufferüberlauf wäre noch schön. Bleibt
aber als Übung für den Leser :-)

Wenn immer ein Zeichen daherkommt, wird die ISR aufgerufen und
die speichert das angekommene Zeichen mal zwischen.

Frage: Wo werden sie dann ausgewertet?
Antwort: In main() in der Hauptschleife.

Die Hauptschleife überwacht ständig den BuffCount Zähler
und wenn dort 16 Zeichen beisammen sind, werden sie ausgewertet,
getan was auch immer zu tun ist und der Buffer wieder zurückgesetzt.

Vorteil: Nirgends bremst irgendwer irgendjemanden aus. Kein
Interrupt muss abgedreht werden (ausser die Verarbeitung dauert
recht lange) und vor allen Dingen: niemand muss in einer
Warteschleife hängen um darauf zu warten, dass 16 Zeichen
übertragen wurden.
Gast #400977
Lesenswert?

Danke für den Tip!

Werd ich Morgen gleich mal ausprobieren.

>Frage: Wo werden sie dann ausgewertet?
>Antwort: In main() in der Hauptschleife.

Also in der while(1) in der Hauptschleife?

>Kein Interrupt muss abgedreht werden (ausser die Verarbeitung dauert
>recht lange) und vor allen Dingen: niemand muss in einer
>Warteschleife hängen um darauf zu warten, dass 16 Zeichen
>übertragen wurden.

Was heißt recht lange? Das ist irgendwie mein Problem. Mein Timer
Interrupt dauert ziemlich lange, ca. 1ms. Wenn ich mir mein Signal des
Timers und den UART Empfang gleichzeitig auf dem Oszi. anschaue,
erkennt man wo bzw. wann der Fehler kommt. Alle paar Sekunden treten
die beiden Interrupts gleichzeitig auf und da gibt es das Problem.

Werd aber Morgen mal versuchen beide ISR's so kurz wie möglich zu
machen. Doch beim der Timer ISR wird das nicht hinhauen, da ich die
gewünschte Signallänge nur mit der ISR erzeugen kann. In der main()
würde dies zu lange dauern.

Werd Morgen mal berichten

Gruß und Danke
#400978
Lesenswert?

> Mein Timer Interrupt dauert ziemlich lange, ca. 1ms.

Da wirst du wohl deine Strategie aendern muessen.
Du wirst 2 Timer-Eregnisse brauchen. Das eine schaltet
das Signal ein, das andere schaltet es wieder aus.

> Doch beim der Timer ISR wird das nicht hinhauen, da ich die
> gewünschte Signallänge nur mit der ISR erzeugen kann.

Und? Wie bereits gesagt: Ein Timer Ereignis schaltet ein,
ein anderes wieder aus. Warteschleifen sind in ISR absolut
tabu!
Gast #400980
Lesenswert?

Ach, ich glaub jetzt kapier ich. Du meinst bei einem Interrupt wird
eingeschaltet, beim nächsten wieder aus usw.

So mach ich das ja auch. Und stimmt gar nicht das die ISR ca. 1ms
brauch. Das ganze Signal brauch 1ms. Ein ISR-Durchlauf dauert zwischen
25 und 100µs.
#400981
Lesenswert?

Nein.
Du hast einen Timer.
Der feuert jetzt. Also schaltest du das Signal ein.
Das Signal soll für 1 ms eingeschaltet bleiben, also
programmierst du den Timer um, so dass du das nächste Timer
Ereignis nach 1 ms kriegst. Im nächsten ISR Aufruf (der nach
der programmierten 1 ms kommt) schaltest du das Signal wieder
aus und berechnest gleichzeit nach welcher Zeit das nächste
Einschalten notwendig ist. Damit wird dann der Timer wieder
umprogrammiert. Im nächsten ISR wird wieder eingeschaltet,
ausgerechnet wie die Timereinstellung fürs nächste Ausschalten
sein muss, usw.

Abhängig vom zu erzielenden Timing kann auch eine andere
Strategie besser zum Ziel führen. Du lässt den Timer so schnell
wie es geht feuern. In der ISR zählst du mit, der wievielte
AUfruf das jetzt war. Jetzt brauchst du nur noch ausrechnen, wieviele
ISR Aufrufe passieren müssen, damit die angestrebte Zeit erreicht
wird.
Beim ersten ISR AUfruf wird also das Signal eingeschaltet und
ausgerechnet nach wievielen ISR Aufrufen wieder ausgeschaltet werden
muss. Diese Anzahl wird zb in einer globalen Variablen gespeichert.
Bei jedem weiteren ISR Aufruf wird diese Variable runtergezählt.
Ist sie nicht 0, dann muss in diesem ISR nichts weiter passieren.
Ist sie aber 0, dann ist die Zeit abgelaufen und das Signal kann
ausgeschaltet werden.

Sei ein bischen kreativ. Fast alles ist erlaubt, nur Warteschleifen
sind in einer ISR absolut pfui. Warum, hast du ja jetzt gesehen.
#400982
Lesenswert?

> Interrupt dauert ziemlich lange, ca. 1ms. Wenn ich mir mein Signal
> des Timers und den UART Empfang gleichzeitig auf dem Oszi.
> anschaue, erkennt man wo bzw. wann der Fehler kommt. Alle paar
> Sekunden treten die beiden Interrupts gleichzeitig auf und da gibt
> es das Problem.

Die 1 ms hast du ja jetzt klar gestellt.
Bleibt immer noch dein USART Interrupt, der das ganze System
für eine gewisse Zeit lahmlegt, weil er auf 15 Bytes wartet.
Solange du in dieser ISR diese Warteschleife für die nächsten
15 Bytes drinnen hast, wird sich da auch nichts ändern.

Du musst dich dran gewöhnen, dass man auf einem µC Aufgaben
in kleine Happen zerteilt. Man wartet nicht aktiv, bis 16 Bytes
eingetrudelt sind (und sperrt gleichzeitig alle anderen Interrupts)
sondern man empfängt ein Zeichen und zählt mit das wievielte
das war. Wenn 16 eingetrudelt sind kann die Verarbeitung beginnen.
Gast #400983
Lesenswert?

So ählich mach ich das ja. Benutze zur Zeit den compare Interrrupt im
CTC Modus. Je nach der gewünschten Zeit wird das OCR Register auf einen
bestimmmten Wert gesetzt. Also beim ersten Interrupt das Signal
eingeschaltet, OCR Wert gesetzt und fertig. Beim zweiten Interrrupt
Signal ausschalten und wieder OCR setzen.

Das mit dem so schnell wie es geht feuern hört sich auch gut an, werd
ich mal ausprobieren.

Welcher Interrupt eignet sich den dafür am besten? Der Compare oder
eher der Overflow?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren