Forum: Mikrocontroller und Digitale Elektronik Interrupts ausscalten


von Bjoern (Gast)


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

von Bjoern (Gast)


Lesenswert?

Ups, das Empfangen....

von johnny.m (Gast)


Lesenswert?

Welcher Controller? Welche Programmiersprache?

von Bjoern (Gast)


Lesenswert?

Atmega48 in C

von johnny.m (Gast)


Lesenswert?

Was für ein C? In AVR-GCC-C geht das mit "cli();" zum Ausschalten und
"sei();" zum Wiedereinschalten, in CodeVision mit "#asm("cli")"
bzw. "#asm("sei")"

von Markus Boremski (Gast)


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

von johnny.m (Gast)


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...

von inoffizieller WM-Rahul (Gast)


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...

von Bjoern (Gast)


Lesenswert?

Wie funktioniert denn das mit dem Polling?

Ob schön oder nicht, Hauptsache das klappt.

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>Wie funktioniert denn das mit dem Polling?
Das ist im Datenblatt beschrieben (Beispiele bei der
USART-Beschreibung).

von Markus Boremski (Gast)


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

von inoffizieller WM-Rahul (Gast)


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...

von Rolf Magnus (Gast)


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.

von inoffizieller WM-Rahul (Gast)


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.

von Markus Boremski (Gast)


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

von inoffizieller WM-Rahul (Gast)


Lesenswert?

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

ja.

von Sonic (Gast)


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.

von Bjoern (Gast)


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?

von Bjoern (Gast)


Lesenswert?

Könnte die Polling in der ISR so ungefähr funktionieren?


while(!(UCSR0A & 1<<7)) {}
   data = UDR0; //
   while(!(UCSR0A & 1<<7)) {}
   data = UDR0; //
   while(!(UCSR0A & 1<<7)) {}
   data = UDR0; //

von inoffizieller WM-Rahul (Gast)


Lesenswert?

ja

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>while(!(UCSR0A & 1<<7))

while(!(UCSR0A & 1<<RXC))

sieht schöner aus, weil lesbarer.

von Björn (Gast)


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?

von BennyS (Gast)


Lesenswert?

Ich mein wenns Uart ist wieso nicht mit den Steuerleitungen arbeiten,
die sind doch genau für sowas gedacht?
Einfach während den 15ms CTS auf LOW. Natürlich gehen durch die
HArdware Flusssteuerung ein zwei Pins verloren aber dafür ist es die
sauberste Lösung bei so langen Wartezeiten.


http://de.wikipedia.org/wiki/EIA-232

von BennyS (Gast)


Lesenswert?

Ahja und dann natürlich alle Interrups deaktivieren, bzw. macht er das
ja selber und die Ints werden einfach nachgeschoben.

von Björn (Gast)


Lesenswert?

Wie kann ich den CTS auf 0 setzen?

Hab doch gar keine Steuerleitungen wenn ich von Controller zu
Controller sende.

von Peter D. (peda)


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

von Karl heinz B. (kbucheg)


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.

von Björn (Gast)


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

von Karl H. (kbuchegg)


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!

von Björn (Gast)


Lesenswert?

>Ein Timer Ereignis schaltet ein,
>ein anderes wieder aus. Warteschleifen sind in ISR absolut
>tabu!

Was heißt zwei Timer Ereignisse? 2 Timer benutzen?

von Björn (Gast)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von Björn (Gast)


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?

von Björn (Gast)


Lesenswert?

Denke mir immer nur, dass ich doch auch nicht alles in die while(1) in
der main() packen kann. Weil irgendwann wird die doch auch super lang.
Oder nicht?

von inoffizieller WM-Rahul (Gast)


Lesenswert?

Lieber die main-while als die ISR.

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.