Forum: Mikrocontroller und Digitale Elektronik ATMEGA SPI-Slave Wert zurückgeben


von Andreas K. (hobbybastler_ak)


Lesenswert?

Hallo liebe Gemeinde,

nach dem mein Account wohl nach einigen Jahren gelöscht wurde nun ein 
aktuelles Problem, bei dem ich eure Hilfe benötige.

Zwei ATMEGAs sind per SPI miteinander verbunden und der Master kann auch 
schon einen Befehl absenden, den der Slave (8515) erkennt und 
entsprechend ausführt.

Nun setze ich das SPDR-Register des Slave "vor" dem Senden des 2. Byte 
der Übertragung mit einem Rückgabewert, damit dieses während der 2. 
Übertragung zurück an den Master gesendet wird.

Mittels Logic-Analyzer habe ich leider feststellen müssen, dass wie 
Werte scheinbar mit dem vom Master übertragenen Byte "verODERt" werden 
und erst beim 3. Byte des Masters der Wert gesendet wird, den ich vor 
dem 2. Byte schon gesetzt habe.

Und nun der Code:
1
unsigned char SPI_LesenSchreiben(unsigned char dataout)
2
{
3
  SPDR=dataout;                                
4
  while(!(SPSR & (1<<SPIF)));                        
5
  return SPDR;                              
6
}

1
int main(void)
2
{
3
  SPCR = (1<<SPE);
4
  while(1)
5
  {
6
    data[0] = SPI_LesenSchreiben(0x00);
7
    switch (data[0])
8
    {
9
      case 10:    // Mein erster Befehl = 0x0A
10
        data[1]=SPI_LesenSchreiben(0x0A);
11
        TuEtwasMitDem2.Byte(data[1]);
12
      break;
13
      case 11:    // 2.Befehl ohne 2.Byte
14
        MacheEtwas();
15
      break;
16
      case 12:
17
        data[1]=SPI_LesenSchreiben(0x0C);
18
        data[1]=SPI_LesenSchreiben(StatusByte1);
19
        data[1]=SPI_LesenSchreiben(StatusByte2);
20
        data[1]=SPI_LesenSchreiben(StatusByte3);
21
        data[1]=SPI_LesenSchreiben(StatusByte4);
22
      break;
23
    }
24
  }
25
}
1
Gesendet    Empfangen
2
0x0A        0x05
3
0x01        0x0E
4
0x0B        0x0A
5
0x0C        0x01
6
0x00        0x0F
7
0x00        0x0C
8
0x00        0x00
9
0x00        0x00
10
0x00        0x00

Ich bin langsam echt ratlos. Und nein, es ist nicht mein erstes Projekt 
und ja, ich habe das sch... Manual schon fünf mal nach Anhaltspunkten 
durchsucht.

ATMEGA8515,
F_CPU ist 16MHz,
SPI-Clock ist Fosc/16


Bin über jede Idee dankbar.

Gruß
der Hobbybastler

von S. Landolt (Gast)


Lesenswert?

Mir ist unklar, weshalb überhaupt etwas beim Master ankommt: im Slave 
fehlt das Setzen von MISO als Ausgang.

von c-hater (Gast)


Lesenswert?

Andreas K. schrieb:

> Zwei ATMEGAs sind per SPI miteinander verbunden und der Master kann auch
> schon einen Befehl absenden, den der Slave (8515) erkennt und
> entsprechend ausführt.

Fein soweit.

> Nun setze ich das SPDR-Register des Slave "vor" dem Senden des 2. Byte
> der Übertragung mit einem Rückgabewert, damit dieses während der 2.
> Übertragung zurück an den Master gesendet wird.

Das kannst du mit einiger Wahrscheinlichkeit nicht tun. Der Slave kann 
nämlich garnix aktiv senden. Er kann nur (hoffentlich rechtzeitig) eine 
Antwort bereitstellen, BEVOR der Master das Antwortbyte abruft.

