www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Interrupts ausscalten


Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ups, das Empfangen....

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welcher Controller? Welche Programmiersprache?

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Atmega48 in C

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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")"

Autor: Markus Boremski (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie funktioniert denn das mit dem Polling?

Ob schön oder nicht, Hauptsache das klappt.

Autor: inoffizieller WM-Rahul (Gast)
Datum:

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

Autor: Markus Boremski (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus Boremski (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

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

ja.

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Bjoern (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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; //

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>while(!(UCSR0A & 1<<7))

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

sieht schöner aus, weil lesbarer.

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: BennyS (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: BennyS (Gast)
Datum:

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

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie kann ich den CTS auf 0 setzen?

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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lieber die main-while als die ISR.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.