Hi, ich plane im Moment ein GCC-Programm und treffe dabei auf folgendes Problem: Mittels eines Timer-Interrupts wird eine Uhr "angetrieben". Ich habe ca. 20 Interrupts pro Sekunde. Dies impliziert auch, dass kein Interrupt ausfallen darf, sonst geht die Uhr nach. Wenn ich nun eine BIDIREKTIONALE serielle Kommunikation aufbauen muss, benötige ich ja den uart-interrupt. Wie macht man das? Wie kann man Kommandos über die serielle Schnittstelle in das Programm eingeben während die Uhr läuft? Es tritt ja sicher der Fall ein, dass ein Interrupt den anderen "verhindert". Damit würde aber die Uhr nach-gehen oder es würde uart-Zeichen verloren gehen? Wer kann den Knoten lösen ? Gibt es irgendwo ein Beispielprogramm ? Gruß Andreas
Andreas St. wrote: > Es tritt > ja sicher der Fall ein, dass ein Interrupt den anderen "verhindert". Woher weißt Du das? Nicht vermuten, sondern ausrechnen! Bei 20MHz kommt ein 20Hz Timerinterupt alle 1.000.000 Zyklen. Wenn also beide Interrupts zusammen länger als 1.000.000 Zyklen dauern, kannst Du einen verlieren. Wenn Du allerdings derart riesenlange Interrupts bastelst, ist irgendwas grundsätzlich falsch. Interrupts sollten möglichst nicht länger als 100..1000 Zyklen sein. Peter
>Es tritt >ja sicher der Fall ein, dass ein Interrupt den anderen "verhindert". Das passiert nicht, wenn die Regel eingehalten wird, dass alle Interrupts und alle Critical Sections in Deinem Programm grundsätzlich so kurz wie möglich zu halten sind. Tritt eine Interruptbedingung "B" auf, während gerade ein anderer Interrupt "A" bearbeitet wird oder sich das Programm zufällig in einer Critical Section aufhält, ist das kein Problem: Das zu "B" gehörige Interrupt-Flag wird automatisch von der Hardware gesetzt, und der "B"-Interrupthandler angesprungen, sobald das möglich ist (sobald das I-Bit im Statusregister wieder gesetzt wird, z. B. durch das "reti" am Ende des "A"-Interrupthandelers). In einem solchen Fall spricht man von "pending interrupt" - der Interrupt steht an und wartet, bis er gehandelt werden kann. Würde Dein Progamm zu lange in einer Interrupt-Behandlung oder in einer Critical Setion verweilen, könnten andere Interrupts "verloren gehen". Ein schon gesetztes Pending-Flag würde dann nochmals gesetzt, aber der Handler nur einmal abgearbeitet.
Hi, > Das zu "B" gehörige > Interrupt-Flag wird automatisch von der Hardware gesetzt, und der > "B"-Interrupthandler angesprungen, sobald das möglich ist (sobald das > I-Bit im Statusregister wieder gesetzt wird, z. B. durch das "reti" am > Ende des "A"-Interrupthandelers). In einem solchen Fall spricht man von > "pending interrupt" - der Interrupt steht an und wartet, bis er > gehandelt werden kann. OK - das löst das Problem wenn ich beim Start der Interupt-Routine A das I-Bit lösche und am Ende wieder setze. Wenn während der Bearbeitungszeit von Routine A die Bedingung für B eintrat (z.B. Ein Zeichen an den Controller gesandt werden soll) Beginnt sofort die Bearbeitung der Routine B (UART). Bitte verifiziert folgende Überlegung: Bei 9600 Baud und kein Stop, Paritiy oder sonstwas BIT dauert ein BIT 60s/(9600*8)= 781us. Ich muss also sicherstellen, dass meine Interrupt-Routine A innerhalb dieser Zeit fertig wird und ich in der Routine B mit dem Lesen des BITs begonnen habe sonst ist es weg und damit kann das Zeichen nicht erkannt werden. Stimmt diese ÜBerlegung ? Gruß Andreas
Ein UART-Interrupt wird erst ausgeführt wenn das Zeichen komplett empfangen ist. Das sind je nach start/stopbit Konfiguration ca. 10 bit. Gruß Roland
Roland Praml wrote: > Ein UART-Interrupt wird erst ausgeführt wenn das Zeichen komplett > empfangen ist. Das sind je nach start/stopbit Konfiguration ca. 10 bit. > OK? - Wie lange steht das Zeichen dann zum Lesen zur Verfügung ? Bis zum nächsten Interrupt ? Gruß Andreas
Andreas St. wrote: > Roland Praml wrote: >> Ein UART-Interrupt wird erst ausgeführt wenn das Zeichen komplett >> empfangen ist. Das sind je nach start/stopbit Konfiguration ca. 10 bit. >> > OK? - Wie lange steht das Zeichen dann zum Lesen zur Verfügung ? Bis zum > nächsten Interrupt ? Bis das nächste Byte komplett empfangen wurde. AVR haben einen 1 Byte breiten Buffer für den UART Empfang.
> OK - das löst das Problem wenn ich beim Start der Interupt-Routine A das > I-Bit lösche und am Ende wieder setze. Du brauchst dich darum nicht kümmern. Der AVR handahbt das Interrupt sperren und freigeben selbsttätig. Beim Eintritt in eine Interrupt Routine werden Interrupts gesperrt und beim abschliessenden return werden sie wieder freigegeben.
Andreas St. wrote: > Bei 9600 Baud und kein Stop, Paritiy oder sonstwas BIT dauert ein BIT > 60s/(9600*8)= 781us. Wo kommen die 60 her? 1 Baud = 1 Zeichen pro 1 Sekunde
Upps, Karl heinz Buchegger wrote: > > Wo kommen die 60 her? > 1 Baud = 1 Zeichen pro 1 Sekunde Du hast recht! Daher kam auch mein Einheitenproblem .... So ists richtig: 1/ (9600 * 8 [bit/s]) = 13us Nach der Erklärung mit dem Puffer bleibt mir aber 1 / 9600 = 104us Zeit? Stimmt das jetzt ? Gruß und nochmals Danke ! Andreas
Karl heinz Buchegger wrote: > Bis das nächste Byte komplett empfangen wurde. > AVR haben einen 1 Byte breiten Buffer für den UART Empfang. Alle halbwegs aktuellen AVRs (ab ca. Mega8/16) besitzen zusätzlich zum Schieberegister 2 Empfangspuffer. Das bedeutet, dass erst dann Daten verloren gehen, wenn insgesamt 3 Bytes vollständig empfangen werden ohne dass eines davon abgeholt wird. Man muss sich schon recht bös anstellen, um bei moderaten Bitraten dabei Daten zu verlieren. Denn bei 9600bps müssen dazu die Interrupts für ca. 3ms blockiert werden.
Andreas St. wrote: > Nach der Erklärung mit dem Puffer bleibt mir aber 1 / 9600 = 104us Zeit? > Stimmt das jetzt ? Nein. Dich interessieren hier Bytes, nicht Bits. Ein Byte wird seriell mit Start- und Stopbit in 10 Bits codiert, d.h. im Empfangspuffer der UART können die Bytes nicht schneller als alle 10*104µs=1040µs eintrudeln.
>OK - das löst das Problem wenn ich beim Start der Interupt-Routine A das >I-Bit lösche und am Ende wieder setze. Gelöscht wird das I-Bit im SREG automatisch durch die Interrupt-Logik des Controllers, in dem Moment, wo ein Interrupt gehandelt wird. Das Setzen des I-Bits obliegt jedoch dem Programmierer, üblicherweise geschieht es mit "reti" am Ende des Interupthandlers. >Wenn während der Bearbeitungszeit von Routine A die Bedingung für B >eintrat (z.B. Ein Zeichen an den Controller gesandt werden soll) Beginnt >sofort die Bearbeitung der Routine B (UART). Nein, eben nicht sofort, sondern erst, wenn das Programm aus Routine A ins Hauptprogramm zurückgekehrt ist. Ist dann wegen des "reti" am Ende der Routine A das I-Bit wieder gesetzt, wird im Hauptprogramm noch genau eine Instruktion abgearbeitet und DANN der über das entsprechende Flag "vorgemerkte" Interrupt B gehandelt (sollten mehrere Interrupts anstehen, gehts nach der Interruptpriorität). >Bei 9600 Baud und kein Stop, "kein Stop" gibts nicht. Üblich sind 1 Startbit, 8 Datenbits, 1 Stopbit. Macht zusammen 10 übertragene Bits pro Nutzbyte. >Paritiy oder sonstwas BIT dauert ein BIT 60s/(9600*8)= 781us. Was rechnest Du für nen Murks? 9600 Baud = 9600 Bit/s = 960 * 10 Bit/s = 960 Nutzbytes/s Die Übertragung eines Nutzbytes dauert somit 1/960 s = 1.041666 ms >Ich muss also sicherstellen, dass meine >Interrupt-Routine A innerhalb dieser Zeit fertig wird und ich in der >Routine B mit dem Lesen des BITs begonnen habe sonst ist es weg und >damit kann das Zeichen nicht erkannt werden. Von der UART-Empfangseinheit werden stets Bytes gelesen, nicht Bits. Für das Auslesen jedes eingetroffenen Bytes stehen Dir tatsächlich "nur" 1.041666 ms zur Verfügung. Das ist aber kein Problem, denn ein AVR kann bei 8 MHz Taktfrequenz während dieser Zeitspanne ca. 5000 Instruktionen abarbeiten, und das reicht hundertmal aus, um das Byte an der richtigen Stelle im UART-Empfangspuffer (= Speicherbereich im SRAM) abzuspeichern, und die Interruptroutine danach wieder zu verlassen (mehr ist ja nicht nötig). Die Prüfung, ob der Puffer voll ist plus Ingangsetzen der entsprechenden Aktionen erfolgt natürlich im Hauptprogramm.
Andreas Kaiser wrote: > Alle halbwegs aktuellen AVRs (ab ca. Mega8/16) besitzen zusätzlich zum > Schieberegister 2 Empfangspuffer. Das bedeutet, dass erst dann Daten > verloren gehen, wenn insgesamt 3 Bytes vollständig empfangen werden ohne > dass eines davon abgeholt wird. Ah. Das wuste ich noch nicht. Um ehrlich zu sein: Ich hab mich auch noch nie darum gekümmert. In der Zeit, die 1 Zeichen bei 9600 Baud zur Übertragung braucht, kann man zwischendurch noch sooooo viel erledigen :-)
AVRFan wrote: >>OK - das löst das Problem wenn ich beim Start der Interupt-Routine A das >>I-Bit lösche und am Ende wieder setze. > > Gelöscht wird das I-Bit im SREG automatisch durch die Interrupt-Logik > des Controllers, in dem Moment, wo ein Interrupt gehandelt wird. Das > Setzen des I-Bits obliegt jedoch dem Programmierer, üblicherweise > geschieht es mit "reti" am Ende des Interupthandlers. Er programmiert in C. Da braucht er sich auch um das reti am Ende des Interrupt Handlers nicht kümmern.
Hi, >>Wenn während der Bearbeitungszeit von Routine A die Bedingung für B >>eintrat (z.B. Ein Zeichen an den Controller gesandt werden soll) Beginnt >>sofort die Bearbeitung der Routine B (UART). > > Nein, eben nicht sofort, sondern erst, wenn das Programm aus Routine A > ins Hauptprogramm zurückgekehrt ist. Ist dann wegen des "reti" am Ende > der Routine A das I-Bit wieder gesetzt, wird im Hauptprogramm noch genau > eine Instruktion abgearbeitet und DANN der über das entsprechende Flag > "vorgemerkte" Interrupt B gehandelt (sollten mehrere Interrupts > anstehen, gehts nach der Interruptpriorität). Ja, ich habs verstanden. Konnte heute Morgen noch nicht klar gradeaus schreiben... > Was rechnest Du für nen Murks? > > 9600 Baud = 9600 Bit/s = 960 * 10 Bit/s = 960 Nutzbytes/s > > Die Übertragung eines Nutzbytes dauert somit 1/960 s = 1.041666 ms > Auch das mit Start- und Stop-BIT ist klar. Keine Ahnung wie ich auf 8 BIT kam. 10 BIT sind richtig. > ... und das reicht hundertmal aus, um das Byte an der richtigen > Stelle im UART-Empfangspuffer (= Speicherbereich im SRAM) abzuspeichern, > und die Interruptroutine danach wieder zu verlassen (mehr ist ja nicht > nötig). Die Prüfung, ob der Puffer voll ist plus Ingangsetzen der > entsprechenden Aktionen erfolgt natürlich im Hauptprogramm. Äh? Ich will ja mit dem Daten, die ich von der Schnittstelle empfange was anfangen. Nehmen wir mal an, ich möchte eine 8-BIT Zahl einlesen: Mir ist klar, dass ich die einzelnen Ziffern (maximal 3) im Anwendungsprogramm zwischenspeichern muss, da ich ja nachher eine Zahl zwischen 0 und 255 erzeugen möchte - die höchstwertige Stelle kommt ja zuerst. Was hat das aber alles mit "um das Byte an der richtigen Stelle im UART-Empfangspuffer (= Speicherbereich im SRAM) abzuspeichern" zu zun? Oder meinst Du mit "UART-Empfangspuffer" z.B. mein obiges Beispiel? Gruß Andreas
Andreas St. wrote: >> ... und das reicht hundertmal aus, um das Byte an der richtigen >> Stelle im UART-Empfangspuffer (= Speicherbereich im SRAM) abzuspeichern, >> und die Interruptroutine danach wieder zu verlassen (mehr ist ja nicht >> nötig). Die Prüfung, ob der Puffer voll ist plus Ingangsetzen der >> entsprechenden Aktionen erfolgt natürlich im Hauptprogramm. > > Äh? > > Ich will ja mit dem Daten, die ich von der Schnittstelle empfange was > anfangen. Schon. Aber das machst du normalerweise nicht im Interrupt. Im Interrupt wird üblicherweise das Zeichen in einem Puffer zwischengespeichert. Mehr nicht. Nicht ganz: Im Interrupt wird auch noch geprüft, ob ein bestimmtes Zeichen angekommen ist, welches das Ende dieser einen Übertragung signalisiert. Dann wird von der ISR eine globale Variable auf 1 gesetzt an der das Hauptprogramm erkennen kann, dass in diesem Zwischenpuffer eine komplette Eingabe vorliegt und ausgwertet werden muss. > Nehmen wir mal an, ich möchte eine 8-BIT Zahl einlesen: > > Mir ist klar, dass ich die einzelnen Ziffern (maximal 3) im > Anwendungsprogramm zwischenspeichern muss, da ich ja nachher eine Zahl > zwischen 0 und 255 erzeugen möchte - die höchstwertige Stelle kommt ja > zuerst. OK. Wenn es nur um diese Aufgabenstellung geht. Die ist einfach genug, dass man sie auch in der ISR machen könnte. In dem Fall braucht man dann auch nicht zwischenspeichern. Bei jeder ISR wird gerechnet: Neue Zahl = Alte Zahl * 10 + neue Ziffer Aber diesr Fall ist eher die Ausnahme. Meist hat man ja irgendwelche Steuerkommandos, die per UART übertragen werden. In dem Fall ist es das einfachste zunächst einfach mal alle Zeichen, bis zum Ende Zeichen, in einen Puffer zwischenzuspeichern und erst dann wenn die Übertragung fertig ist, auszuwerten (aber nicht in der ISR auswerten. Die ist dafür nicht zuständig!) so ungefähr:
1 | ...
|
2 | |
3 | uint8_t LineCnt; |
4 | char InLine[80]; |
5 | volatile uint8_t LineReceived; |
6 | |
7 | ISR( RX ... ) |
8 | {
|
9 | char c = UDR; |
10 | |
11 | if( c == '\n' ) { |
12 | LineReceived = 1; |
13 | InLine[LineCnt] = '\0'; |
14 | LineCnt = 0; |
15 | }
|
16 | else { |
17 | InLine[LineCnt++] = c; |
18 | }
|
19 | }
|
20 | |
21 | |
22 | int main() |
23 | {
|
24 | char RecLine[80]; |
25 | |
26 | ....
|
27 | |
28 | LineCnt = 0; |
29 | LineReceived = 0; |
30 | |
31 | sei(); |
32 | |
33 | while( 1 ) { |
34 | |
35 | if( LineReceived ) { |
36 | // mach was mit dem empfangenen Text, zb umkopieren
|
37 | // damit im Hintergrund wieder ein neuer Text empfangen
|
38 | // werden kann
|
39 | cli(); |
40 | strcpy( RecLine, InLine ); |
41 | LineReceived = 0; |
42 | sei(); |
43 | |
44 | // und jetzt kann ausgewertet werden, was der Benutzer
|
45 | // schon wieder will
|
46 | |
47 | if( strcmp( RecLine, "Help" ) == 0 ) { |
48 | // es war das help Kommando
|
49 | |
50 | else if( strcmp( RecLine, "CLR" ) == 0 ) { |
51 | // es war das clear Kommando
|
52 | }
|
53 | |
54 | ...
|
55 | |
56 | }
|
57 | }
|
58 | }
|
WIe gesagt: So ungefähr. Wie man das dann genau umsetzt, hängt auch von der konkreten Aufgabenstellung ab.
@ Karl heinz Buchegger (kbuchegg) >WIe gesagt: So ungefähr. Wie man das dann genau umsetzt, hängt auch >von der konkreten Aufgabenstellung ab. Ja, Scheisse. Die Frage kommt nun zum 1001 mal, und du hast sie bestimmt 999 mal so ausführlich beantwortet? Wäre es nicht höchste Eisenbahn, das mal in einen ORDENTLICHEN Wikiartikel mit getestetem, vollständigem Beispiel zu packen? Oder willst das noch 10000 mal zu 99% erklären (was dann wieder endlose Fragen auslöt und wieder 1001 Erklärung). MFG Falk
>Ich will ja mit dem Daten, die ich von der Schnittstelle empfange was >anfangen. Logisch. >Nehmen wir mal an, ich möchte eine 8-BIT Zahl einlesen: >Mir ist klar, dass ich die einzelnen Ziffern (maximal 3) im >Anwendungsprogramm zwischenspeichern muss, da ich ja nachher eine Zahl >zwischen 0 und 255 erzeugen möchte - die höchstwertige Stelle kommt ja >zuerst. Um eben dieses Zwischenspeichern geht es. Du definierst Dir irgendeinen zusammenhängenden Bereich im SRAM als "UART-Empfangspuffer" und stapelst die eintreffenden UART-Bytes darin auf (je ein Byte in jedem Interrupt), bis der "Block" komplett ist. Das ist die minimalst mögliche Aktion, bei der gewährleistet ist, dass kein eintreffendes Byte verloren geht. Mehr muss im Interrupthandler nicht geschehen und wegen dem Grundsatz "Alle Interrupthandler so kurz wie möglich halten" sollte man auch nicht sehr viel mehr im Interrupthandler veranstalten. Die Aktion, die bei "Block vollständig empfangen" abhängig vom Inhalt des Blocks ausgeführt werden soll, wird erst im Hauptprogramm synchron mit den restlichen Vorgängen (Tasks) Deiner Software in Gang gesetzt. Solche - teilweise rechenaufwendigen - Aktionen können z. B. sein die Umwandlung einer Zahl in einen String für die Anzeige auf einem Display, das Formatieren einer CF-Karte, das Einschalten eines Motors, das Auslesen eines Sensors, das Beschreiben eines EEPROM-Bytes etc.
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.