Forum: Compiler & IDEs SPI, Wie SPDR Empty erkennen


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich hab ein kleines Problem bei der Programmierung des SPI. Die 
Forumssuchfunktion half mir leider auch nicht weiter.
Ich habe einen ATmega8515 und möchte damit Daten an einen ATmega16 per 
SPI senden. Das funktioniert im Prinzip auch zu 90%. Bei 10% kommt 
Datenmüll im Mega8 an.

Mein Problem ist, das ich nicht weis wann ich wieder Daten in das 
Senderegister des Mega8515 schreiben darf. Laut Datenblatt wird das Flag 
SPIF im SPSR sofort gelöscht, sobald der Interrupt auslöst oder ich 
einmal das SPSR lese.
Da ich mit Interrupts arbeite, kriege ich ein Problem. Ich kann ja nicht 
sagen
if (SPSR & _BV(SPIF)) SPDR = x; // Pseudocode :-)

Das SPIF wird ja schon bei meinem letzten Interrupt gelöscht. Bin ich 
nun im Transfer oder Nicht ?!

Ein Bit wie beim UART "TrasmitRegister Empty" habe ich nicht gefunden.

Wie löst Ihr so ein Problem ?!

Danke
Juergen

von Thomas Müller (Gast)


Lesenswert?

Die SPI-Schnittstelle ist doch eine synchrone Schnittstelle, d.h. bei 
Auslösen des Interruptes ist die Datenübertragung komplett. Also hat an 
dieser Stelle der Slave die Daten vom Master übernommen.

Durch das Ring-Schieberegister-Prinzip könntest du dir doch vom Slave 
ein Bestätigungs-Byte (0xAA oder so) schicken lassen. So weißt du 
eventuell, wieso bei 10% Mist übertragen/empfangen wird. Wenn du einen 
SPI-Interrupt bekommst, liest du zuerst das SPI-Datenregister (MISO-Byte 
vom ATmega16) und schreibst dann die MOSI-Daten (zum ATmega16) in das 
SPI-Datenregister.

Mfg

von unbeschreiblicher Rahul (Gast)


Lesenswert?

>Da ich mit Interrupts arbeite, kriege ich ein Problem. Ich kann ja nicht
>sagen if (SPSR & _BV(SPIF)) SPDR = x; // Pseudocode :-)

Du vermischt da irgendwie zwei Sachen:
Der Interrupt wird ausgeführt, wenn das SPIF gesetzt wird und das 
entsprechende Enable-Bit gesetzt ist.
Dann springt der Controller in die SPI-Interruptservice routine.
Wenn man in der ISR dann noch mal das SPIF abfragt, ist das die gleiche 
Abfrage, die der Controller durch den Sprung in die ISR schon erledigt 
hat.
Die Daten kann man also einfach in der ISR ins SPDR schreiben.

Oder wie machst du das?
Etwas Code würde helfen...

von Jürgen S. (jsachs)


Angehängte Dateien:

Lesenswert?

Anbei ist mal der code für die SPI. Das ganze Projekt wäre zu 
umfangreich.

Beim Start rufe ich "spiInitMaster()" auf.

Danach immer wenn ich was senden will
------
spibuffer.add("Dies ist ein test");
spiStartSend();
------

In spiStartSend() komme ich dann an mein Problem. Darf ich nun schon das 
erste Byte in das SPDR schreiben ? Läuft mein Interrupt ?

Das File ist nur mit Mega CPUs getestet.

Also ich schreibe Daten in den Sendepuffer.
starte das Senden und den Interrupt

per Interrupt sende ich die weiterne Bytes bis der Puffer leer ist und 
Stoppe den Interrupt.

