Hallo, im rätsle im Moment an einem Problem. Im TWI Interrupt möchte ich (zu Debugzwecken) Daten mit dem UART ausgeben. Mache ich das Interrupt gesteuert (Daten in Puffer schreiben und UDRE aktivieren) Funktioniert mein TWI nicht mehr. Es scheint als ob ich in eine Endlos Schleife gerate. Mache ich die UART per "Polling" ist alles bestens. Vermutlich sehe ich den Wald vor lauter Bäumen nicht mehr. Genutzt wird auf beiden Seiten ein atmega32. Die UART und TWI Dateien mal anbei. TWI wird nur im MT oder MR Modus betrieben ! Hat jemand einen Tipp für mich ? Danke im voraus Juergen
Aus einer ISR heraus eine Funktion aufrufen, die selber mit cli() / sei() rumspielt ist immer ... gefährlich. Wenn dir der Sendepuffer überläuft, dann hast du hier ein Problem:
1 | void uartPut ( char c ) |
2 | {
|
3 | #ifdef UART_TX_DEBUG_MODE
|
4 | #warning "UART_TX_DEBUG_MODE"
|
5 | while ( ! ( UCSRA&_BV ( UDRE ) ) ) |
6 | nop(); |
7 | |
8 | UDR = c; |
9 | |
10 | #else
|
11 | // loop until we have space
|
12 | while (txpos >= (sizeof(txbuffer)-2)) |
13 | {
|
14 | nop(); |
15 | }
|
tja. da kannst du lange warten. Die Funktion wird aus einer ISR heraus aufgerufen und zu diesem Zeitpunkt sind Interrupts daher gesperrt. Kein Interrupt -> kein Ausräumen des Sendepuffers -> Endlosschleife.
Guter Punkt. Habe den TX Puffer von 20 Zeichen auf 100 vergrößert, trotzdem noch... Werde mal einen Pin toggeln ind er Schleife, dann weis ich ob es die Schleife ist.
Das akute Problem ist das sei() in uartPut. Das kommt zur Ausführung bevor das TWINT-Flag gelöscht wird, ergo steckst du in einer endlosen TWI-Interrupt Rekursion fest. PS: Ok, nicht wirklich endlos. Nur bis der Puffer durch die Rekursion voll ist, dann schlägt das von Karl Heinz erwähnte Problem zu.
Ich habe uartPutc wie folgt geändert:
1 | #define BACKLIGHT_PIN PD5
|
2 | #define BACKLIGHT_DDR DDRD
|
3 | #define BACKLIGHT_PORT PORTD
|
4 | #define BACKLIGHT_ON (BACKLIGHT_PORT |= _BV(BACKLIGHT_PIN))
|
5 | #define BACKLIGHT_OFF (BACKLIGHT_PORT &= ~_BV(BACKLIGHT_PIN))
|
6 | #define BACKLIGHT_TOGGLE (BACKLIGHT_PORT ^= _BV(BACKLIGHT_PIN))
|
7 | |
8 | void uartPut ( char c ) |
9 | {
|
10 | #ifdef UART_TX_DEBUG_MODE
|
11 | #warning "UART_TX_DEBUG_MODE"
|
12 | while ( ! ( UCSRA&_BV ( UDRE ) ) ) |
13 | {
|
14 | nop(); |
15 | }
|
16 | |
17 | UDR = c; |
18 | |
19 | #else
|
20 | // loop until we have space
|
21 | while (txpos >= (sizeof(txbuffer)-2)) |
22 | {
|
23 | BACKLIGHT_TOGGLE; |
24 | nop(); |
25 | }
|
26 | ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) |
27 | {
|
28 | txbuffer[txpos] = c; |
29 | txpos++; |
30 | UCSRB |= _BV (UDRIE); // Enable INT, also triggers start sending |
31 | }
|
32 | #endif
|
33 | }
|
mit großem Puffer (100) ist das Problem weg, mit normalen Puffer (20) ist es da. Die LED (BACKLIGHT) toggelt, hum....
Mir ist jetzt klar, was passiert. eine Lösung ohne den Puffer zu vergrößern sehe ich im Moment nicht. Danke für den Wink mit dem Zaunpfahl. Gruss Juergen
Jürgen Sachs schrieb: > Mir ist jetzt klar, was passiert. eine Lösung ohne den Puffer zu > vergrößern sehe ich im Moment nicht. > Danke für den Wink mit dem Zaunpfahl. Das klingt jetzt nach einer Binsenweisheit: In einer ISR gibt es keine lang andauernden Operationen. Insbesondere gibt es keine Ausgaben auf LCD oder UART. Das alles dauer viel zu lang. Und wie du gesehen hast, ist auch eine High-Tech Lösung mit Ausgabe in einem Interrupt keine Lösung. Eine ISR darf/soll nicht von anderen Interrupts abhängen, sondern muss auf jeden Fall immer komplett durchlaufen. Was eine ISR machen kann: Sie kann mit ein paar globalen Variablen arbeiten und dort bestimmte Werte reinschreiben, die dann in der Hauptschleife dafür sorgen, dass die Texte ausgegeben werden. Das hat dann auch den Vorteil, dass die Ausgaben geordnet erfolgen und dir eine Ausgabe in einer ISR nicht mitten in eine andere Ausgabe in der Hauptschleife reinplatzt, nur weil der Interrupt zufällig dann gekommen ist, wenn in der Hauptschleife auch gerade eine Ausgabe läuft.
Ja, das ist mir bewusst. Aber die Daten muss ich, für eine spätere Bearbeitung, in einen Puffer schreiben, egal ob in Mainline oder im Interrupt verarbeitet wird. Ob ich den nun in Mainline oder im Interrupt wieder leere, macht keinen Unterschied. Auf mein Problem treffe ich immer. Ist der Puffer voll, wird nichts abgearbeitet, solange ich in der ISR des TWI stecke. Später wird natürlich in Mainline das ganze ausgewertet und ein Display mit Leben gefüllt. Dazu gehören leider auch Variable Texte die daher über TWI kommen und nicht im Flash des Controllers am Display stecken können. Aber diese "RX Puffer voll" Situation bekomme ich wohl immer....
Jürgen Sachs schrieb: > Aber diese "RX Puffer voll" Situation bekomme ich wohl immer.... Jep. Ist wie im täglichen Leben. Wenn dir der Chef sukzessive mehr Arbeit auf den Schreibtisch legt als du abarbeiten kannst, dann wird irgendwann der Schreibtisch voll und du urlaubsreif. Dann muss man auch mal den Mut haben und eine Übertragung mit einem Bufferoverrun als gescheitert erklären und die Daten verwerfen, bzw. der Gegenstelle mitteilen, dass sie sich doch bitte etwas zurückhalten soll.
Wenn Du der I2C-Master bist, hast Du doch alle Zeit der Welt. Sende einfach das Debugpaket komplett aus dem Puffer raus und erst dann mach den nächsten I2C-Transfer. Niemand zwingt Dich, Deine CPU mit haufenweise I2C-Zeugs vollzupumpen. Der Trick bei effizienter Programmierung ist, mache alles immer nur so oft, wie nötig. Peter
Peter Dannegger schrieb: > Wenn Du der I2C-Master bist, hast Du doch alle Zeit der Welt. Bei den Debugausgaben gebe ich dir (Bedingt) recht. Die sind jetzt auch eher nur für den Stresstest um genau solche Schwachpunkte auf zu decken. Das es sich um ein RC Fernbedienung handelt, kann ich natürlich nicht ewig Pause machen. In Mainline polle ich die Analog und Digitaleingänge. Per Interrupt mache ich das Timing (Zeitbasis), UART senden und Empfangen, TWI senden und Empfangen. Auch die Auswertung der Befehle die per UART, Digital Input usw ankommen werden in Mainline verarbeitet. Dort Erzeuge ich die entsprechenden Daten und sende diese entweder per UART (Hängt ein BTM222 Funkmodul dran) an den RC Empfänger oder z.B. auch per TWI an das LCD. Der Prozessor am LCD übernimmt die Grafischen ausgaben und die Auswertung der Tastatur, Drehgeber usw. So hoffe ich das Display "tauschbar" machen zu können. Alles andere hängt im Hauptprozessor. Jetzt kann ich die Ausführung nicht "ewig" blockieren. Beim RC-Empfänger, wo ich über TWI erweitern kann, ist das ganze noch viel kritischer. Die PWM wird ja per Interrupt erzeugt, aber in der Syncpause wird alles blockiert, bis die Mainline den Zyklus vorbereitet hat. Lange Wartezeiten sind daher nicht drin. Daher war mein Ansatz, wenn immer Möglich alles nur in Puffer zu schreiben und dann per Interrupt zu verarbeiten. Im schlimmsten Fall kann es sein, das eine Aktion am LCD (Menüauswahl, Setting verändern) ein Datenpacket per TWI an den TX CPU geht, diese per Funk an den RX weiterleitet und der es wieder per TWI an den Empfänger. Eine Eventuelle Antwort auch wieder zurück :-) Der Vorteil, das Modell kann in Echtzeit konfiguriert werden. Auch kann man so Modelle einfach weiter geben von einem TX zum nächsten, etc.... Jetzt muss ich mir hier eben eine Lösung überlegen. Bisher dachte ich "Im Sonderfall Puffer voll, warten wir eben". Das innerhalb eines Interrupts der Puffer nicht mehr leer gemacht wird, daran habe ich nicht gedacht. Eine Möglichkeit wäre natürlich in diesem Sonderfall UDR per polling zu bedinenen. Das will ich jetzt mal nicht umsetzen :-) Danke für die Hilfe Juergen
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.