Forum: Compiler & IDEs Probleme mit AVR als SPI-Slave und kurzen Wartezeiten


von Michi (Gast)


Lesenswert?

Ich habe bisher schon verschiedene AVRs aus der ATMega-Reihe eingesetzt
und für ein neues Projekt möchte ich den ATMega2560 einsetzen, da ich 3
UARTs, 1 x TWI und 1 x SPI als Slave benötige. Durch die
Außenbeschaltung bedingt kann ich ihn nur mit 3,3V versorgen und somit
stehen mir max. nur ca. 8 MHz zur Verfügung.
Auf der SPI-Schnittstelle ist der AVR Slave und die wird mit 100kHz
betrieben, die Wartezeit zwischen zwei Bytes beträgt vom Master aus im
Moment 30us.

D.h. nach 80us ist ein komplettes Byte angekommen, das einen
SPI-Interrupt im AVR auslöst, um das Zeichen dort zu sichern und das
Antwortzeichen für den Master auszugeben.
Der Master wartet 30us bis er den Clock wieder aktiviert und das
nächste Byte schickt. Innerhalb diesen 30us muss der AVR in den
SPI-Interrupt springen, das Zeichen ablegen und das Antwortzeichen in
den Sendepuffer eintragen.

Das funktioniert mit dem ersten Prototypen auch recht gut, sofern die
anderen (höherprioren) Interrupts deaktiviert sind!
Da der AVR für andere interne Verarbeitungen aber auch noch einige
Timerinterrupts aktiv geschaltet benötigt, werden zuerst diese
abgearbeitet, sofern eines der Timerinterruptflags aktiv gesetzt ist.
Die Timerinterrupts sind alle recht kurz, aber da sie völlig asynchron
und unabhängig voneinander sind, kann es durchaus vorkommen, dass er
einen Timerinterrupt abarbeitet und während der Abarbeitung das
INT-Flag eines anderen Timerinterrupts gesetzt wird und er erst noch
diesen abarbeitet, bis er in den SPI-Interrupt kommt. Falls der AVR es
nun nicht schafft das Antwortzeichen in den SPI-Sendepuffer zu
schreiben bevor der Master nach den 30us den Clock für das weitere Byte
wieder aktiviert, erhält der Master auf der Leitung ein fehlerhaftes
Zeichen und ich eine Kollission im SPI-Interrupt.

Die Interruptprioritäten sind im AVR ja fest vorgegeben, daher kann ich
die Priorität für SPI-Interrupts auch nicht anheben.

Nun die Frage, ist es möglich in den höherprioren Interrupts das
Interruptflag für die SPI-Schnittstelle abzufragen und falls es gesetzt
ist die Interruptverarbeitung des SPI-Schnittstelleninterrupts vom
Timerinterrupts aus aufzurufen? Falls ja, wie genau?

Wer hat hiermit schon Erfahrungen gesammelt oder fällt Euch eine andere
Lösungsmöglichkeit hierfür ein?

Freue mich auf Eure Rückmeldungen :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Vielleicht reicht es einfach, in den Timer-ISRs gleich
am Anfang die Interrupts wieder freizugeben?

von peter dannegger (Gast)


Lesenswert?

Ich würde ein Handshake nehmen:

Der Master setzt ja /SS auf low, um den Slave zu adressieren und nach
dem Byte wieder high. Statt high würde ich nun auf Eingang schalten und
nen Pullup draußen dran.

Hat der Slave nun das neue Byte in den Puffer geschrieben, macht er auf
der /SS-Leitung einen H-L-H Impuls.

Der Master nimmt für die /SS-Steuerung einen externen Interrupt und
kriegt diesen Impuls als externen Interrupt zurück.

Nun weiß der Master, daß er weiter machen kann.


Peter

von Michi (Gast)


Lesenswert?