Ich vermute das ich hier Timingprobleme bekomme, das ich SPDR 
überschreibe während einer Übertragung :-(

Im Slave mache ich alles per Polling, da er nix anderes macht wie SPI 
Daten zu Sammeln und per UARt an den PC zu senden.
if ((SPSR & _BV(SPIF)))  // neue Daten Da !
{
  register char c;
  c = SPDR;
  serbuffer.add(c);    // Add Data to buffer
  PORTD ^= _BV(PD5);
}

Wieso gibt es da auch keine Möglichkeit eine laufende Übertragung 
festzustellen ?

Gruss
Juergen

von Peter D. (peda)


Lesenswert?

> serbuffer.add(c);    // Add Data to buffer

Diese Funktion steht nirgends.
Was macht sie, wie lange dauert sie (worst case) ?
Wie lange dauern sämtliche Interrupts im Programm ?


AVR-SPI-Slave möchte man nicht wirklich benutzen.

Wenn das Empfangsbyte nicht garantiert innerhalb des nächsten Bytes 
abgeholt wird, ist Sense.
Und Senden wird überhaupt nicht gepuffert, ohne Handshake oder lange 
Wartezeiten geht da überhaupt nichts.


Als SPI-Slave würde ich nur den AT89LP4051 nehmen.
Der hat auch nen Sendepuffer und Interuptprioritäten, d.h. er kann 
sämtliche anderen Interrupts unterbrechen, sobald die SPI-Kacke am 
Dampfen ist.


Peter

von Jürgen S. (jsachs)


Lesenswert?

Die Funktion "serbuffer.add()" steht an anderen Stellen im Programm, wo 
ich eben Debugausgaben haben will.
Wird von mir vielfach genutzt. Ist nichts anderes wie ein Puffer Objekt.
Die Routine ist aber getestet. Habe im Slave in Mainline einfach 
"serbuffer.add(x);" laufen lassen...... Alles wurde fehlerfrei zum PC 
übertragen. Natürlich hat die Funktion die meisten Daten verworfen, da 
der Puffer die Daten nicht alle aufnehmen konnte. Aber das war ja nur 
ein Test um zu sehen ob ich hier schon Daten Müll produziere.

Uff, mir gehts nur darum zum Debuggen eine Schnittstelle zu haben.
Mit dem USART der Mega8515 bearbeite ich einen RS485 Bus, das läuft auch 
Prima.
Anfangs hatte ich auch eine Softuart Schnittstelle, Das Problem ist nur, 
das durch die anderen Interrupts, das Timing nicht mehr stimmt und ich 
auch Übertragungsfehler bekomme, das Bittiming ist einfach zu kritisch.
War schon am Überlegen mir einfach einen Externen Hardware UART zu 
besorgen.
Ich dachte so kann ich mir das Hardware kaufen sparen und bekomme etwas 
Übung mit dem SPI.

Aber das Problem müsste doch handhabbar sein.
Wenn beim Empfang etwas verloren geht, würde ja ein komplettes Byte 
fehlen. Bei mir sind die Daten aber Müll. Kein Bitkipper sondern richitg 
Müll bei 10%.

Juergen

von Peter D. (peda)


Lesenswert?

- Master-SCK < Slave-XTAL / 4

- kurze Leitungen < 20cm

- dicke GND-Verbindung

- lange Pausen (>10ms) zwischen dem Senden oder Handshakeleitung, wenn 
der Slave das Byte ausgelesen hat.


Peter

von Jürgen S. (jsachs)


Lesenswert?

Hmm,

so wie ich das sehe wäre es wohl sinnvoller auf I2C umzusteigen....
Da hab ich die Probleme nicht und brauch nur halb so viele Leitungen ;-)

Danke für die Tips soweit.

Gruss
Juergen

von fieser, klugscheissender Rahul (Gast)


Lesenswert?

>Da hab ich die Probleme nicht und brauch nur halb so viele Leitungen ;-)

Sicher? TWI ist auch nicht ganz ohne.
Die einfachste (und bei AVRs vermutlich auch am besten realisierte) 
Schnittstelle ist immer noch das USART...

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


Lesenswert?

Ja, I²C ist besser geeignet, einen Controller als Slave anzusteuern.
SPI ist wirklich weiter nichts als ein Hardwareschieberegister und
kann folglich von einem solchen am besten implementiert werden.

Aber zurück zu deiner eigentlichen Frage: wenn du mit Interrupts
arbeitest, musst du dir den Status einfach in der ISR merken: wenn
der Interrupt getriggert hat, war das SPIF irgendwann mal gesetzt.
Ansonsten gar nicht erst mit Interrupts arbeiten und gleich pollen.
Wenn dein SPI-Slave vorbeugend auf sein slave select pollen kann,
weil er weiß, wann der Master ihm was in Auftrag geben wird, hast
du den ersten Teil erschlagen.  Aber der zweite Hammer kommt gleich
hinterher: typischerweise sendet man bei SPI sowas wie ein Kommando
als erstes, an Hand dessen der Slave dann entscheidet, was zu tun
ist.  Diese Entscheidung ist auch total zeitkritisch, zumindest,
falls du den Master nicht irgendwo per ,,Aktennotiz'' dazu bewegen
kannst, nach dem Kommandobyte eine Pause einzulegen.

von Jürgen S. (jsachs)


Lesenswert?

Naja,

Bei mir ist die Aufgabe einfach für den Slave:
Nimm alle Daten die du per SPI bekommst. kopiere sie in einen 
Zwischenpuffer, und Übertrage Sie bei Gelegenheit zum PC per UART.

Nunja, versuche ich eben mal mit I2C. Hab gestern mit viel Probieren es 
so hinbekommen, das keine Daten mehr verstümmelt wurden. Dafür gehen 
jetzt Zeichen verloren.
Ich hab ein Fach zwischen jedem Byte per SPI die SS Leitung mit 
Verzögerung kurz High und wieder Low gesetzt um den SPI zu 
Syncronisieren.
Naja die Wartezeiten die ich brauche das es Fehlerfrei läuft sind zu 
lange und stören schon das restliche Timing mehr als mir lieb ist.

Aber ich werde mir Morgen mal den I2C per Hardware ansehen. Den in SW zu 
machen ist mir etwas Rechenverschwendung :-)

Gruss und Danke für die Tips
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.