Forum: Compiler & IDEs Problem Uart aus TWI


von Jürgen S. (jsachs)


Angehängte Dateien:

Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

Wie gross ist dein Sendepuffer?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Jürgen S. (jsachs)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Jürgen S. (jsachs)


Lesenswert?

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

von Jürgen S. (jsachs)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Jürgen S. (jsachs)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Jürgen S. (jsachs)


Lesenswert?

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
Noch kein Account? Hier anmelden.