Hallo Jörg:
>> Vielleicht reicht es einfach, in den Timer-ISRs gleich
>> am Anfang die Interrupts wieder freizugeben?
Das Problem ist ja, dass die Timerinterrupts eine höhere Priorität
besitzen, wenn ich nun im Timerinterrupt die Interrupts wieder
freischalte kann er ja nur durch einen höherprioren verdrängt werden,
der SPI-Interrupt besitzt jedoch eine geringere Priorität.

Hallo Peter:
Auf dieser SPI-Schnittstelle sitzt nicht nur der AVR sondern auch noch
einige andere SPI-Slaves wie Dataflash, EEPROM, AD-Wandler... Von daher
muss sich der AVR auf dieser SPI-Schnittstelle konform verhalten. Sobald
die /SS-Leitung auf low geht, wird das SPI-Interface im AVR aktiv und
erhält je nach Telegramm bis zu ca. 32 Bytes an Nutzdaten, bis /SS
wieder auf high geht.
Technisch ist ein Handshakeverfahren mit einer weiteren Leitung außer
den 4en für SPI (MOSI, MISO, SCLK und CS) nicht möglich, da der Master
keinen Pin mehr für solche Zwecke zur Verfügung hat und die /SS immer
aktiv treibt.

Trotzdem Danke für Eure Vorschläge bisher und ich bin mal gespannt, was
Euch oder den anderen noch so einfällt um dieses Problem zu lösen :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Das Problem ist ja, dass die Timerinterrupts eine höhere Priorität
> besitzen, wenn ich nun im Timerinterrupt die Interrupts wieder
> freischalte kann er ja nur durch einen höherprioren verdrängt
> werden, ...

Nein, das ist kein 8051.  Interruptprioritäten haben nur Bedeutung bei
der Annahme des Interrupts, wenn mehrere Requests gleichzeitig
anliegen.  Wenn du innerhalb einer ISR die Interrupts wieder
freigibst, kann sie durch jeden weiteren Interrupt unterbrochen
werden (einschließlich des eigenen, falls die Bedingung noch anhängig
ist -- daher ist es keine gute Idee, z. B. in einer UART-ISR die
Interrupts gleich am Anfang erneut freizuschalten...).

von peter dannegger (Gast)


Lesenswert?

Es wird Dir zwar nichts nützen, aber die neuen AT89LP4052 haben ein
gepuffertes SPI, d.h. der Slave kann gleich 2 Byte in den Sendepuffer
schreiben und kriegt nach dem 1.Byte den Interrupt, um ein nächstes
Byte reinzupacken.
Er hat also ein volles Byte Zeit um den Interrupt zu bedienen.
Und er hat auch 4 echte Interruptprioritäten, d.h. dem SPI könnte man
die höchste Priorität zuweisen.
Und mit 20MIPS ist er auch nicht langsam.
Ist aber eben ne andere Architecktur.


Peter

von Michi (Gast)


Lesenswert?

Hallo Jörg,

würdest Du dann empfehlen in den entsprechenden höherprioreren ISRs die
Interrupts wieder freizugeben, damit der SPI-Interrupt schneller "dran
kommt"?

Wäre es auch möglich oder geschickter das SPI-Interruptflag in den
anderen Interrupts abzufragen und den SPI-Interrupt aufrufen, falls es
gesetzt ist? Fall ja, wie?

Michi

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> würdest Du dann empfehlen in den entsprechenden höherprioreren ISRs
> die Interrupts wieder freizugeben, damit der SPI-Interrupt schneller
> "dran kommt"?

Ja, wobei du natürlich das Timing ordentlich nachvollziehen musst
(worst case Betrachtung).

> Wäre es auch möglich oder geschickter das SPI-Interruptflag in den
> anderen Interrupts abzufragen und den SPI-Interrupt aufrufen, falls
> es gesetzt ist?

Ich denke nicht.  Der Aufruf einer anderen Funktion kostet so viel
mehr Zeit (da der Compiler dann alle per ABI als `caller-saved'
markierten Register rettet), dass das nicht lohnt.  Man müsste das
dann sorgfältig mit der Hand feilen.  Sofern die geschachtelten
Interrupts funktionieren, würde ich diese bevorzugen.

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.