Wenn der Slave aber erst nach Eintrudeln des ersten Bytes vom Master 
irgendwelche, wer weiß wie verfrickelten Sachen ausführt, dann hat er 
mit einiger Wahrscheinlichkeit das Ergebnis noch nicht parat, wenn der 
Master das nächste Byte abruft. Es sei denn, der Master kennt seine 
Slaves und WEISS das er erstmal eine Pause machen muss, nachdem er das 
Kommando abgesondert hat.

> Mittels Logic-Analyzer habe ich leider feststellen müssen, dass wie
> Werte scheinbar mit dem vom Master übertragenen Byte "verODERt" werden

Da wird nix verodert. Der Slave war einfach noch nicht soweit. Der 
Master empfängt in diesem Fall schlicht das Byte, was er zuletzt 
gesendet hat.

Du hast offensichtlich nicht begriffen, wie SPI-Kommunikation 
funktioniert. Das ist immer gleichzeitiger "Austausch" von Information. 
Und da die Kausalität unseres Universums dazu zwingt, dass die Wirkung 
nicht vor der Ursache passieren kann, wird das Zeitfenster sehr eng...

Sprich: dein Slave müsste innerhalb von weniger als 1.5 SPI-Bitzeiten 
(wenn der Master ein ATMega ist, bei richtigen SPI-Mastern innerhalb von 
weniger als 0.5 SPI-Bitzeiten) das Ergebnis bereitstellen. Du hörst dich 
nicht so an, als wärest du in der Lage, Code zu produzieren, der das 
könnte...

Also musst du entweder den Master nach dem Kommando eine Pause machen 
lassen, oder ihm beibringen, dass nicht nur das erste, sondern auch das 
zweite empfangene Byte Bullshit ist.

So einfach ist das. Wenn man weiß, was man da eigentlich tut...

von S. Landolt (Gast)


Lesenswert?

So viel Text - ich staune immer wieder über die Mühe, die Sie sich 
manchmal machen, c-hater.
  Aber ich wollte Ihnen noch erklären, weshalb ich die vielen Klammern 
schreibe, ist mir erst später eingefallen, wie das vor zwei Jahrzehnten 
so kam: wie erwähnt schreibe ich im Zehn-Finger-System, und da stört 
dieses '|' den Fluss ganz enorm; stattdessen verwende ich '+' und nehme 
die dann nötigen Klammern in Kauf, die kommen quasi 
fließend-automatisch.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Andreas K. schrieb:
> Nun setze ich das SPDR-Register des Slave "vor" dem Senden des 2. Byte
> der Übertragung mit einem Rückgabewert, damit dieses während der 2.
> Übertragung zurück an den Master gesendet wird.

 Was heisst "vor" dem Senden?
 Woher soll der Slave wissen, wann dieser Byte gesendet wird?

 ATMEL sagt folgendes:
1
The system is single buffered in the transmit direction and double buffered in the receive direction

 D.h. du hast Zeit erstes empfangenes Byte zu lesen bis zweites Byte
 (fast) komplett empfangen wird, aber gesendet wird das, was sich beim
 Start des zweiten Bytes im Slave-SPDR befindet.

 Insofern hat c-hater Recht - da ist wahrscheinlich nicht genug Zeit
 zwischen Bytes.
1
 Schritt 1:
2
 SLAVE stellt Antwort Nr.1 bereit.
3
 MASTER sendet Byte Nr.1 und empfängt bereitgestellte Antwort.
4
5
 Schritt x:
6
 MASTER schreibt empfangenes Byte irgendwo, holt sich Byte Nr.x
7
 und schreibt dieses in SPDR.
8
 SLAVE schreibt empfangenes Byte irgendwo, holt sich Antwort Nr.x
9
 und schreibt diese in SPDR.

 Falls der SLAVE Schritt x genauso schnell oder etwas schneller als
 der MASTER abarbeiten kann, gibt es keine Probleme.
 Falls nicht, passiert das, was bei dir passiert ist...

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:
> So viel Text - ich staune immer wieder über die Mühe, die Sie sich
> manchmal machen, c-hater.

