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
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")"
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
@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...
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...
Wie funktioniert denn das mit dem Polling? Ob schön oder nicht, Hauptsache das klappt.
>Wie funktioniert denn das mit dem Polling?
Das ist im Datenblatt beschrieben (Beispiele bei der
USART-Beschreibung).
@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
>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...
> 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.
>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.
@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
>Setzt der Controller auch nach dem Ausführen des Interrupt das I-Bit >wieder? Iss anzunehmen oder? ja.
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.
>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?
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; //
>while(!(UCSR0A & 1<<7))
while(!(UCSR0A & 1<<RXC))
sieht schöner aus, weil lesbarer.
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?
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
Ahja und dann natürlich alle Interrups deaktivieren, bzw. macht er das ja selber und die Ints werden einfach nachgeschoben.
Wie kann ich den CTS auf 0 setzen? Hab doch gar keine Steuerleitungen wenn ich von Controller zu Controller sende.
"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
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.
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
> 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!
>Ein Timer Ereignis schaltet ein, >ein anderes wieder aus. Warteschleifen sind in ISR absolut >tabu! Was heißt zwei Timer Ereignisse? 2 Timer benutzen?
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.
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.
> 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.
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?
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?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.