Hallo zusammen.
System:
ATMEGA8 ( dient als SPI-Master )
ATMEGA8 ( dient als SPI-Master-Dummy )
AVR Studio 4.18
den ganzen Leitungskram kann ich mir hier sparen, da die SPI ansich
funktioniert.
zum Code des Masters:
1
voidspi_init(void)
2
{
3
4
SPI_DDR|=(1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<SPI_SS);
5
SPI_DDR&=~(1<<SPI_MISO);
6
SPI_PORT|=(1<<SPI_SS)|(1<<SPI_MISO);;
7
SPCR|=(1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<CPHA);
8
9
}
10
11
while(1){
12
13
spi_receive_3bword();
14
15
}
16
17
voidspi_receive_3bword(){
18
19
spi_received_3bword[0]=0;
20
spi_received_3bword[1]=0;
21
spi_received_3bword[2]=0;
22
23
24
25
SPDR=SPI_DUMMY;
26
SPI_PORT&=~(1<<SPI_SS);
27
while(!(SPSR&(1<<SPIF)));
28
spi_received_3bword[2]=SPDR;
29
30
SPDR=SPI_DUMMY;
31
while(!(SPSR&(1<<SPIF)));
32
spi_received_3bword[0]=SPDR;
33
34
SPDR=SPI_DUMMY;
35
while(!(SPSR&(1<<SPIF)));
36
spi_received_3bword[1]=SPDR;
37
38
SPI_PORT|=(1<<SPI_SS);
39
40
}
zum Code des Slaves:
1
voidSPI_Init(void)
2
{
3
4
SPI_DDR=(1<<SPI_MISO);
5
SPCR|=(1<<SPE)|(1<<CPHA)|(1<<SPIE);
6
7
}
8
9
SIGNAL(SIG_SPI){
10
11
volatileu8receive_dummy=0;
12
13
SPDR=0b00000001;
14
while(!(SPSR&(1<<SPIF)));
15
receive_dummy=SPDR;
16
SPDR=0b00001000;
17
while(!(SPSR&(1<<SPIF)));
18
receive_dummy=SPDR;
19
SPDR=0b10000000;
20
while(!(SPSR&(1<<SPIF)));
21
22
}
zum Problem:
Mit den oben gezeigten Routinen bekomme ich eine Verbindung; kann senden
und empfangen. Nun möchte ich vom Slave ( was später mal ein
EKG-Messchip ADS1298 sein wird ) mehrere Byte empfangen bekommen ohne
zwischenzeitlich SS auf high zu ziehen, da der ADS1298 24bitconfig + ( 8
x 24bit Messdaten ) durchgängig an den µC sendet. Die Kommunikation wird
vom Master eingeleitet und nur dann sollen Daten vom Slave bekommen
werden. Nun verstehe ich zum einen nicht, wie oben beschriebener Code
funktionieren kann, obwohl die Atmega nur 8bit Puffer haben und die Bits
ja aber durchgängig ( also bei jeder Taktflanke ) gesendet/empfangen
werden sollen.
Da die Datenpakete der Messkanäle 24bit=3Byte groß sind, möchte ich
zunächst natürlich das Übertragen von 3Byte umsetzen. Das tut es auch.
Nur wie man sehen kann überträgt der Slave nicht das erste Byte zuerst,
sondern das dritte. Darauf folgen dann das erste Byte und dann das
mittlere. Ich muss zugeben...bin verwirrt. Jetzt hab ich zum Test die
Arrayelemente angepasst um alle Bytes da zu haben, wo sie sein sollten.
Aber das ist doch Gullorx. Das kann ja nicht der richtige Weg sein und
bei Datenmengen >3Byte klappt das dann sowieso nicht mehr.
Ich bitte um Hilfe, da ich nicht zu einer funktionierenden Kommunikation
komme. Wo ist nur mein Denkfehler?
Habt vielen Dank für eure Mühe!
Hi,
Du solltest dir das Beispiel im Datenblatt nochmal anschauen, dort ist
es eigentlich sehr schön beschrieben.
Du wartest zum beispiel beim slave im Interrupt darauf, daß er fertig
wird, das solltest du nicht dort machen.
Besser ist es wie im Datenblatt beschrieben, dich per Interrupt
"benachrichtigen" zu lassen, und dort auch schon das neue Databyte ins
spi Register zu legen. Danach ist der Interrupt im slave auch schon fast
fertig, du kannst zum Schluss noch das empfangene Byte auslesen, aber
das brauchst du ja erstmal im slave nicht.
Schau Dir mal die Beispiele zu EEPROMs oder RAMs an.
Die Adressen sind immer >8 Bit und im Blockmodus werden auch mehr als 1
Byte gelesen oder geschrieben.
Beide Seiten müssen diesen Modus aber auch unterstützen.
Marc2100 schrieb:> Du wartest zum beispiel beim slave im Interrupt darauf, daß er fertig> wird, das solltest du nicht dort machen.
Woran siehst du den, dass es eine ISR ist ?
Matthias T. schrieb:> Nur wie man sehen kann überträgt der Slave nicht das erste Byte zuerst,> sondern das dritte. Darauf folgen dann das erste Byte und dann das> mittlere. Ich muss zugeben...bin verwirrt. Jetzt hab ich zum Test die
Was spricht dagegen, Bytes so zu übertragen, wie die auch definiert
sind ?
Beides sind deine MEGAs, du kannst die Reihenfolge festlegen, oder ?
Matthias T. schrieb:> obwohl die Atmega nur 8bit Puffer haben und die Bits> ja aber durchgängig ( also bei jeder Taktflanke ) gesendet/empfangen> werden sollen.
Dem SPI Slave ist es egal, wenn der Master zwischen den Bits mal kurz
eine Pause hinlegt - solange CS sich nicht ändert. Mit dem Schreiben auf
SPDR werden jeweils 8 Clock Pulse gesendet.
Matthias T. schrieb:> Nur wie man sehen kann überträgt der Slave nicht das erste Byte zuerst,> sondern das dritte. Darauf folgen dann das erste Byte und dann das> mittlere.
Das ist hier irgendein kranker Beispielcode. Im ADS1298 Handbuch steht
genau drin, wie die Bits kommen müssen - und zwar im normalen Big Endian
Format.
Matthias T. schrieb:> Da die Datenpakete der Messkanäle 24bit=3Byte groß sind, möchte ich> zunächst natürlich das Übertragen von 3Byte umsetzen.
Nein, Du möchtest eine Funktion bauen, die Dir die 27 oder 28 (RDATA
Kommando) Byte auf einmal vom ADS abholt und in ein Array packt.
Marc Vesely schrieb:> Marc2100 schrieb:>> Du wartest zum beispiel beim slave im Interrupt darauf, daß er fertig>> wird, das solltest du nicht dort machen.>> Woran siehst du den, dass es eine ISR ist ?Matthias T. schrieb:> SIGNAL (SIG_SPI)
Ganz neben bei: SIGNAL ist gefühlt seit 20 Jahre veraltet...
danke erstmal für eure Antworten und das ist schon einiges, nach dem ich
nachlesen und testen kann. Ich hatte nur jetzt schon drei Tage gelesen
und...naja...nichts gefunden mit mehreren Bytes...oder ich war zu doof
dazu. egal.
Soweit ich weiss kann ich mit dem Flag DORD die Übertragungsrichtung des
einzelnen Bytes festlegen. Im Code sende ich jedoch
1: 0b10000000
2: 0b00001000
3: 0b00000001
( als Test! ) und so soll es danach im spi_received_3bword-Array auch
drin stehn und nicht verwürfelt
Jim Meba schrieb:> Nein, Du möchtest eine Funktion bauen, die Dir die 27 oder 28 (RDATA> Kommando) Byte auf einmal vom ADS abholt und in ein Array packt.
Nein. Ich möchte gleich jeden Messkanal ( 3Byte ) separat in einem
Array...zwecks Mittelwert/Prüfsumme/CRC-Berechnung
Marc2100 schrieb:> SIGNAL (SIG_SPI), sieht für mich wie ein Interrupt Aufruf aus.
ja das ist veraltet. In dem Fall ist das allerdings unwichtig, da es
funktioniert und ich keinen Unterschied zur neueren ISR kenne.
Matthias T. schrieb:> ( als Test! ) und so soll es danach im spi_received_3bword-Array auch> drin stehn und nicht verwürfelt
LOL.
Reihenfolge beim einschreiben ändern ?
Marc Vesely schrieb:> Reihenfolge beim einschreiben ändern ?
wäre das die "profimethode"? kann ich mir schwer vorstellen. Zumal die
Daten später vom ADS1289 unveränderbar sind bis sie im Master mC landen
Matthias T. schrieb:> wäre das die "profimethode"? kann ich mir schwer vorstellen. Zumal die> Daten später vom ADS1289 unveränderbar sind bis sie im Master mC landen
Du sendest 0x80, 0x08, 0x01, schreibst aber 0x01, 0x80, 0x08 in dein
Array.
Warum ?
Ist das die "profimethode" ?
Kann ich mir aber auch schwer vorstellen.
Marc2100 schrieb:> Du wartest zum beispiel beim slave im Interrupt darauf, daß er fertig> wird, das solltest du nicht dort machen.
mir ist das bekannt und in regulärem Code würde ich eine ISR auch nur so
kurz es geht beanspruchen. In dem Fall hier ist es so, dass ich alle 1S
vom Master eine Kommunikation aufbaue und damit die ISR des Slaves mehr
als genug Zeit hat, die ISR abzuarbeiten, ohne dass ein anderer
Interrupt oder dieser stört.
Marc Vesely schrieb:> Du sendest 0x80, 0x08, 0x01, schreibst aber 0x01, 0x80, 0x08 in dein> Array.
sry war mein Fehler.
ich sende:
1: 0x01
2: 0x08
3: 0x80
wenn ich jetzt das Array im Master so anlegen würde, dass es mit
0...beginnt, würden die Elemente quasi:
..[0]: 0x80
..[1]: 0x01
..[2]: 0x08
beeinhalten. Es soll aber nach der gesendeten Reihenfolge ins Array
geschrieben werden.
Also da muss was an der Übertragung falsch sein, da die Bytes ansich
richtig übermittelt werden.
Warum sendet der Slave nicht einfach 1,2,3 in Abfolge sondern 3,1,2. Das
ist mir noch schleierhaft.
Matthias T. schrieb:> Warum sendet der Slave nicht einfach 1,2,3 in Abfolge sondern 3,1,2. Das> ist mir noch schleierhaft.
0x01, 0x08, 0x80 werden gesendet.
0x01 ist erstes Byte der gesendet (und empfangen) wird.
0x08 ist zweites Byte der gesendet (und empfangen) wird.
0x80 ist drittes Byte der gesendet (und empfangen) wird.
Warum schreibst du diese Bytes nicht einfach so, wie die empfangen
werden, in deinen Array ?
Matthias T. schrieb:> Nun verstehe ich zum einen nicht, wie oben beschriebener Code> funktionieren kann, obwohl die Atmega nur 8bit Puffer haben und die Bits> ja aber durchgängig ( also bei jeder Taktflanke ) gesendet/empfangen> werden sollen.
Was genau verstehst du daran nicht? Für die SPI-Logik an sich spielt der
Zustand der SS-Leitung so gut wie keine Rolle, mal abgesehen von der
Tatsache, daß SS auf High die Anschlüsse des Slave in den High-Z-Status
bringt und ein Pegelwechsel an SS von High auf Low die SPI-Logik
zurücksetzt.
Der Trick ist einfach, daß der Zustand nach dem Rücksetzen der Logik
exakt derselbe Zustand ist, den sie auch dann hat, wenn eben gerade das
achte Bit einer Transaktion übertragen wurde. Es spielt also für die
Übertragung an sich absolut keine Geige, ob zwischen den Bytes SS
toggelt oder nicht.
Das muß aber nicht so sein. Es gibt (viele) Geräte, die das Signalspiel
an SS zur Wortsynchronisation benutzen. Für solche Geräte ist es
natürlich dann nicht egal, wenn SS zwischen den Bytes auf Low geht, die
würden dann immer nur das "erste" Byte senden, weil eben der "not
selected"-Status den Bytezähler zurücksetzt.
Dieses Verhalten kann man natürlich auch mit einem AVR nachbauen, wenn
man das möchte.
> Da die Datenpakete der Messkanäle 24bit=3Byte groß sind, möchte ich> zunächst natürlich das Übertragen von 3Byte umsetzen. Das tut es auch.> Nur wie man sehen kann überträgt der Slave nicht das erste Byte zuerst,> sondern das dritte. Darauf folgen dann das erste Byte und dann das> mittlere.
Nun, ich habe das DB des Teils nicht gelesen. Entweder ist das sozusagen
die "natürliche" Reihenfolge in der die Daten geliefert werden, oder es
gibt einen Mechanismus zur Wortsynchronisation, den du nicht korrekt
implementiert hast (weil du das DB ebenfalls nicht gelesen hast). Die
Wortsynchronisation muß übrigens nicht zwingend über das Signalspiel an
SS erfolgen, sie kann auch über die vom Master gesendeten Daten
erfolgen.
Welche Variante dein Teil verwendet, steht in seinem Datenblatt. Am
besten, du liest es einfach mal und programmierst dann deinen Master so,
daß er es korrekt so umsetzt, wie es der Slave braucht.
Was mir noch auffällt, wann löst der Interrupt eigentlich das erste mal
aus, im slave?
So wie ich das sehe, ja erst nachdem du den ersten Transfer vom Master
initiiert hast. Dann wartest du in der Isr auf die nächste drei
Übertragungen, während der Master die erste falsche, da im slave nicht
festgelegten, Daten ins Array schreibt, sodass du ab da, immer eine
falsche Reihe folge hast.
Marc Vesely schrieb:> Warum schreibst du diese Bytes nicht einfach so, wie die empfangen> werden, in deinen Array ?
so würde ich es erwarten und auch gern so machen. Nur steht im Array
nach Empfang:
Element 1 ([0]) hat 0x80
Element 2 ([1]) hat 0x01
Element 3 ([2]) hat 0x08
das ist aber nicht was ich gesendet hab :(
Wie das Array im Master jetzt von der SPI gefüllt wird stünden die Werte
an richtiger Stelle. Aber das wäre halt scheisse. Ich möchte
Byte 1 -> Element 1
Byte 2 -> Element 2
Byte 3 -> Element 3
Wenn ich das so anlege, steht aber danach im Array:
Element 1 hat Byte 3
Element 2 hat Byte 1
Element 3 hat Byte 2
Das soll nicht!!!
Matthias T. schrieb:> Warum sendet der Slave nicht einfach 1,2,3 in Abfolge sondern 3,1,2. Das> ist mir noch schleierhaft.
Die ISR im Slave startet nach einer Übertragung, also ist das erste
Byte, dass der Master empfängt, Müll.
Der Master startet die Übertragung des zweiten Bytes direkt nach dem
ersten. Das ist zu wenig Zeit für den Slave, um in der ISR überhaupt zu
dem Punkt zu kommen, wo das erste echte Byte ins Datenregister
geschrieben wird. Also ist auch das zweite von Master empfangene Byte
Müll.
Das dritte empfangene Byte ist dann erste echte Datenbyte.
Zu allem Überfluss startest du die Übertragung auch noch, bevor du das
SS-Signal setzt.
Stefan Ernst schrieb:> Zu allem Überfluss startest du die Übertragung auch noch, bevor du das> SS-Signal setzt.
das war ein Test und wundert mich ehrlich gesagt ja auch. Nur - erst SS
auf low zu ziehen und dann das SPDR zu "befüllen" führte zu keinem
besseren Ergebnis - leider.
ich werde all eure Vorschläge jetzt mal durchackern und dabei auf die
Lösung hoffen. Ich weiss es liegt an mir und meinem Verständnis und
deshalb wird "begreifen" des Kommunikationsablaufes hier das
Schlüsselwort sein ;)
ich teste/ lerne und berichte natürlich sobald ich mehr weiss
vielen vielen Dank euch erstmal!!!
Matthias T. schrieb:> Marc Vesely schrieb:>> Warum schreibst du diese Bytes nicht einfach so, wie die empfangen>> werden, in deinen Array ?>> so würde ich es erwarten und auch gern so machen. Nur steht im Array> nach Empfang:>> Element 1 ([0]) hat 0x80> Element 2 ([1]) hat 0x01> Element 3 ([2]) hat 0x08>> das ist aber nicht was ich gesendet hab :(
Dein Code im Master
1
SPDR=SPI_DUMMY;
2
SPI_PORT&=~(1<<SPI_SS);
3
while(!(SPSR&(1<<SPIF)));
4
spi_received_3bword[2]=SPDR;
5
6
SPDR=SPI_DUMMY;
7
while(!(SPSR&(1<<SPIF)));
8
spi_received_3bword[0]=SPDR;
9
10
SPDR=SPI_DUMMY;
11
while(!(SPSR&(1<<SPIF)));
12
spi_received_3bword[1]=SPDR;
reduziert auf das wichtige
1
...
2
spi_received_3bword[2]=SPDR;
3
...
4
spi_received_3bword[0]=SPDR;
5
...
6
spi_received_3bword[1]=SPDR;
Vergleich die Indizies.
Genau hier verwürfelst du die Daten.
Beim SPI dreht sich da gar nichts um. In der Reihenfolge, in der
gesendet wird, in der Reihenfolge wird auch empfangen.
Es liegt an dir, bzw. an deinem Code die Reihenfolge festzulegen. Schick
sie im Slave genau in der Reihenfolge weg, in der du sie gespeichert
haben willst und speichere sie im Master mit aufsteigenden Indizes im
Array. Das ist wirklich keine Hexerei. Je mehr du da künstelst, um so
eher baust du dir da Fehler ein.
1
SPI_PORT&=~(1<<SPI_SS);
2
for(i=0;i<3;i++){
3
SPDR=SPI_DUMMY;
4
while(!(SPSR&(1<<SPIF)));
5
spi_received_3bword[i]=SPDR;
6
}
es wird auf 3 Bytes vom Slave gewartet und genau in der Reihenfolge, in
der die Bytes eintrudeln, werden sie im Array abgelegt.
Karl Heinz schrieb:> Vergleich die Indizies.> Genau hier verwürfelst du die Daten.
Und noch einer. ;-)
Matthias T. schrieb:> Jetzt hab ich zum Test die> Arrayelemente angepasst um alle Bytes da zu haben, wo sie sein sollten.> Aber das ist doch Gullorx.
Seine Frage lautet also eigentlich:
"Warum muss ich die Daten so verwürfelt ins Array Schreiben, damit der
Inhalt dann dem entspricht, was ich bei normaler Reihenfolge erwarten
würde?"
Und die Antwort steht (denke ich) in meinem Post.
Jetzt sende ich zum Anfang der Kommunikation ein Dummy-Byte, um am Slave
die ISR auszulösen, und ich habe am Master zwischen jedem Bytesenden
etwas Zeit gelassen. Jetzt kommen die Bytes auch da an im Array, wo sie
hin sollen.
Einmal je Sekunde iniziiert der Master nun diese Kommunikation.
Jetzt jedoch verschieben sich die Werte jeweils um ein Element. Also:
1e Kommunikation:
0x01 -> ..[0]
0x08 -> ..[1]
0x80 -> ..[2]
2e Kommunikation:
0x08 -> ..[0]
0x80 -> ..[1]
0x01 -> ..[2]
3e Kommunikation:
0x80 -> ..[0]
0x01 -> ..[1]
0x08 -> ..[2]
u.s.w.
Im Anhang hab ich mal den Ablauf, wie ich ihn möchte
Matthias T. schrieb:> Jetzt sende ich zum Anfang der Kommunikation ein Dummy-Byte
Und was genau ist für dich jetzt "Anfang der Kommunikation"?
Wenn das innerhalb der Hauptschleife passiert, wundert mich das
beobachtete Verhalten nicht.
Ansonsten: Code zeigen.
Edit:
Dir ist schon klar, dass die Änderungen, die du jetzt am Master
vornimmst, nur wegen deines speziellen Fake-Slaves nötig sind, oder?
> Nun möchte ich vom Slave ( was später mal ein> EKG-Messchip ADS1298 sein wird )
Da sieht die Sache dann wieder anders aus.
Stefan Ernst schrieb:> Wenn das innerhalb der Hauptschleife passiert, wundert mich das> beobachtete Verhalten nicht.
Die Hauptschleife führt momentan zur Fehlersuche nur einmal die Sekunde
die SPI-Funktion aus.
Stefan Ernst schrieb:> Dir ist schon klar, dass die Änderungen, die du jetzt am Master> vornimmst, nur wegen deines speziellen Fake-Slaves nötig sind, oder?
Ja. Es geht im Moment nur um die rudimentäre Funktionsweise. Der ADS1298
benötigt dann andere Einstellungen zur Kommunikation. Aber ich muss
Schritt für Schritt sicherstellen, dass jede Funktion perfekt läuft. Ich
habe schon USART mit 9830400Hz, die Umrechnung und einen Sheduler
umgesetzt. Ist im Mom aber alles ausgeblendet.
Matthias T. schrieb:> SPDR = SPI_DUMMY;> SPI_PORT &= ~(1<<SPI_SS);
/SS muß natürlich auf 0 gesetzt werden, bevor man sendet.
Matthias T. schrieb:> SIGNAL (SIG_SPI) {>> volatile u8 receive_dummy = 0;>> SPDR = 0b00000001;
Du schreibst also erst, nachdem ein Byte gesendet wurde.
Das geht so nicht.
Du mußt einen externen Interrupt auf die fallende Flanke von /SS
auslösen und dann sofort das 1. Byte laden.
Der AVR ist allerdings ein mangelhafter Slave, da er keinen Sendepuffer
hat. Daher muß der Master vor jedem Dummy-Senden etwas warten, z.B.
10µs.
Peter Dannegger schrieb:> Der AVR ist allerdings ein mangelhafter Slave, da er keinen Sendepuffer> hat. Daher muß der Master vor jedem Dummy-Senden etwas warten, z.B.> 10µs.
genau das hab ich jetzt getan.
der Code sieht im Moment so aus
Master:
Aus mir unersichtlichen Gründen bestehen weiterhin folgende Ergebnisse:
1e Kommunikation:
0x01 -> ..[0]
0x08 -> ..[1]
0x80 -> ..[2]
2e Kommunikation:
0x08 -> ..[0]
0x80 -> ..[1]
0x01 -> ..[2]
3e Kommunikation:
0x80 -> ..[0]
0x01 -> ..[1]
0x08 -> ..[2]
u.s.w.
ich verstehe nicht wo dieses Schieben stattfindet???
Matthias T. schrieb:> der Code sieht im Moment so aus
Also doch, das Senden des Dummy-Bytes steht innerhalb der Hauptschleife.
Wenn du laufend ein Byte "vernichtest", statt wirklich nur das
allererste, dann fehlt dir natürlich auch laufend ein Byte.
Matthias T. schrieb:>> Also doch, das Senden des Dummy-Bytes steht innerhalb der Hauptschleife.
Aber wo soll es denn sonst stehen? Jetzt bin ich gänzlich verwirrt.
Und warum genau geht mir da je ein Byte verloren? Ich sende pro
transmission 4x das Dummy. Bitte schau nochmal auf den Ablaufplan im
Anhang. Wo liegt nun der Unterschied zum Code?
Matthias T. schrieb:> Aber wo soll es denn sonst stehen? Jetzt bin ich gänzlich verwirrt.
Eine Tatsache, die dir offensichtlich nicht klar ist, und dir vielleicht
ein Licht aufgehen lässt:
Bei deinem Slave-Code ist das Interrupt-Flag am Ende der ISR gesetzt, so
dass die ISR nach ihrem Ende gleich wieder aufgerufen wird.
So und und nun mal die Lösung. Jetzt funktioniert alles, auch wenn ich
die Kommunikation jetzt natürlich noch anpassen muss.
Der "Fehler" lag auf der Slaveseite. Nach dem letzten Byte einer Abfrage
des Masters muss ich das Slave-SPI noch mit 0x00 "leeren". Jetzt steht
der Inhalt bei neuer Anfrage wieder auf null und alles läuft wie
erwartet.
Ich danke nochmals sehr für eure Bemühungen !!!