Forum: Compiler & IDEs problem mit peter fleurys uart lib und interrupts


von ben (Gast)


Lesenswert?

Hi,

erstmal danke an Peter Fleury für die wunderbare lib :-)

habe allerdings ein kleines Problem.

wenn ich aus einer Interruptroutine (z.B. Timerinterrupt) heraus 
mehrmals die funktion uart_putc aufrufe und deutlich mehr sende, als die 
Buffergröße hängt sich mein programm auf. Bei einer Buffergröße von 8 
Byte funktioniert es z.B. noch, wenn ich 15 Byte sende, bei 16 Byte ist 
dann schluss. Es ist aber nicht die doppelte Bytezahl bei 64Byte Buffer 
schon bei etwa 70Byte Schluss
1
//uart TX Buffer = 8
2
ISR(irgendwas)
3
{
4
    for(uchar8 i=0; i<16;i++)
5
    //for(uchar8 i=0; i<15;i++)  geht noch
6
    {
7
  uart_putc(56);
8
    }
9
    //Ab hier wird kein Code mehr ausgeführt
10
}

oder
1
//uart TX Buffer = 64
2
ISR(irgendwas)
3
{
4
    for(uchar8 i=0; i<70;i++)
5
    //for(uchar8 i=0; i<64;i++)  geht noch
6
    {
7
  uart_putc(56);
8
    }
9
    //Ab hier wird kein Code mehr ausgeführt
10
}

Außerhalb vin Interruptroutinen habe ich das Problem nicht und wenn ich 
den TX Buffer vergrößere tritt es auch nicht auf.

Habe auch obige schleife experimentell mit cli(); sei(); umschlossen um 
auszuschließen, dass mir vielleicht ein anderer Interrupt 
dazwischenfunkt, das Verhalten ist jedoch das selbe.

(und klar, kann ich den Buffer größer machen , die Anzahl der zu 
sendenden Bytes ist aber nicht kostant und kann unter Umständen auch mal 
groß werden.)

Bin Ratlos, danke für Hilfe

von Tom M. (tomm) Benutzerseite


Lesenswert?

> Habe auch obige schleife experimentell mit cli(); sei(); umschlossen um
> auszuschließen, dass mir vielleicht ein anderer Interrupt
> dazwischenfunkt, das Verhalten ist jedoch das selbe.

Vermutlich müsstest du's genau andersrum machen: Interrupts freigeben, 
damit der TX Interrupt (USART0_TX_vect o.ä.) ausgeführt wird, damit 
leert sich der Sendepuffer.

von ben (Gast)


Lesenswert?

>Vermutlich müsstest du's genau andersrum machen: Interrupts freigeben,
>damit der TX Interrupt (USART0_TX_vect o.ä.) ausgeführt wird, damit
>leert sich der Sendepuffer.

Das macht eigendlich schon uart_putc:
1
/* enable UDRE interrupt */
2
    UART0_CONTROL    |= _BV(UART0_UDRIE);

von Tom M. (tomm) Benutzerseite


Lesenswert?

Auf dem AVR hast du aber per Default keine "nested interrupts": Der 
Interrupt Handler für die USART wird deinen Interrupt nicht unterbrechen 
(der blockiert ja, weil der Ringpuffer voll ist).

von ben (Gast)


Lesenswert?

tatsächlich!

dachte immer die avr interrups wären per default nested, aber hier steht 
das gegenteil: 
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

wieder was gelernt..