Kommt auf den Frager an. Der hier war offensichtlich wirklich 
interessiert daran, zu verstehen, warum das so passiert, wie es passiert 
und er hatte auch bereits selber etwas unternommen, um dahinter zu 
kommen, warum es nicht so läuft, wie erwartet. Das ist offensichtlich 
Fleiß und Wißbegier. Sowas muss man belohnen. Da kann man schonmal etwas 
weiter ausholen und selbst triviale Sachen auch mal etwas erklären. Na 
ja, ich bin sicher kein guter Pädagoge, ich hoffe aber, dass es dem TO 
trotzdem irgendwie geholfen hat.

> Aber ich wollte Ihnen noch erklären, weshalb ich die vielen Klammern
> schreibe, ist mir erst später eingefallen, wie das vor zwei Jahrzehnten
> so kam: wie erwähnt schreibe ich im Zehn-Finger-System, und da stört
> dieses '|' den Fluss ganz enorm; stattdessen verwende ich '+' und nehme
> die dann nötigen Klammern in Kauf, die kommen quasi
> fließend-automatisch.

Potentiell gefährliches Konzept, jedenfall meiner Meinung nach. "|" 
macht nicht dasselbe wie '+'. Bei '+' besteht potentiell das Risiko, 
einen Übertrag in's nächsthöherwertige Bit zu befördern. Das sollte 
natürlich bei der Verknüpfung von einzelnen zu setzenden Bits nicht 
passieren. Aber was, wenn man z.B. durch C&P versehentlich ein Bit 
doppelt setzt...

BTW: Ich schreibe auch mit zehn Fingern. Aber nach einem völlig anderen 
System, als man es früher(tm) in Schreibmaschinenkursen vermittelt 
bekommen hat. Das System ist durch reine Benutzung der Tastatur zu 
Programmieraufgaben aus einem anfänglichen Zweifinger-"Adler-Suchsystem" 
durch unbewußte (oder unterbewußte?) Optimierung quasi von selbst 
entstanden. Ich könnte nicht systematisch aufdröseln, wie es eigentlich 
funktioniert, ich weiß also diesbezüglich nicht wirklich, was ich da 
eigentliche tue. Egal, es funktioniert immerhin ganz gut...

Jedenfalls, solange ich an einer PC-Tastatur mit deutschem 
Standard-Layout sitze...

von S. Landolt (Gast)


Lesenswert?

> ... potentiell das Risiko, einen Übertrag ...

Ich kann mich an einen solchen Fall nicht erinnern, und selbst wenn: mit 
zunehmendem Alter traue ich mir selbst immer weniger, soll heißen, ich 
teste recht ausgiebig.

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:

> Ich kann mich an einen solchen Fall nicht erinnern

Ist aber absolut realistisch. Siehst du sofort den Fehler in:

ldi Rn,(1<<CS10)+(1<<CS10)

Ja klar, jetzt schon, weil du ja sozusagen psychologisch vorbereitet 
bist, den Fehler zu sehen.

Aber finde den mal unvorbereitet...

Und: es gibt Sachen, die sich viel schwerer testen lassen, als die 
Tatsache, ob ein blöder Timer richtig tickt...

von Andreas K. (Gast)


Lesenswert?

Das ist eine deutlich reduzierte Test-Version des eigentlichen Programm 
gewesen.

Da die Antwort nicht funktioniert hat erst mal alles raus, was für die 
SPI nicht notwendig ist.

MISO ist natürlich als Ausgang gesetzt.

Zur Zeitkontrolle habe ich in der Funktion noch einen Ausgang des 8515 
kurz getriggert, nach dem SPDR mit der Antwort auf das zuvor empfangene 
Byte gesetzt wurde. Zwischen SPDR = dataout und erstem Clock des 
nächsten vom Master übertragenen Byte sind ca. 50us. Sollte also genug 
"Pufferzeit" sein.

Dass der Master die erste "richtige" Antwort erst während dem 2. Byte 
bekommt ist klar. Daher wird ja auch vor dem ersten Empfang das SPDR des 
Slave mit 0x00 gesetzt. Beim Master kommt aber 0x05 an.
Wenn z.B. ein 0x0C empfangen wird, wird das SPDR zur Bestätigung 0x0C 
gesetzt. 50us später kommt das 2.Byte vom Master (0x00). Bei diesem Byte 
sendet der Slave aber ein 0x0F. Dann wird SPDR mit 0x00 gefüttert. 
Wieder 50us Später kommt das 3.Byte (0x00) vom Master, während der Slave 
0x0C sendet.

