Hi,
ich hab eine Frage zu SPI. Das Verhalten find ich ehrlich gesagt etwas
verwunderlich und wüsste nicht, wieso es auftritt.
Mein Slave sendet dem Master ein Array, nachdem der Master es
angefordert hat. Danach sendet mein Master dem Slave, dass alles ok ist
(1 Byte). Der Slave erhält es, jedoch bleibt er irgendwo in einer while
schleife (schätze ich) hängen und es passieren komische Sachen auf dem
Display, wenn der Master wieder ein Empfangen anfordert.
Wenn der Slave aber 2 mal etwas empfängt, läuft es, falls das 2
empfangene Byte 0 ist.
Hat jemand da Erfahrung mit sowas schonmal gemacht?
Es ist so, dass mein Master dummie Bytes sendet und dann der Slave die
Bytes sendet. Keine Ahnung ob es daran liegen könnte.
Hab hier auch den Code, falls es hilft.
Ich hab den CRC16 Korrektheitstest erstmal ausgelassen.
Wenn jedoch 2 mal CRC16_is_CORRECT gesendet und empfangen wird, klappts
nicht.
Wenn aber CRC16_IS_CORRECT und danach eine 0 gesendet wird,
funktionierts.
CRC16_IS_CORRECT ist 0xFF
und CRC16_IS_NOT_CORRECT ist 0x00
Master:
1 |
| 2 | SPI_M_send_byte_to_hardware_slave(SEND_ARRAY_WITH_DEFINED_LENGTH_AND_CRC16);
| 3 |
| 4 | SPI_M_send_byte_to_hardware_slave(SPI_CHECK_BYTE);
| 5 | SPI_M_send_byte_to_hardware_slave(SPI_DUMMY_BYTE_FOR_RECEIVING);
| 6 |
| 7 | array_size = SPI_M_receive_byte();
| 8 |
| 9 | for(i = 0; i < array_size; i++)
| 10 | {
| 11 |
| 12 | SPI_M_send_byte_to_hardware_slave(SPI_DUMMY_BYTE_FOR_RECEIVING); // for receiving
| 13 | *tmp = SPI_M_receive_byte();
| 14 | *tmp++;
| 15 |
| 16 | }
| 17 |
| 18 | *tmp = ARRAY_END;
| 19 | tmp = array;
| 20 |
| 21 |
| 22 | crc16_check = crcSlow(tmp, array_size);
| 23 |
| 24 | SPI_M_send_byte_to_hardware_slave(SPI_DUMMY_BYTE_FOR_RECEIVING); // for receiving
| 25 | crc16 = SPI_M_receive_byte();
| 26 | crc16 <<= 8;
| 27 | SPI_M_send_byte_to_hardware_slave(SPI_DUMMY_BYTE_FOR_RECEIVING);
| 28 | crc16 |= SPI_M_receive_byte();
| 29 |
| 30 | SPI_M_send_byte_to_hardware_slave(CRC16_IS_CORRECT);
| 31 |
| 32 | SPI_M_send_byte_to_hardware_slave(CRC16_IS_NOT_CORRECT);
| 33 |
| 34 |
| 35 | return array_size;
| 36 |
| 37 | }
|
Slave:
1 | if (SPI_S_receive_byte() >= SPI_CHECK_BYTE_FOR_RECEIVING)
| 2 | {
| 3 |
| 4 | SPI_S_send_byte(array_size);
| 5 |
| 6 | for(i = 0; i < array_size; i++)
| 7 | {
| 8 |
| 9 | SPI_S_send_pointer(tmp);
| 10 | tmp++;
| 11 |
| 12 | }
| 13 |
| 14 | *tmp = ARRAY_END;
| 15 | tmp = array;
| 16 |
| 17 |
| 18 |
| 19 | crc16 = crcSlow(tmp, array_size);
| 20 | crc16_low_byte = crc16 & 0b11111111;
| 21 | crc16_high_byte = crc16 >> 8;
| 22 |
| 23 | SPI_S_send_byte(crc16_high_byte);
| 24 |
| 25 | SPI_S_send_byte(crc16_low_byte);
| 26 |
| 27 |
| 28 | crc16_ok = SPI_S_receive_byte();
| 29 |
| 30 | crc16_ok = SPI_S_receive_byte();
|
Vielen Dank
Also du kannst dir das Leben etwas leichter machen, wenn du direkt auf
das Array zugreifst und nicht so viel mit Pointern rummachst.
statt 1 | *tmp = SPI_M_receive_byte();
|
könntest du z.B. einfach 1 | array[i] = SPI_M_receive_byte();
|
schreiben (Master).
Im Master ist dementsprechend auch ein Fehler:
soll sicher
heissen.
Das begründet aber nicht das Problem.
Ich denke du solltest mal mit einem Logicanalyzer oder mit einem DSO den
SPI Bus angucken.
Sieht mir so aus, als bei deiner Kommunikation IN und OUT nicht im sync
sind.
Eugen Thorben schrieb:
> Slave:
>
> [c]
> if (SPI_S_receive_byte() >= SPI_CHECK_BYTE_FOR_RECEIVING)
> {
>
> SPI_S_send_byte(array_size);
Das ist sehr missverständlich ausgedrückt und ich denke irgendwo in
diesem Umfeld wird auch dein Problem sitzen.
Der Slave kann von sich aus nichts senden!
Die Initiative liegt immer beim Master. Überhaupt sind die Begriffe
Senden und Empfangen bei SPI etwas deplaziert. Denn eigentlich ist SPI
eher so etwas wie ein gleichzeitig Byteaustausch. Während vom Maser 1
Byte zum Slave wandert, wandert gleichzeitig 1 Byte vom Slave zum
Master.
D.h. Wenn der Master die Übertragung eines Bytes initiiert, dann muss im
Slave das gleichzeitig rückzuübertragende Byte schon im SPDR Register
sein.
D.h. aber auch: der Master muss dem Slave die Zeit einräumen, die der
Slave braucht um zb angeforderte Daten bereitzustellen.
SPI funktioniert so, wie wenn du und dein Kumpel sich am Tisch gegenüber
sitzen. Du bist der Master, dein Kumpel ist der Slave. Ihr habt beide
einen Zettel vor euch liegen auf dem ihr was aufschreiben könnt.
Auf DEIN Zeichen (du bist der Master) hin, nimmt jeder seinen Zettel und
reicht ihn mit der rechten Hand zum Gegenüber rüber, der ihn mit der
linken Hand entgegen nimmt.
Wenn du daher etwas von deinem Kumpel willst, dann geht das so
* du schreibst auf deinen Zettel: Wie alt bist du?
* dann tauscht ihr beide die Zettel aus. Logischerweise kann auf dem
Zettel, den du gerade bekommen hast noch nichts vernünftiges stehen.
Denn dein Kumpel weiss ja erst was du von ihm willst, wenn er deinen
Zettel bekommen hat.
* Jetzt musst du deinem Kumpel auch ein wenig Zeit einräumen, damit er
sein Alter auf den Zettel schreiben kann. Denn das dauert ja auch ein
wenig
* Dann tauscht ihr wieder die Zettel aus. Was auf deinem Zettel steht,
spielt keine Rolle. Aber du musst den Zettel rausrücken, weil ihr ja
immer die Zettel austauscht.
* Mit dem Zettel, den du jetzt kriegst hast du dann die gewünschte
Information.
* Du radierst das aus und schreibst "OK" drauf.
* wieder werden die Zettel ausgetauscht
* auf dem Zettel den du kriegst steht nichts vernünftiges. Vielleicht
steht da "keine weitere Information verfügbar", aber im Grunde brauchts
das nicht, denn auf die Frage nach dem Alter genügt es, wenn du die Zahl
zurück kriegst.
* dein Kumpel hingegen kriegt den Zettel, liest ihn, liest das OK und
weiss damit, dass er die nächste Zeit von dir Ruhe haben wird.
So läuft SPI ab.
Der eigentliche Transfer ist immer ein Byte Austausch. Daher sind die
Begriffe Senden und Empfangen hier etwas deplaziert. Denn bei jedem
Transfer gibt es einen Sender und einen Empfänger. Und zwar in beiden
Richtungen gleichzeitig!
Und ganz wichtig: Der Slave kann sich nicht wehren! Wenn der Master den
Zettelaustausch anordnet, dann werden die Zettel getauscht. Egal ob dein
Kumpel sein Alter schon auf den Zettel geschrieben hat oder nicht. D.h.
nach der Anforderung einer Information, wirst du gut daran tun, deinem
Kumpel auch etwas Zeit einzuräumen. Vielleicht muss er ja erst nach der
Information Googeln - das kann schon dauern. Entscheidend ist, dass du
keine Information darüber hast, ob er schon fertig ist oder nicht.
Daher implementiert man meistens den Slave auch nicht so, wie du das
machst. Im Slave implementiert man einen Interrupt, der immer dann
aufgerufen wird, wenn ein Byteaustausch komplett fertig ist. In dieser
ISR musst du dir dann zb einen Zustand merken um zu wissen, was beim
nächsten ISR Aufruf zu tun ist. Die ISR stellt zb fest, dass jetzt
gerade das Kommando 'Array liefern' eingetrudelt ist. VOn diesem
Kommando weiss es, dass der Master als nächstes die Übertragung der
Array Größe erwartet. Also stellt sie die gleich mal ins SPDR Register,
damit sie sich der Master abholen kann. Holt sich der Master die
Arraygröße, dann weiss die ISR anhand der gemerkten Zustände, dass als
nächstes 1 Byte vom Array zu übertragen ist. Also wird dieses ins SPDR
geschrieben, damit der Master sich das abholen kann. Beim nächsten ISR
AUfruf kommt dann das nächste Array Element drann. Und so geht das
dahin, bis das Array komplett übertragen wurde, woraufhin die ISR die
CRC bereitstellt.
Der Slave 'sendet' also in diesem Sinne nichts. Der Slave stellt die
jeweils nächste Information im SPDR bereit, auf dass der Master sich die
abholen kann. Das muss deine Denkweise sein und dann gibts auch keine
Schleifen, in denen dein Programm hängen könnte.
Joe F. schrieb:
> Im Master ist dementsprechend auch ein Fehler:*tmp++;soll
> sichertmp++;heissen.
Hab den Fehler nicht gesehen, danke habs geändert und CRC16 mit do while
funktioniert.
Danke, das Forum ist top :D
Danke auch für den Text Karl Heinz.
Na denn.
Man kann es aus deinen Codeausschnitten nicht sehen, aber stelle auch
sicher, dass du
am Anfang der Funktionen stehen hast. Sonst schreibst du irgend wo
hin...
ja, hab ich, hab nicht alles gepostet.
Joe F. schrieb:
> Na denn.
> Man kann es aus deinen Codeausschnitten nicht sehen, aber stelle auch
> sicher, dass du
>
> am Anfang der Funktionen stehen hast. Sonst schreibst du irgend wo
> hin...
Wie schon gesagt:
schmeiss den Pointer komplett raus. Den braucht keiner. Damit schiesst
du dir nur ins Knie.
Und immer schön darauf achten, dass du dem Slave auch etwas Zeit
einräumen musst.
Genau aus dem Grund :-) hast du wahrscheinlich hier im Master 1 | SPI_M_send_byte_to_hardware_slave(SEND_ARRAY_WITH_DEFINED_LENGTH_AND_CRC16);
| 2 |
| 3 | SPI_M_send_byte_to_hardware_slave(SPI_CHECK_BYTE);
| 4 | SPI_M_send_byte_to_hardware_slave(SPI_DUMMY_BYTE_FOR_RECEIVING);
| 5 |
| 6 | array_size = SPI_M_receive_byte();
|
ein zusärtliches SPI_CHECK_BYTE übertragen. EInfach nur damit Zeit
vergeht.
Noch ein Tipp: Mach dir das Leben einfacher und nenn deine Funktionen
nicht so lang. Man kann alles übertreiben und dann dreht sich die gute
Absicht ins Gegenteil um.
Auch sollte aus den Ausführungen schon klar sein, dass im Master es
keine getrennten Funktionen für Send und Receive braucht. Eine simple
Funktion 'Transfer' reicht völlig aus. Sie kriegt das Byte, dass sie zum
Slave übertragen soll und liefert das Byte, welches im Gegenzug vom
Slave reingekommen ist.
1 | uint8_t SPI_M_Transfer( uint8_t byte )
| 2 | {
| 3 | SPDR = byte;
| 4 | while(!(SPSR & (1<<SPIF)))
| 5 | ;
| 6 | return SPDR;
| 7 | }
|
1 | SPI_M_Transfer( SEND_ARRAY_WITH_DEFINED_LENGTH_AND_CRC16 );
| 2 | _delay_ms( 1 ); // gib dem Slave ein wenig Zeit, damit er auswerten kann
| 3 | // was der Master eigentlich will und die Array Size bereit
| 4 | // stellen kann
| 5 |
| 6 | // Jetzt is aber gut. Schön langsam sollte der Slave fertig sein.
| 7 | // Rück mal die Array Size raus!
| 8 | array_size = SPI_M_Transfer( SPI_DUMMY_BYTE_FOR_RECEIVING );
| 9 |
| 10 | _delay_ms( 1 ); // gib dem Slave ein wenig Zeit um das erste Array Element bereit zu stellen
| 11 |
| 12 | for( i = 0; i < array_size; ++i ) {
| 13 | array[i] = SPI_M_Transfer( SPI_DUMMY_BYTE_FOR_RECEIVING );
| 14 | _delay_ms( 1 ); // gib dem Slave ein wenig Zeit um das nächste ELement bereit zu stellen (falls noch was da ist)
| 15 | }
| 16 |
| 17 | ....
|
(die 1 ms werden jetzt etwas übertrieben lang sein!)
Wohingegen du den Slave am vernünftigesten mittels Interrupt und
Statemachine implementierst 1 | // Die Zustände, in denen die ISR sein kann
| 2 | #define IDLE 0
| 3 | #define SEND_ARRAY_DATA 1
| 4 | #define SEND_ARRAY_CRC_BYTE1 2
| 5 | #define SEND_ARRAY_CRC_BYTE2 3
| 6 |
| 7 | volatile uint8_t state = IDLE;
| 8 |
| 9 | // Variablen, mit denen sich die Statemachine Informationen von
| 10 | // einem ISR Aufruf zum nächsten hinterlassen kann
| 11 | uint8_t nextElemToSend;
| 12 | uint8_t crc16_high_byte;
| 13 | uint8_t crc16_low_byte;
| 14 |
| 15 | ISR( SPI_STC_vect )
| 16 | {
| 17 | uint8_t byte = SPDR;
| 18 |
| 19 | if( state == IDLE ) { // Idle Zustand. Das nächste byte vom Master ist ein Kommando
| 20 | if( byte == SEND_ARRAY_WITH_DEFINED_LENGTH_AND_CRC16 ) {
| 21 | SPDR = array_size;
| 22 | nextElemToSend = 0;
| 23 | state = SEND_ARRAY_DATA;
| 24 | }
| 25 | }
| 26 |
| 27 | else if( state == SEND_ARRAY_DATA ) {
| 28 | SPDR = array[nextElemToSend++];
| 29 | if( nextElemToSend == array_Size ) {
| 30 | state = SEND_ARRAY_CRC_BYTE1;
| 31 | ... CRC Berechnen // gleich mal die CRC Berechnen, kann hier
| 32 | // passieren, während wir darauf warten, dass
| 33 | // der Master sich das letzte Array Element holt
| 34 | }
| 35 |
| 36 | else if( state == SEND_ARRAY_CRC_BYTE1 ) {
| 37 | SPDR = crc16_high_byte;
| 38 | state = SEND_ARRAY_CRC_BYTE2;
| 39 | }
| 40 |
| 41 | else if( state == SEND_ARRAY_CRC_BYTE2 ) {
| 42 | SPDR = crc16_low_byte;
| 43 | state = IDLE;
| 44 | }
| 45 | }
|
In der Hauptschleife vom Slave überwachst du dann auch noch die Chip
Select Leitung des Slave. Wenn der Master mitten in einer Übertragung
den Chip Select wieder frei gibt (den Slave also inaktiv schaltet), dann
setzt du den state wieder zurück auf IDLE. So bleibt dann der Slave
nirgends hängen. Zieht der Master den Chip Select wieder auf aktiv, dann
ist das erste Byte, das er überträgt das Kommando, mit dem er mitteilt
was er will. Und genau so reagiert dann auch der Slave: Da er im IDLE
ist, wird das erste Byte vom Master als Kommando aufgefasst und die
ganze Statemachine kommt wieder ins laufen. Nirgends wird gewartet, es
gibt keine Schleifen, in denen sich der Slave verhängen könnte und aus
denen ihn der Master nicht mehr rauskriegt. Selbst dann, wenn der Master
mitten in einer Übertragung abschmiert, läuft der Slave weiter. Mit dem
nächsten Aktivieren des Slave (mittels Chip Select Leitung) kann der
Master wieder ein Kommando einspeisen.
Mensch, das ist ja nett. Ich denke, dass mit dem delay ist ganz gut und
auch das mit dem CS, damit es nicht in einer while-schleife hängen
bleibt
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|