werde es mal ausprobieren, danke. Habe leider gerade meinen avr 
"programmiert" ohne die Betriebsspannung anzuhängen und seitdem reagiert 
er nicht mehr. Muss also bis Montag warten....:-(

von Stefan E. (sternst)


Lesenswert?

Dass in einer ISR die Interrupts per Default deaktiviert sind, ist genau 
das Problem hier. Die Fleury-Lib versendet die Zeichen aus dem Puffer 
per Interrupt. Wird uart_putc aufgerufen während der Puffer voll ist, 
wartet die Funktion so lange, bis wieder Platz im Puffer ist. Da aber 
die Interrupts gerade deaktiviert sind, wird nichts gesendet, also wird 
auch kein Platz im Puffer frei, also wird bis in alle Ewigkeit gewartet.

von ben (Gast)


Lesenswert?

Noch eine Frage hierzu:

ich möchte nun also erreichen, dass mein Interrupt vom USART_UDRE_vect 
unterbrochen werden kannauf keinen Fall aber von anderern Interrupts.

Das einzige, was mir dazu einfällt ist es
(1) die Register der in Frage kommenden Interrupts zu sichern (also z.B. 
TIMSK1, UCSR0B, usw...)
(2) in diesen nun alle Interrupts bis auf den DataRegisterEmpty 
Interrupt zu deaktivieren,
(3) sei(),
(4) meine Zeichen zu senden,
(5) cli()
(6) und dann alle Register rücksichern.

Hat jemand ne Idee, wie das einfacher gehen könnte?

von Karl H. (kbuchegg)


Lesenswert?

Wenn irgendwie möglich, solltest du zu Plan B überwechseln:
In der ISR gar nicht ausgeben. Zumindest nicht derartige Mengen.

Setzt dir in der ISR ein Jobflag und atbeite das im Hauptprogramm ab und 
mach von dort die Ausgaben.

In einer ISR möchtest du normalerweise sowieso nicht ausgeben, da dir 
dieser Interrupt jederzeit überall reinknallen kann, zb auch dann, wenn 
im Hauptprogramm gerade eine Stringausgabe läuft.
Das was du in der ISR ausgibst, taucht dann mitten in der Stringausgabe 
aus. Sind in der Stringausgabe (die den Buffer befüllt) Interrupts nicht 
sowieso gesperrt, dann kann es sogar sein, dass du in mächtige Probleme 
reinläufst, weil die Funktion höchstwahrscheinlich nicht reentrant 
geschrieben ist.

von ben (Gast)


Lesenswert?

danke, werde ich mir zu Herzen nehmen, sende die Zeichen in der 
Interruptroutine, da ich sie sehr timinggenau ausgeben möchte, aber 
werde mal testen, ob es auch noch genau genug ist, wenn ich sie in der 
main schreibe...

von Peter (Gast)


Lesenswert?

>ich möchte nun also erreichen, dass mein Interrupt vom USART_UDRE_vect
>unterbrochen werden kannauf keinen Fall aber von anderern Interrupts.
>
>Das einzige, was mir dazu einfällt ist es
>(1) die Register der in Frage kommenden Interrupts zu sichern (also z.B.
>TIMSK1, UCSR0B, usw...)
>(2) in diesen nun alle Interrupts bis auf den DataRegisterEmpty
>Interrupt zu deaktivieren,
>(3) sei(),
>(4) meine Zeichen zu senden,
>(5) cli()
>(6) und dann alle Register rücksichern.
>
>Hat jemand ne Idee, wie das einfacher gehen könnte?

>Hat jemand ne Idee, wie das einfacher gehen könnte?

*Das hier reicht völlig:*

(1) sei(),
(2) meine Zeichen zu senden,
(3) cli()

An den anderen Registern und Bits must Du nix fummeln, darum kümmert 
sich der Compiler und die CPU drum.

Aber ich muss Karl rechtgeben, in einer ISR solltest Du keine Ausgaben 
machen. Falls es doch nötig ist, dann sowenig wie möglich (z.B. 1..3 
Zeichen)

von ben (Gast)


Lesenswert?

>An den anderen Registern und Bits must Du nix fummeln, darum kümmert
>sich der Compiler und die CPU drum.


Ach und woher weiß der compiler, dass ich gerne vom DataRegisterEmpty 
Interrupt unterbrochen werden will, aber sonst von keinem anderen 
Interrupt?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

ben schrieb:
>>An den anderen Registern und Bits must Du nix fummeln, darum kümmert
>>sich der Compiler und die CPU drum.
>
>
> Ach und woher weiß der compiler, dass ich gerne vom DataRegisterEmpty
> Interrupt unterbrochen werden will, aber sonst von keinem anderen
> Interrupt?

Der Compler weiß es nicht und der µC auch nicht.

Die von Peter vorgeschlagene Methode birgt die Gefahr, daß dir alles um 
die Ohren fliegt, zB wenn die irgendwas-ISR von sich selbst unterbrochen 
wird weil die Ausgabe zu lange dauert. Dort wird sie dann wieder von 
sich selbst unterbrochen etc, bis der Stack ausgeht und der µC (bzw. das 
Programm) dann ziemlichen Unsinn treibt und schliesslich nicht 
unwahrscheinlich im Reset landet.

Timing-ganu kannst du hier schlecht sein, weil die Ausgabe durch die 
UART-ISRs erledigt wird, und deren Aufrufverhalten ist abhängig von der 
Baudrate und zudem von der IRQ-Latenz. Letzteres hast du ja schon 
schmerzlich festgestellt...

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.