Eine Beispielübertragung hatte ich ja oben geposted.
Das SPI-Protokoll ist bekannt und später wird das auch in einer 
Interrupt-Routine empfangen und sofort das nächste Antwort-Byte in SPDR 
geschrieben, das an entsprechender Stelle in einem Array liegt.

Wie geschrieben, das obige Programm ist eine Hau-Ruck zusammenkürzung um 
alle möglichen Fehlerquellen zu eliminieren. Daher auch die für manche 
eventuell umständliche Schreibweise. Ist halt einfacher zu überfliegen 
wie &=~1<<PB4.

Hoffe eine*r hat noch eine Idee.

Gruß
der Hobbybastler

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Andreas K. schrieb:
> Da die Antwort nicht funktioniert hat erst mal alles raus, was für die
> SPI nicht notwendig ist.

Andreas K. schrieb:
> Hoffe eine*r hat noch eine Idee.

 Ja, ich.
 Vollständigen Code für Master und  Slave als Datei anhängen.
 Glaubst du das ein einziges Codeschnipsel für Master und Slave, dazu
 noch ohne init und mit fehlenden Deklarationen, mit Erklärungen und
 Behauptungen wie:
 Das funktioniert, das ist OK, der Fehler kann hier nicht sein, der
 Slave hat genug Zeit u.s.w. bei der Fehlersuche helfen?

von S. Landolt (Gast)


Lesenswert?

Marc V. kann ich nur zustimmen, aber im Gegensatz zu ihm verabschiede 
ich mich, mir fallen die Schultern runter und ich pfeife durch die Nase. 
Zuerst war ich noch versucht, Watzlawicks Betrunkenen anzuführen, aber 
nein, wäre nur weitere vertane Zeit.

  Allein schon dies:
> ... erst mal alles raus, was für die SPI nicht notwendig ist.
Und dann:
> MISO ist natürlich als Ausgang gesetzt.

von Peter D. (peda)


Lesenswert?

Marc V. schrieb:
> ATMEL sagt folgendes:The system is single buffered in the transmit
> direction and double buffered in the receive direction

Da steht aber noch mehr:

"The system is single buffered in the transmit direction and double 
buffered in the receivedirection. This means that bytes to be 
transmitted cannot be written to the SPI DataRegister before the entire 
shift cycle is completed. When receiving data, however, areceived 
character must be read from the SPI Data Register before the next 
characterhas been completely shifted in. Otherwise, the first byte is 
lost."

Anders gesagt, der Autor widerspricht sich selbst, da ist kein 
Sendepuffer und auch nur ein Byte Empfangspuffer.
Der Slave hat ein WCOL-Bit, was ihm zeigt, daß das Schreiben in die Hose 
gegangen ist. Nur kann sich der Master nichts dafür kaufen.
Keine Ahnung, was die damals bei Atmel geraucht haben müssen, um so ein 
vergurktes SPI zu entwickeln.

Nur wenige neuere AVRs haben einen Puffermodus, den man aber erst 
einschalten muß, z.B. Attiny817:
"
• Unbuffered mode:
The default mode is unbuffered in the transmit direction and single 
buffered in the receive direction.
This means that bytes to be transmitted cannot be written to the 
SPI.DATA register before the entire
shift cycle is completed. When receiving data, a received character must 
be read from the
SPI.DATA register before the next character has been completely shifted 
in. Otherwise, the first
byte will be lost.
• Buffered mode with dummy transfer:
The SPI peripheral is single buffered in the transmit direction and 
double buffered in the receive
direction. A byte written to the transmit register will be copied to the 
shift register when a full
character has been received. When receiving data, a received character 
must be read from the
SPI.DATA register before the third character has been completely shifted 
in to avoid losing data.
• Buffered mode without dummy transfer:
The SPI peripheral is single buffered in the transmit direction and 
double buffered in the receive
direction. A byte written to the transmit register will be copied to the 
shift register when the SPI is
enabled and SS is high."

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.