Hallo Liebe Forumgemeinde,
nachdem ich mich mit etlichen Tutorials und Infoblatt vom Atmel
durchgeforstet habe versuche ich mal jetzt von euch ein wenig Hilfe zu
bekommen.
Es geht wie im Betreff schon geschrieben um die Datenübertragung
zwischen zweier ATMega8 anhand der Nutzung von SPI.
Ich erkläre erstmal was ich rausgefunden habe und falls es nicht richtig
sein soll bitte ich um Korrektur.
Also SPI Kommunikation findet zwischen Master & Slave statt und dabei
müssen die MISO, MOSI und SCK Pins miteinander verbunden werden?
MISO und MOSI sind die Datenkanäle.
Über SCK wird der Taktsignal übertragen und je nach eingestellem Modus
wird erst bei eine steigende (auf HIGH) oder fallende (auf LOW)
Taktflanke der Zustand vom MISO/MOSI beim Empfänger/Sender gelesen oder
halt geschrieben.
Die Datenübertragung beginnt immer dann wenn SS von HIGH auf LOW gesetzt
wird. Erst wenn SS auf LOW gezogen ist kann die Übertragung starten.
CPOL und CPHA sind Bits die ich setzen muss um die o.g. Modus
einzustellen. Soweit ich das verstanden habe sieht es folgendermaßen
aus.
CPOL = 0 , CPHA = 0 : Bit wird bei steigender Taktflanke übertragen
CPOL = 0 , CPHA = 1 : Bit wird bei fallender Taktflanke übertragen
CPOL = 1 , CPHA = 0 : Bit wird bei fallender Taktflanke übertragen
CPOL = 1 , CPHA = 1 : Bit wird bei steigender Taktflanke übertragen
Je nachdem ob es bei fallender oder steigender Taktflanke übertragen
wird, wird das Bit bei gegensätziger Taktflanke geschoben (Shift). Ich
habe mir darüber keine Gedanken gemacht, bei welcher Taktflanke gelesen
oder geschoben wird...
Nun stellt sich für mich die Frage: Auf dem folgenden Atmel Datenblatt
über SPI:
http://www.atmel.com/Images/Atmel-2585-Setup-and-Use-of-the-SPI_ApplicationNote_AVR151.pdf
steht das weiter unten auch so geschrieben, wie ich es erklärt habe...
Weiter oben ist aber in der Zeichnung (Figure 1 - 1 Master and slave
interface) Master und Slave so beschaltet, dass der Master sein SS PIN
auf VCC und der Slave sein SS PIN auf GND schaltet.
Wie soll laut dieser Zeichnung der Master den SS Pin auf LOW setzen
damit eine Übertragung beginnt?
Desweiteren stand im Datenblatt übersetzt: 'Master generiert nur dann
Taktsignale wenn es Daten sendet. Dies bedeutet, dass der Master Daten
an den Slave senden muss um Daten vom Slave zu bekommen.' Da musste ich
kurz überlegen warum aber dachte mir okay, dann schicke ich den einfach
lauter nullen, wenn nichts anliegt.
So hatte ich das vor:
1
Master Slave
2
SCK OUTPUT INPUT
3
MISO INPUT OUTPUT
4
MOSI OUTPUT INPUT
5
SS OUTPUT INPUT
Nachdem ich die beiden Mega8 mit den jeweiligen PINS aneinander
angeschlossen habe (SCK - SCK , MISO - MISO, MOSI - MOSI, SS - SS) muss
ich ja die Master und Slave Seite wie oben erklärt erstmal
Konfigurieren:
MASTER:
1
2
voidspiInitMaster(){
3
DDRB|=((1<<PB5)|(1<<PB3)|(1<<PB2));//SCK, MOSI, SS als output
4
DDRB&=~(1<<PB4);// MISO als INPUT, wobei muss ich ja nicht mehr machen?
DDRB|=(1<<PB4);// MISO als OUTPUT rest kann ja standartmäßig so bleiben
4
/* Enable SPI, Master, set clock rate fck/16 */
5
SPCR=(1<<SPE);
6
7
}
8
9
unsignedcharspiSlaveTransmit(charcData){
10
/* Start transmission */
11
SPDR=cData;
12
/* Wait for transmission complete */
13
while(!(SPSR&(1<<SPIF)));
14
returnSPDR;
15
}
Die Programmcodes die Ihr oben sieht habe ich aus dem kompletten
Datenblatt der Mega8 (vllt. ein wenig optimiert).
Nun erkläre ich euch mal in PSEUDOCODE wie ich vorgehen wollte:
1
SOLANGE !WELTUNTERGANG :
2
MASTER & SLAVE rufen ihre spiInit... Methoden auf
3
SLAVE überwacht SS PIN und wartet auf ein LOW Signal
4
MASTER will senden
5
MASTER setzt SS PIN auf LOW
6
MASTER ruft SPI_MasterTransmit auf
7
SLAVE bekommt den LOW Signal an SS PIN mit und ruft spiSlaveTransmit auf
8
MASTER & SLAVE Warten bis Datenübertragung zuende ist
9
MASTER setzt den SS PIN auf HIGH
10
MASTER & SLAVE können die empfangenen Daten verarbeiten
Ich bedanke mich bei allen, die alles durchgelesen haben und mir
versuchen werden zu helfen :)
Sercan S. schrieb:> Keiner Ideen?
Soweit uch das auf die Schnelle überblicke machst du alles
richtig bis auf:
Sercan S. schrieb:> SLAVE bekommt den LOW Signal an SS PIN mit und ruft spiSlaveTransmit auf
bzw.
1
SercanS.schriebimBeitrag#4938136:
2
unsignedcharspiSlaveTransmit(charcData){
3
/* Start transmission */
4
SPDR=cData;
5
/* Wait for transmission complete */
6
while(!(SPSR&(1<<SPIF)));
7
returnSPDR;
8
}
Der Slave muss nicht explizit einen "Receive-Transmit" starten da
er vom Master selbständig die Daten hereingeclockt bekommt.
Im Slave musst du nur per Status-Flag abwarten (ich glaube es
kommt nur das Interrupt-Flag in Frage) bis das SPI-Datenregister
voll ist, dann kannst du es z.B. mit
1
value=SPDR;
einfach abholen.
Sercan S. schrieb:> Weiter oben ist aber in der Zeichnung (Figure 1 - 1 Master and slave> interface) Master und Slave so beschaltet, dass der Master sein SS PIN> auf VCC und der Slave sein SS PIN auf GND schaltet.
Das ist wohl ein Irrtum vom Amt.
Endlich eine Antwort, ich hatte schon langsam die Hoffnung aufgegeben
und habe mir wahrscheinlich 1000 weitere Tutorials & Infos über SPI
gelesen.
Was meinst du genau mit den Status-Flag abwarten. Also mir ist bewusst,
dass es wie ein Schieberegister funktioniert und es 8-Bits
'rübergeclockt' werden müssen. Aber wie gehe ich dabei vor? Wie Prüfe
ich das? Muss ich die Taktflanken beim SCK zählen und bei 8 steigenden
Taktflanken (8-Bits, kamen rein) versuchen zu lesen, weil dann genau ein
Byte reingekommen ist?
Ich bin echt überfordert, da es alle in den Tutorials mit Ihren Arduinos
ohne Probleme hinbekommen und bin echt Froh, dass da mal ein Antwort
reingekommen ist... :)
SPISpezialist schrieb:> Sercan S. schrieb:>> Keiner Ideen?>> Soweit uch das auf die Schnelle überblicke machst du alles> richtig bis auf:>> Sercan S. schrieb:>> SLAVE bekommt den LOW Signal an SS PIN mit und ruft spiSlaveTransmit auf>> bzw.Sercan S. schrieb:> unsigned char spiSlaveTransmit(char cData) {> /* Start transmission */> SPDR = cData;> /* Wait for transmission complete */> while(!(SPSR & (1<<SPIF)));> return SPDR;> }>> Der Slave muss nicht explizit einen "Receive-Transmit" starten da> er vom Master selbständig die Daten hereingeclockt bekommt.> Im Slave musst du nur per Status-Flag abwarten (ich glaube es> kommt nur das Interrupt-Flag in Frage) bis das SPI-Datenregister> voll ist, dann kannst du es z.B. mit> value = SPDR;>> einfach abholen.>> Sercan S. schrieb:>> Weiter oben ist aber in der Zeichnung (Figure 1 - 1 Master and slave>> interface) Master und Slave so beschaltet, dass der Master sein SS PIN>> auf VCC und der Slave sein SS PIN auf GND schaltet.>> Das ist wohl ein Irrtum vom Amt.
Sercan S. schrieb:> Was meinst du genau mit den Status-Flag abwarten.
Das SPI-Statusregister (SPSR) hat ein Interrupt-Flag (Bit 7)
welches (im Slave Mode) gesetzt wird wenn ein vollständiges
Byte vom Master hereingekommmen ist. Siehe Datenblatt Seite 130.
Du musst dafür keinen Global Interrupt Enable machen (sei()),
das Flag wird immer bei Gegebenheit gesetzt.
> Das SPI-Statusregister (SPSR) hat ein Interrupt-Flag (Bit 7)> welches (im Slave Mode) gesetzt wird wenn ein vollständiges> Byte vom Master hereingekommmen ist. Siehe Datenblatt Seite 130.>> Du musst dafür keinen Global Interrupt Enable machen (sei()),> das Flag wird immer bei Gegebenheit gesetzt.
Bevor du wieder verschwindest, sende ich am besten mal mein Code beider
Seiten vielleicht entdeckst du ja etwas, was mir entgeht oder was ich
komplett falsch mache.
Master-Vollständig
1
#define F_CPU 8000000UL
2
#include<asf.h>
3
#include<avr/io.h>
4
#include<util/delay.h>
5
6
charsend='1';
7
floatincrement=0.0;
8
boolledOn=false;
9
voidSPI_MasterInit(void){
10
/* Set MOSI and SCK output, all others input */
11
DDRB=(1<<PB5)|(1<<PB3)|(1<<PB2);
12
PORTB|=(1<<PB2);
13
/* Enable SPI, Master, set clock rate fck/16 */
14
SPCR=(1<<SPE)|(1<<MSTR)|(1<<SPR0);
15
}
16
voidSPI_MasterTransmit(charcData){
17
/* Start transmission */
18
PORTB&=(1<<PB2);
19
SPDR=cData;
20
/* Wait for transmission complete */
21
while(!(SPSR&(1<<SPIF)))
22
PORTB|=(1<<PB2);
23
;}
24
25
intmain(void)
26
{
27
/* Insert system clock initialization code here (sysclk_init()). */
28
29
board_init();
30
// Timer 0 konfigurieren
31
TCCR0=(1<<CS00);// Start Timer 0 with prescaler 1024
Wie du sehen kannst, schalte ich den PB2 also den SS Pin erst einmal
wider auf LOW und möchte es hochschalten wenn ich mit der Übertragung
fertig bin, damit der Slave es mitbekommt. Du hast eigentlich Recht, da
ich nur ein Slave habe brauche ich das ja nicht zu prüfen. Um Ehrlich zu
sagen, habe ich das aus purer Verzweiflung gemacht, weil es sonst ja
nicht richtig geklappt hat.
Ich habe es in allen Varianten versucht, was im Internet so rumerzählt
wird.
SS an SS, Master-SS an VCC & Slave-SS an GND und noch andersrum, Beide
SS nicht beschaltet, Master allein auf VCC/GND, Slave allein auf
VCC/GND. Nichts klappt.
Ich muss auch hinzufügen: Ich habe gerade getestet ob der SCK überhaupt
Taktsignale erzeugt, da ich kein gescheites Oszilloskop hier habe, habe
ich einfach an den SCK PIN (mit Vorwiderstand natürlich), ein LED
geschaltet und siehe da LED leuchtet nicht? Also werden dort dann ja
auch keine Taktsignale erzeugt oder? Ich sende doch ununterbrochen, die
gleichen Daten vom Master aus an den Slave?
Vielleicht kannst du ja mein Programm dort auf Fehler überprüfen und
ggfls. etwas verändern wodurch das ganze hier ein Ende findet. Da die
TXD/RXD PINs bereits durch Bluetooth benutzt werden, kamen diese leider
nicht in Frage...
Ich hoffe ich schaffe das alles mal bald...
Sercan S. schrieb:> void SPI_MasterTransmit(char cData) {> /* Start transmission */> PORTB &= (1<<PB2);> SPDR = cData;> /* Wait for transmission complete */> while(!(SPSR & (1<<SPIF)))> PORTB |= (1<<PB2);> ; }
Erstens machst du es verkehrt, zweitens musst du nur einmal vorher
SS Pin als Ausgang setzen, alles andere macht deine MEGA alleine.
Lass SS Pin in Ruhe !!
Sercan S. schrieb:> vielleicht entdeckst du ja etwas
Zuerst müsstest du dazu sagen was du von deinem Code erwartest.
Dann:
- board_init() fehlt total, sowohl bei Master als auch bei Slave
Sercan S. schrieb:> if(!(PINB & (1<<PB2))){
Du brauchst den SlaveSelect nicht abfragen, es reicht das SPI
Status Register zu fragen. Denn wenn SlaveSelect Low ist hast
du noch kein Byte empfangen. Das Status Register gibt zuverlässig
Auskunft darüber.
Sercan S. schrieb:> ein LED> geschaltet und siehe da LED leuchtet nicht? Also werden dort dann ja> auch keine Taktsignale erzeugt oder?
Nein, nicht unbedingt. Das SPI Telegramm kann so kurz und schnell
sein dass du die LED nicht leuchten siehst.
SPISpezialist schrieb:> - board_init() fehlt total, sowohl bei Master als auch bei Slave
Ach so, kommt wohl vom ASF (das ich nicht benutze) ....
Marc V. schrieb:> Sercan S. schrieb:>> void SPI_MasterTransmit(char cData) {>> /* Start transmission */>> PORTB &= (1<<PB2);>> SPDR = cData;>> /* Wait for transmission complete */>> while(!(SPSR & (1<<SPIF)))>> PORTB |= (1<<PB2);>> ; }>> Erstens machst du es verkehrt, zweitens musst du nur einmal vorher> SS Pin als Ausgang setzen, alles andere macht deine MEGA alleine.>> Lass SS Pin in Ruhe !!
Ich würde gerne aber da alles nicht geklappt hat und ich es vorher ohne
den SS PIN versucht habe, habe ich natürlich dort rumgefuscht. :D
>Sercan S. schrieb:>> vielleicht entdeckst du ja etwas>>Zuerst müsstest du dazu sagen was du von deinem Code erwartest.
Als erstes will ich, dass es Problemfrei hinbekommt den einen Byte zu
senden, den ich versuche zu senden. Also
1
charsend='1';
Was kommt eigentlich beim Empfänger an wenn ich
1
charsend='1';
oder
1
charsend=1;
sende? Kommt da beim '1' die 49 (wegen ASCII) und bei 1 tatsächlich die
1 an?
>>Dann:>>- board_init() fehlt total, sowohl bei Master als auch bei Slave
board_init() ist doch da :O ?
>>Sercan S. schrieb:>> if(!(PINB & (1<<PB2))){>>Du brauchst den SlaveSelect nicht abfragen, es reicht das SPI>Status Register zu fragen. Denn wenn SlaveSelect Low ist hast>du noch kein Byte empfangen. Das Status Register gibt zuverlässig>Auskunft darüber.
also wie Marc Veseyl geschrieben hat muss ich einfach while(!(SPSR &
(1<<SPIF))) aufrufen und warten bis dort was reingeschrieben wurden ist?
Marc V. schrieb:> Lass SS Pin in Ruhe !!
Ich verspüre - wie in anderen Beiträgen auch - eine gewisse
Agressivität im Ton.
Auch höre ich zwischen den Zeilen dauernd:
"du machst alles scheisse, mach das gefälligst besser!"
Sercan S. schrieb:> also wie Marc Veseyl geschrieben hat muss ich einfach while(!(SPSR &> (1<<SPIF))) aufrufen und warten bis dort was reingeschrieben wurden ist?
Ja.
>Marc V. schrieb:>> Lass SS Pin in Ruhe !!>>Ich verspüre - wie in anderen Beiträgen auch - eine gewisse>Agressivität im Ton.
Alles gut. Nicht streiten :D
>>Auch höre ich zwischen den Zeilen dauernd:>"du machst alles scheisse, mach das gefälligst besser!">>SPISpezialist schrieb:>> Sercan S. schrieb:>>> also wie Marc Veseyl geschrieben hat muss ich einfach while(!(SPSR &>>> (1<<SPIF))) aufrufen und warten bis dort was reingeschrieben wurden ist?>>>> Ja.
Und die PINS wirklich so lassen also (MOSI-MOSI, MISO-MISO, SCK-SCK,
SS-SS)?
SPISpezialist schrieb:> Ich verspüre - wie in anderen Beiträgen auch - eine gewisse> Agressivität im Ton.
Irgendwie verspüre ich gewisse Hörschwierigkeiten.
Grossbuchstaben bedeuten agressives Schreien.
Ausrufungszeichen betont etwas.
ICH HABE ABER NICHT GESCHRIEN, IST DAS KLAR ?!
:-)
Noch ein Tip:
Mache ein neues Projekt auf und kopiere deine existierenden
Sourcen, reduziere alles auf das Minimum was du brauchst um
deine SPI-Übertragung erfolgreich durchzuführen. Wenn das
funktioniert kannst du ja dein übriges Programm drum herum
bauen.
Marc V. schrieb:> IST DAS KLAR ?!
Schon dieser Teilsatz erzeugt ein gewisses Schauern bei mir.
Ehrlich gesagt möchte ich dir nicht am Tisch gegenüber sitzen.
Auch nicht neben dir.
Also, ich habe das jetzt gerade so hingekriegt, dass die Totalsumme von
1010 auf der Segmentanzeige angezeigt wurde sprich, ich kam beim Slave
aus der while schleife raus, wo die Datenübertragung stattgefunden hat?
Also habe ich ja Daten bekommen, sprich es hat geklappt? :O
Anschließend habe ich versucht, den totalSum statt mit 1010
abzuspeichern mit den empfangenen Byte zu belegen. Aber wenn ich
(totalSum ist ein int) totalSum += (int)SPI_SlaveReceive();
oder gar ohne den int-Cast versuche, klappt die Anzeige nicht... :hmmm:
Sercan S. schrieb:> also wie Marc Veseyl geschrieben hat muss ich einfach while(!(SPSR &> (1<<SPIF))) aufrufen und warten bis dort was reingeschrieben wurden ist?
Ja, aber bestimmt nicht in der ISR(TIMER0_OVF_vect).
P.S.
Wenn über SPI nichts kommt, funktioniert auch deine Anzeige nicht.
Das SPI des AVR ist nicht gerade das, was man als Slave haben will. Es
ist ne Krücke.
Du mußt alle 4 Leitungen verbinden und das SS des Master muß vor dem
SPI-Init als Ausgang gesetzt werden. Damit ist SS vom SPI abgekoppelt,
d.h. ein ganz normaler Output-Pin.
Dann mußt Du als Master mit SS=0 dem Slave sagen, daß ein Byte beginnt,
d.h. nur so wird der Slave Bit-synchron.
Sind alle Bytes übertragen, mußt Du SS=1 setzen.
Vorzugsweise nimmt man als Slave nicht den alten Mega8, sondern den
Mega88 und setzt für SS den Pin-change Interrupt. Nur so kann man Anfang
und Ende eines Paketes feststellen. Falls es der Mega8 sein muß, muß man
eben den SS-Pin mit dem INT0/1-Pin verbinden.
Will der Master was lesen, muß er vor jedem Byte eine Pause lassen,
damit der Slave in den Interrupt springen und das Byte in SPDR schreiben
kann. Ich würde >=100µs Pause empfehlen.
Peter D. schrieb:> Das SPI des AVR ist nicht gerade das, was man als Slave haben will. Es> ist ne Krücke.>> Du mußt alle 4 Leitungen verbinden und das SS des Master muß vor dem> SPI-Init als Ausgang gesetzt werden. Damit ist SS vom SPI abgekoppelt,> d.h. ein ganz normaler Output-Pin.> Dann mußt Du als Master mit SS=0 dem Slave sagen, daß ein Byte beginnt,> d.h. nur so wird der Slave Bit-synchron.> Sind alle Bytes übertragen, mußt Du SS=1 setzen.>> Vorzugsweise nimmt man als Slave nicht den alten Mega8, sondern den> Mega88 und setzt für SS den Pin-change Interrupt. Nur so kann man Anfang> und Ende eines Paketes feststellen. Falls es der Mega8 sein muß, muß man> eben den SS-Pin mit dem INT0/1-Pin verbinden.>> Will der Master was lesen, muß er vor jedem Byte eine Pause lassen,> damit der Slave in den Interrupt springen und das Byte in SPDR schreiben> kann. Ich würde >=100µs Pause empfehlen.
Jetzt kommts...
Nun soll ich den SS-PIN doch setzen und den vordem SPI-Init als Ausgang
setzen und esklappt irgendwie vorne und hinten nicht.... Ich glaube SPI
ist bisschen komplizierter als ich es mir gedacht habe.
Was haltet Ihr von I2C? Ich bin bei der Recherche auf I2C gestoßen und
da meinte man, dass es einfacher und sicherer von statten ginge....
Sercan S. schrieb:> Wo soll ich es denn sonst aufrufen, wenn nicht in ISR (TIMER0_OVF_vect)?
In deiner Hauptschleife die du dir vorstellst.
Deine ISR (Timer) hat uja mit dem SPI-Empfangen nichts zu tun.
Sercan S. schrieb:> Wo soll ich es denn sonst aufrufen, wenn nicht in ISR (TIMER0_OVF_vect)?
Du hast in deinem Code Timer0 mit Vorteiler 1 laufen lassen, also
mit 8MHz.
Probiere das mal, nur zum Testen:
Master
1
charsend='1';
2
volatileuint16_tincrement=0;
3
volatileuint8_tFlag=0;
4
5
voidSPI_MasterInit(void){
6
/* Set MOSI and SCK output, all others input */
7
DDRB=(1<<PB5)|(1<<PB3)|(1<<PB2);
8
PORTB|=(1<<PB2);
9
/* Enable SPI, Master, set clock rate fck/16 */
10
SPCR=(1<<SPE)|(1<<MSTR)|(1<<SPR0);
11
}
12
voidSPI_MasterTransmit(charcData){
13
/* Start transmission */
14
PORTB&=(1<<PB2);
15
SPDR=cData;
16
/* Wait for transmission complete */
17
while(!(SPSR&(1<<SPIF)))
18
PORTB|=(1<<PB2);
19
;}
20
21
intmain(void)
22
{
23
board_init();
24
// Timer 0 konfigurieren
25
// Bei dir war es mit Prescaler 1, also 8 MHz !!!
26
TCCR0=(1<<CS00)|(1<<CS02);// Start Timer 0 with prescaler 1024
Fang doch mal ganz einfach an:
- Master SS offen lassen und auf Output setzen
- Slave SS fix auf GND
- MOSI-MOSI, SCK-SCK und MISO-MISO verbinden und so auf Output setzen,
wie du es oben beschrieben hast (ich prüfe jetzt nicht, ob B5 und B3
wirklich MOSI und SCK sind)
- Initialisierung nochmal überprüfen
- Taktrate so niedrig wie möglich (damit vielleicht du eine Chance hast,
mit einer LED etwas zu sehen)
Wenn du jetzt auf dem Master folgendes ausführst...
1
while(true)
2
{
3
SPDR = 0xaa; // Daten per SPI rausschicken
4
while (! (SPSR & (1<<SPIF)) ) // solange Daten nicht vollständig verschickt...
5
; // nichts tun
6
}
...müsste permanent Gezappel an allen drei Pins zu beobachten sein (0xaa
ist 0b10101010, also bei jedem Takt ein Flankenwechsel, alternativ
kannst du auch 0xf0 = 0b11110000 probieren, dann sind die Flankenwechsel
auf MOSI/MISO deutlich langsamer als auf SCK). Solange muss der Slave
noch gar nichts tun...
Wenn das klappt, kannst du 0xaa mit 0x00 und 0xff ersetzen, schauen, ob
MOSI und MISO jetzt dauerhaft low bzw. high bleiben
Dann packst du statt dieser Spieldaten mal etwas sinnvolles (1 oder '1')
in die Daten und packst in den Slave sowas wie...
1
while(true)
2
{
3
if (SPSR & (1<<SPIF)) // ist ein Byte angekommen?
4
{
5
unsigned char data = SPDR; // Byte aus dem SPI-Register holen
6
output (data); // irgendwie ausgeben
7
}
8
}
Jetzt wirst du feststellen, dass nicht die Daten ankommen, die du
erwartest. Das liegt daran, dass der Slave nicht weiß, wo das Datenbyte
beginnt. Und genau dafür kannst du den SS-Pin nutzen, jetzt also wieder
Master und Slave SS-Pins verbinden und in Pseudocode:
Master: Setze SS auf low
Master: Warte, bis sicher ist, dass das Signal beim Slave angekommen ist
(zum Testen gerne mal 1s probieren, dann kannst du das mit deiner LED
nachverfolgen, später wirst du vermutlich ein paar µs brauchen)
Master: Sende Daten (SPDR = '1'; while ( !(SPSR & (1<<SPIF)) ) ;)
Master: Warte, bis sicher ist, dass das letzte Bit beim Slave angekommen
ist
Master: Setze SS auf high
Am Slave-Code änderst du nichts.
MfG, Arno
P.S: Einen Bug sehe ich in deiner SPI_MasterTransmit...
1
while(!(SPSR & (1<<SPIF)))
2
PORTB |= (1<<PB2);
...damit setzt du SS wieder auf High, bevor das Byte durchgetaktet
wurde. Du willst vermutlich:
Marc V. schrieb:> Sercan S. schrieb:>> Wo soll ich es denn sonst aufrufen, wenn nicht in ISR (TIMER0_OVF_vect)?>> Du hast in deinem Code Timer0 mit Vorteiler 1 laufen lassen, also> mit 8MHz.>> Probiere das mal, nur zum Testen:>..........
Also ich bin jetzt heute erst dazugekommen und habe diese Testweise
raufgespielt und diese wie von mir beschrieben angeschlossen und muss
feststellen, dass es irgendwie nicht klappt. Irgendwas muss da ja falsch
sein, die LED schaltet sich einfach nicht an. LED habe ich getestet und
ohne den
1
send=SPI_SlaveReceive();
geht die LED auch an (toggelt). Also liegt es wirklich an der
Übertragung...
Dann gibt es drei Möglichkeiten:
1) Der Slave bekommt keine oder zu schnelle SCK-Signale
2) Der Slave bekommt kein SS-Signal bzw. eins mit falschem Timing (SS
muss auf low bleiben, bis acht SCK-Pulse durch sind, sonst erkennt der
Slave nie ein vollständig übertragenes Byte)
3) An der Slave-Initialisierung stimmt irgendwas nicht
Hast du Marcs Code in SPI_MasterTransmit schon um die beiden Fehler (die
er von dir kopiert hat) korrigiert, die Martin und ich beschrieben
haben? Die sorgen nämlich für gar kein ("Martins" Bug) bzw. ein viel zu
kurzes ("mein" Bug) SS-Signal, also Punkt 2 oben auf der Liste, damit
kann es also auch gar nicht gehen.
Alternativ nimm die SS-Verbindung weg und leg den Slave-SS fest auf
Masse, dann hast du Fehlerquelle 2 schonmal ausgeschlossen. Wenn es dann
immer noch nicht geht, nimm auch die SCK-Verbindung weg und halte einen
Draht von Slave-SCK abwechselnd an GND und VCC bis die LED toggelt. Ja,
SPI ist so einfach und kann beliebig langsam laufen.
MfG, Arno
Ich muss mal euch nun ein Update geben...
Also ausprobiert habe ich o.g. Code wie folgt angeschlossen:
1
SCK <-> SCK
2
MISO <-> MISO
3
MOSI <-> MOSI
4
SS <-> SS
Klappt nicht.
Wenn ich es allerdings so anschließe, wie im Atmel Datenblatt
beschrieben:
1
SCK <-> SCK
2
MISO <-> MISO
3
MOSI <-> MOSI
4
5
MASTER_SS <-> VCC
6
SLAVE_SS <-> GND
Klappt! WARUM? :O
Ich muss allerdings nachprüfen, ob auch das richtige ankommt, aber
irgendetwas kommt schonmal an sonst würde es doch nicht toggeln...
EDIT: Es kommt auch das richtige an! Also es fängt von 49 an und springt
bei nach 57 auf 48 und geht wieder von vorne los. Ich habe Testweise
beide SS_PINS freigelassen und es klappt auch ohne es in VCC/GND zu
beschalten...
Arno schrieb:> Hast du Marcs Code in SPI_MasterTransmit schon um die beiden Fehler (die> er von dir kopiert hat) korrigiert, die Martin und ich beschrieben> haben? Die sorgen nämlich für gar kein ("Martins" Bug) bzw. ein viel zu> kurzes ("mein" Bug) SS-Signal, also Punkt 2 oben auf der Liste, damit> kann es also auch gar nicht gehen.
MfG, Arno
Arno schrieb:> Master und Slave SS-Pins verbinden und in Pseudocode:>> Master: Setze SS auf low> Master: Warte, bis sicher ist, dass das Signal beim Slave angekommen ist> (zum Testen gerne mal 1s probieren, dann kannst du das mit deiner LED> nachverfolgen, später wirst du vermutlich ein paar µs brauchen)> Master: Sende Daten (SPDR = '1'; while ( !(SPSR & (1<<SPIF)) ) ;)> Master: Warte, bis sicher ist, dass das letzte Bit beim Slave angekommen> ist> Master: Setze SS auf high
MfG, Arno
Martin B. schrieb:> und möglichst noch das>> PORTB &= (1<<PB2);>> in>> PORTB &= ~(1<<PB2);>> ändern.
So, nochmal in kleinen Häppchen zusammengesucht, warum es mit der
Verbindung der beiden SS-Pins nicht geht. Steht alles schon oben,
teilweise mehrfach - entweder du hast es nicht verstanden (das ist keine
Schande, dann frag bitte nach) oder du hast es nicht gelesen (dann hör
auf, herumzuprobieren, bis du alles gelesen hast, sonst ist es sehr
anstrengend, irgendwie sinnvoll auf deine Fragen zu antworten).
Danke und viele Grüße,
Arno
Ergänzung (vielleicht sollte ich mich doch mal registrieren, dass ich
meine Beiträge bearbeiten kann...):
- Es ist gefährlich, wenn du den Master-SS-Pin auf VCC legst und ihn
gleichzeitig als Ausgang definierst. Damit kannst du dir ganz schnell
den Ausgang kaputtmachen, oder sogar den ganzen Controller, wenn du ihn
aus Versehen umschaltest.
- Es ist Zufall, dass es mit offenem Slave-SS-Pin funktioniert. Je
nachdem, was gerade an elektromagnetischem Dreck in der Luft ist, kann
der Slave auch Highlevel am SS-Pin sehen, und dann funktioniert es nicht
mehr.
- Es ist Zufall, dass es ohne verbundene SS-Pins richtig funktioniert.
Es kommen immer Daten an, aber dass es die richtigen sind, hängt davon
ab, in welcher Reihenfolge du Master und Slave einschaltest, welcher
Controller wie schnell durch seine Initialisierung läuft... Wenn der
Master ein kleines bisschen schneller ist als der Slave, wird er schon
ein paar Bits auf die SPI gesendet haben, bevor der Slave bereit ist,
sie zu empfangen. Der Master schickt also zum Beispiel Bits 1-3, dann
empfängt der Slave Bit 4-8 und interpretiert das als Bits 1-5, dann
sendet der Master das nächste Byte, Bit 1-3 landen beim Slave als Bits
6-8, der Slave erkennt das erste vollständige Byte, setzt SPIF, empfängt
weiter Bits 4-8 vom zweiten Byte und speichert sie als Bits 1-5 in
seinem zweiten Byte. Der SS-Pin sorgt dafür, dass der Slave genau weiß,
wann ein Byte beginnt - wenn man ihn richtig beschaltet.
MfG, Arno
Arno schrieb:> - Es ist Zufall, dass es mit offenem Slave-SS-Pin funktioniert. Je> - Es ist Zufall, dass es ohne verbundene SS-Pins richtig funktioniert.
Kann mich dem nur anschliessen.
Sercan S. schrieb:> Klappt! WARUM? :O
Warum nicht ?
Dein SLAVE ist selektiert ( SLAVE_SS auf GND ), bei MASTER ist SS auf
HIGH - MASTER sendet, SLAVE empfängt - für die beiden ist ALLES in
bester Ordnung...
P.S.
Nur nicht jetzt zufällig MASTER_SS auf 0 setzen.
Hallo,
ja ich habe die Zeile verbessert :)
Wenn ich nun die SS am SS verbinde, wie gehe ich dabei vor?
Master zieht den PB2 (SS) auf LOW und nach der While-Schleife (nach der
Übertragung) wird es ja wieder auf HIGH gezogen.
Ich müsste ja logischerweise auf Seiten der Slave nun:
1
if(!(PINB&(1<<PB2))){
2
send=SPI_SlaveReceive();
3
}
Also Überprüfen ob PB2 auf LOW gezogen wurde und dann lesen oder?
Sercan S. schrieb:> Also Überprüfen ob PB2 auf LOW gezogen wurde und dann lesen oder?
Mann oh Mann, du hast schon noch überhaupt nichts kapiert.
SPISpezialist schrieb:> Du brauchst den SlaveSelect nicht abfragen, es reicht das SPI> Status Register zu fragen. Denn wenn SlaveSelect Low ist hast> du noch kein Byte empfangen. Das Status Register gibt zuverlässig> Auskunft darüber.
SPISpezialist schrieb:> Sercan S. schrieb:>> Also Überprüfen ob PB2 auf LOW gezogen wurde und dann lesen oder?>> Mann oh Mann, du hast schon noch überhaupt nichts kapiert.>> SPISpezialist schrieb:>> Du brauchst den SlaveSelect nicht abfragen, es reicht das SPI>> Status Register zu fragen. Denn wenn SlaveSelect Low ist hast>> du noch kein Byte empfangen. Das Status Register gibt zuverlässig>> Auskunft darüber.
Ich habe es schon verstanden, dass der Status Register darüber
zuverlässig Informationen darüber gibt.
Ich möchte ja in meinem Programm auch noch andere Sachen machen und da
dachte ich mir, da ich sowieso den SS auf LOW und anschließend auf HIGH
ziehe, kann ich ja dann SPDR abholen. Weil ich sende ja nicht ständig
irgendwelche Daten rüber, aber falls mal was da ist möchte ich das
mitbekommen und da schien es mir logisch einfach den SS-LOW-HIGH zu
prüfen um dann die angekommene Daten abzulesen.
EDIT: Da ich den letzten SPDR speichere könnte ich ja auch einfach
vergleichen ob die jetzige Daten in SPDR verschieden sind, dann könnte
ich somit auch Bescheid wissen, dass da was neues ist. Ich möchte mit
der While-Schleife halt nicht warten, bis es verändert wurden ist, somit
stoppe ich ja mein ganzen Programmlauf.
Sercan S. schrieb:> Weil ich sende ja nicht ständig> irgendwelche Daten rüber, aber falls mal was da ist möchte ich das> mitbekommen und da schien es mir logisch einfach den SS-LOW-HIGH zu> prüfen um dann die angekommene Daten abzulesen.
Auf dem Slave?
Wenn man da nicht mit Interrupts oder DMA arbeitet, wird es mit SPI nix.
Bei SPI möchte man den Takt am Master normalerweise nicht so weit runter
drehen - weil der ja auch vermutlich auch noch was anderes zu tun hat
als auf den Slave zu warten.
Bessere Lösung: SPI Interrupt + FIFOs für Transmit und Receive. Atmega8
hat ja leider kein DMA.
Die Hauptschleife muss sich dann nur noch um die FIFOs kümmern.
Sercan S. schrieb:> ja ich habe die Zeile verbessert :)
Beide Zeilen? Martins und meine?
> Wenn ich nun die SS am SS verbinde, wie gehe ich dabei vor?> Master zieht den PB2 (SS) auf LOW und nach der While-Schleife (nach der> Übertragung) wird es ja wieder auf HIGH gezogen.
Genau (aber nur, wenn du die while-Schleife korrigiert hast, die macht
nämlich im Moment noch was anderes, wie ich oben schon geschrieben
habe). Ich würde da noch jeweils ein paar µs delay einbauen, um sicher
zu gehen, dass dir unterschiedliche Signallaufzeiten nicht den Plan
kaputt machen, aber es müsste auch ohne funktionieren.
> Ich müsste ja logischerweise auf Seiten der Slave nun:>
1
>if(!(PINB&(1<<PB2))){
2
>send=SPI_SlaveReceive();
3
>}
4
>
>> Also Überprüfen ob PB2 auf LOW gezogen wurde und dann lesen oder?
Nein. Das macht die SPI-Hardware im Slave von ganz alleine. Du musst nur
noch prüfen, ob die SPI ein komplettes Byte empfangen hat:
[c]
if(SPSR & (1<<SPIF))
{
unsigned char data = SPDR;
//mach irgendwas mit data
}
[c]
Sercan S. schrieb:> Ich habe es schon verstanden, dass der Status Register darüber> zuverlässig Informationen darüber gibt.
Dann nutz es doch. Wenn du oft genug SPIF abfragst, bekommst du das mit,
noch sicherer, wenn du einen SPI-Interrupt nutzt. Besser als den SS-Pin
abzufragen, denn ein SPI-Master kann auch direkt ein zweites Byte
hinterher schicken, ohne SS zwischendurch auf High zu ziehen. Und selbst
bei deinem Master kann SS sehr sehr kurz auf High und gleich wieder auf
Low gehen.
> Ich möchte ja in meinem Programm auch noch andere Sachen machen und da> dachte ich mir, da ich sowieso den SS auf LOW und anschließend auf HIGH> ziehe,
SS fasst du auf dem Slave mit deinem Code doch nach der Initialisierung
gar nicht an?
> Ich möchte mit> der While-Schleife halt nicht warten, bis es verändert wurden ist, somit> stoppe ich ja mein ganzen Programmlauf.
Deswegen solltest du keine while-Schleife verwenden, sondern eine
if-Abfrage, die wird übersprungen, wenn das SPIF-Bit noch nicht gesetzt
ist. Muss nur oft genug aufgerufen werden, damit der Master nicht schon
ein komplettes zweites Byte geschickt hat. Während der Master das zweite
Byte sendet, ist OK, solange hat SPDR noch den alten Wert, aber sobald
das zweite Byte komplett ist, wird SPDR auf den neuen Wert aktualisiert.
MfG, Arno