Forum: Mikrocontroller und Digitale Elektronik SPI Daten Empfangen


von Remo S. (Gast)


Lesenswert?

Hallo zusammen,

Kurze Frage was SPI Empfangen angeht. Das Programm funktioniert bis auf 
das Empfangen einwandfrei. Der Mikrocontroller bleibt hängen sobald der 
Befehl Master Receive ausgeführt wird. Kann mir jemand erklähren wiso 
und was ev. das Problem sein könnte, das ich keine Daten empfangen kann. 
Der Code für Emfangen und Senden sind fast identisch und aus meiner 
Sicht aus auch korrekt.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define F_CPU 16000000UL
5
#define DD_MISO    PB3
6
#define DD_MOSI    PB2
7
#define DD_SCK     PB1
8
#define DD_SS      PB0
9
#define DDR_SPI    DDRB
10
11
void SPI_MasterInit(void)
12
{
13
  // Set MOSI and SCK output, all others input
14
  DDR_SPI |= (1<<DD_MOSI)| (1<<DD_MISO)|(1<<DD_SCK) | (1<<DD_SS);
15
  // Enable SPI, Master, set clock rate fck/16
16
  SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0)| (1 << CPHA); //| (1 << CPHA) //CPOL  //SPHA Phasenverschiebung CPOL invertieren
17
}
18
uint8_t SPI_MasterTransmit(char Data)
19
{
20
  SPDR = Data;
21
  while(!(SPSR & (1<<SPIF)));
22
  return(SPDR);
23
}
24
uint8_t SPI_MasterReceive(void)
25
{
26
  while(!(SPSR & (1<<SPIF)));
27
  return SPDR;
28
}
29
//****************************************************************************
30
int main(void)
31
{
32
  PORTB |=(1<<DD_SS);          
33
  SPI_MasterInit();        
34
  unsigned char MAC1;
35
36
    while(1)
37
    {
38
      PORTB &=~(1<<DD_SS);
39
       SPI_MasterTransmit(0x11);
40
       SPI_MasterTransmit(0xFA);
41
       MAC1 = SPI_MasterReceive();
42
      PORTB |=(1<<DD_SS);
43
  }    
44
}

von Pandur S. (jetztnicht)


Lesenswert?

Wie SPI funktioniert hast du aber begriffen ?

von Remo S. (Gast)


Lesenswert?

Joggel E. schrieb:
> Wie SPI funktioniert hast du aber begriffen ?

Ja, grundsätzlich schon wiso?

von Peter (Gast)


Lesenswert?

Du musst auch zum Empfangen etwas senden. Sende einfach 0x00. Wenn du 
ein Bit übergibst, erhältst Du auch einen.

von doz (Gast)


Lesenswert?

Meine Glaskugel sagt mir, dass du wahrscheinlich mit irgendeinem IC 
kommunizieren möchtest und dabei die Daten dieses Bausteins aus einem 
seiner Register lesen möchtest.

Remo S. schrieb:
> SPI_MasterTransmit(0x11);
> SPI_MasterTransmit(0xFA);

.. sind dann wohl die entsprechenden Register des ICs. Ob die gesendeten 
Bytes so sinnvoll sind. (richtige Register und read-Bit richtig 
gesetzt..) kann dir keiner sagen ohne deinen Slave zu kennen.

Peter schrieb:
> Du musst auch zum Empfangen etwas senden. Sende einfach 0x00. Wenn du
> ein Bit übergibst, erhältst Du auch einen.

Dein Mikrocontroller erzeugt als Master das Clock Signal auf dem SPI 
Bus. Das wird aber nur "erzeugt" so lange der Master etwas sendet. Um 
dem IC die Möglichkeit zu geben mit seinem Register Inhalt zu antworten 
musst du jetzt N -dummy Bytes senden, wobei N die Anzahl der erwarteten 
Bytes des Slave ICs sind.
Wie Peter geschrieben hat einfach ein 0xFF oder 0x00 hinterher und dann 
den Inhalt aus dem Empfangregister holen.

Wenn du uns aber noch sagst mit welchem Slave Device zu kommunizieren 
möchtest, kann man dir bestimmt auch weiter helfen.

von Remo S. (Gast)


Lesenswert?

> Dein Mikrocontroller erzeugt als Master das Clock Signal auf dem SPI
> Bus. Das wird aber nur "erzeugt" so lange der Master etwas sendet. Um
> dem IC die Möglichkeit zu geben mit seinem Register Inhalt zu antworten
> musst du jetzt N -dummy Bytes senden, wobei N die Anzahl der erwarteten
> Bytes des Slave ICs sind.
> Wie Peter geschrieben hat einfach ein 0xFF oder 0x00 hinterher und dann
> den Inhalt aus dem Empfangregister holen.
>
> Wenn du uns aber noch sagst mit welchem Slave Device zu kommunizieren
> möchtest, kann man dir bestimmt auch weiter helfen.

Oke das hab ich jetzt soweit begriffen, danke nochmals für die 
Erklährung. Das Ic ist ein EEPROM mit integrierter MAC Adresse 
IC:(25AA02E48) Ich versuche gerade aus den Registern die MAC Adresse zu 
lesen. Dafür wird im Datenblatt 0x11 für lesen und dan 0xFA für das 
Entsprechende Register gesendet. Ab da kann die 6byte MAC Adresse 
ausgelesen werden. Jetzt suche ich verzweifelt nach dem Fehler im Code.

von S. Landolt (Gast)


Lesenswert?

> DDR_SPI |= (1<<DD_MOSI)| (1<<DD_MISO)|(1<<DD_SCK) | (1<<DD_SS);

Im MasterInit MISO als Ausgang?

von Cyblord -. (cyblord)


Lesenswert?

S. Landolt schrieb:
>> DDR_SPI |= (1<<DD_MOSI)| (1<<DD_MISO)|(1<<DD_SCK) | (1<<DD_SS);
>
> Im MasterInit MISO als Ausgang?

Aber er hat doch SPI kapiert. Kannst du nicht lesen?

von doz (Gast)


Lesenswert?

Remo S. schrieb:
> Das Ic ist ein EEPROM mit integrierter MAC Adresse
> IC:(25AA02E48)

Alles klar jetzt wird das Bild doch schon langsam klarer.
Die Info, die du von EEPROM lesen möchtest ist 6 Byte lang und steht an 
den Adressen 0xFA bis 0xFF. Also musst du schon mal mehr als nur ein 
Byte lesen.

"The 6-byte EUI-48™ node address value of the
25AA02E48 is stored in array locations 0xFA through 0xFF[..]" - Seite 13 
im Db.

Deine Idee, erst eine Read Instruction zu senden und dann die Adresse 
hinterher, war auch richtig. Nur musst du als Read Instruction das Byte 
0b0000 0011 senden. In Hex also 0x03 und nicht 0x11.
Danach kommt dann das Adress-Byte also 0xFA. Dann musst du nur noch 
abwechselnd ein Dummy Byte senden und das Byte empfangen.

Falls du ein Oszi oder einen logic analyzer hast, häng das mit auf 
deinen SPI Bus um alle anderen Fehler auszuschließen.
Bin leider mit den AVR Controllern nicht so bewandert und kann deswegen 
auf die Schnelle einen Fehler in deiner Empfangs und Sende Routine 
erkennen.

Remo S. schrieb:
> Das Programm funktioniert bis auf
> das Empfangen einwandfrei.

.. aber wenn du das sagst wird es schon stimmen..

von doz (Gast)


Lesenswert?

S. Landolt schrieb:
>> DDR_SPI |= (1<<DD_MOSI)| (1<<DD_MISO)|(1<<DD_SCK) | (1<<DD_SS);
>
> Im MasterInit MISO als Ausgang?

gut, MISO als Ausgang zu setzten ist natürlich Banane. Bring also erst 
mal dein SPI so weit zum laufen, dass du einzelne Bytes senden und 
empfangen kannst und dann lies dir nochmal meinen Beitrag zum richtigen 
Protokoll durch. Eine Baustelle nach der anderen.

von Remo S. (Gast)


Angehängte Dateien:

Lesenswert?

> Deine Idee, erst eine Read Instruction zu senden und dann die Adresse
> hinterher, war auch richtig. Nur musst du als Read Instruction das Byte
> 0b0000 0011 senden. In Hex also 0x03 und nicht 0x11.
> Danach kommt dann das Adress-Byte also 0xFA. Dann musst du nur noch
> abwechselnd ein Dummy Byte senden und das Byte empfangen.
> Falls du ein Oszi oder einen logic analyzer hast, häng das mit auf
> deinen SPI Bus um alle anderen Fehler auszuschließen.
> Bin leider mit den AVR Controllern nicht so bewandert und kann deswegen
> auf die Schnelle einen Fehler in deiner Empfangs und Sende Routine
> erkennen.

Ja hab ein logic analyzter der mir dabei hilft. Soweit sieht es ja so 
aus wie im Datenblatt jedoch kommt von dem Bautein keine Antwort. Ich 
hatte zuerst noch micht alle 6byte ausgelesen da ich mit dem ersten 
zuerst das ganze testete.

von Remo S. (Gast)


Lesenswert?

doz schrieb:
> S. Landolt schrieb:
>>> DDR_SPI |= (1<<DD_MOSI)| (1<<DD_MISO)|(1<<DD_SCK) | (1<<DD_SS);
>>
>> Im MasterInit MISO als Ausgang?
>
> gut, MISO als Ausgang zu setzten ist natürlich Banane. Bring also erst
> mal dein SPI so weit zum laufen, dass du einzelne Bytes senden und
> empfangen kannst und dann lies dir nochmal meinen Beitrag zum richtigen
> Protokoll durch. Eine Baustelle nach der anderen.

Das wurde schon korrigiert Danke dafür.

von M. K. (sylaina)


Lesenswert?

Remo S. schrieb:
> Das Programm funktioniert bis auf
> das Empfangen einwandfrei.

Wenn du dein EEPROM nicht lesen kannst weil das Empfangen auf SPI nicht 
funktioniert, woher weißt du dann, dass das Schreiben auf das EEPROM, 
also das Senden auf SPI, funktioniert? Nur weils Pegelwechsel auf der 
Leitung gibt muss das noch lange nicht heißen, dass das Senden 
funktioniert.

Der Rest wurde ja schon gesagt: Für jedes Byte, dass du auf dem SPI 
lesen willst, musst du auch ein Byte senden. Das haben solche 
Schieberegister so ansich.

von Gustl B. (-gb-)


Lesenswert?

Im Datenblatt auf den Seiten 7 und 8 ist doch klar das Timing gezeigt. 
Und das solltest du einhalten.

Zuerst geht Chipselect runter, mit den folgenden 8 Takten von SCLK wird 
die "Instruction" zum IC übertragen (MOSI), darauf folgen weitere 8 
Takte von SCLK mit denen die Adresse zum IC übertragen wird (auch MOSI) 
und danach kommen die Daten (Bytes).

Du willst 6 Byte Daten lesen, und muss vorher Instruction + Adresse 
senden, macht zusammen 8 Bytes oder 64 Takte von SCLK.

Du ziehst also Chip select runter, gibst danach 64 Takte auf SCLK aus 
und nimmst Chip select wieder hoch.
Während der ersten 8 Takte von SCLK gibst du Instruction, wärend der 
nächsten 8 Takte die Adresse aus. Während der weiteren 6*8 Takte von 
SCLK kannst du beliebiges ausgeben. Wichtig ist, dass du dann MISO 
einliest. Da kommen nämlich die Daten.

M. K. schrieb:
> Für jedes Byte, dass du auf dem SPI
> lesen willst, musst du auch ein Byte senden. Das haben solche
> Schieberegister so ansich.

Das stimmt so allgemein nicht. Es gibt auch ICs, denen genügt es wenn 
man Chipselect runterzeiht und die entsprechende anzahl an Takten über 
SCLK ausgibt und dann Chipselect wieder hoch nimmt. Die brauchen also 
kein MOSI. Beispiel sind ADCs wie der LTC2325.

: Bearbeitet durch User
von Remo S. (Gast)


Lesenswert?

> Du ziehst also Chip select runter, gibst danach 64 Takte auf SCLK aus
> und nimmst Chip select wieder hoch.
> Während der ersten 8 Takte von SCLK gibst du Instruction, wärend der
> nächsten 8 Takte die Adresse aus. Während der weiteren 6*8 Takte von
> SCLK kannst du beliebiges ausgeben. Wichtig ist, dass du dann MISO
> einliest. Da kommen nämlich die Daten.

Also für mich sieht das auf dem Bild genau danach aus nicht?
Bild siehe weiter oben (Analyzer)

von Remo S. (Gast)


Lesenswert?

> Wenn du dein EEPROM nicht lesen kannst weil das Empfangen auf SPI nicht
> funktioniert, woher weißt du dann, dass das Schreiben auf das EEPROM,
> also das Senden auf SPI, funktioniert? Nur weils Pegelwechsel auf der
> Leitung gibt muss das noch lange nicht heißen, dass das Senden
> funktioniert.

Das weiss ich daher, dass ich noch ein anderer Baustein anspreche der 
sauber funktioniert, allerdings muss ich bei dem nur schreiben nicht 
lesen. Aber schreiben funktioniert also sonst würde der andere Baustein 
auch nicht gehen.

von Peter (Gast)


Lesenswert?

Das Bild sieht gut aus. Was ist mit dem CS?

von doz (Gast)


Lesenswert?

Remo S. schrieb:
> SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0)| (1 << CPHA);

stell mal deine CPHA und CPOL auf 0, also SPI Mode 0.

von Remo S. (Gast)


Lesenswert?

Peter schrieb:
> Das Bild sieht gut aus. Was ist mit dem CS?

Der ist Kanal 3 auf dem Bild

von Peter (Gast)


Lesenswert?

Hm, dann könnte es einfach sein, dass deine Signale beim EEPROM nicht 
ankommen, oder er keine Spannung hat. Einfach Mal das prüfen.

von Gustl B. (-gb-)


Lesenswert?

Remo S. schrieb:
> Also für mich sieht das auf dem Bild genau danach aus nicht?
> Bild siehe weiter oben (Analyzer)

Entschuldigung, das hatte ich übersehen. Ja, sieht gut aus. Aber ... 
vielleicht funktioniert das ja auch, aber du bekommst als Antwort eben 
die Bytes mit dem Wert x"00".

Du musst also vorher auch etwas schreiben um etwas von Null 
verschiedenes lesen zu können.

Und da hat der IC einen Schreibschutz. Das Write Enable Latch.
http://ww1.microchip.com/downloads/en/DeviceDoc/20002123F.pdf
Auf Seite 9.

: Bearbeitet durch User
von Remo S. (Gast)


Lesenswert?

doz schrieb:
> Remo S. schrieb:
>> SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0)| (1 << CPHA);
>
> stell mal deine CPHA und CPOL auf 0, also SPI Mode 0.

CPHA auf 0 das war die Lösung. DANKE Jedoch ist das einwenig sehr 
umstädlich den der eine Baustein braucht CPHA 1 und jezt das EEPROM 0.

von Zod M. (do0zy)


Lesenswert?

Remo S. schrieb:
> CPHA auf 0 das war die Lösung. DANKE Jedoch ist das einwenig sehr
> umstädlich den der eine Baustein braucht CPHA 1 und jezt das EEPROM 0.

Sorry, war vorhin nicht eingeloggt und habe als Gast(doz) geschrieben.

Dass der EEPROM SPI Mode 0 verlangt, habe ich dem Timing Diagramm aus 
dem Datenblatt entnommen.
Bei den meisten ICs steht aber auch konkret drin welche Modes 
unterstützt werden. Schau also einfach mal bei deinem anderen IC im 
Datenblatt ob er vielleicht auch mit Mode 0 läuft.

Gustl B. schrieb:
> Du musst also vorher auch etwas schreiben um etwas von Null
> verschiedenes lesen zu können.
>
> Und da hat der IC einen Schreibschutz. Das Write Enable Latch.
> http://ww1.microchip.com/downloads/en/DeviceDoc/20002123F.pdf
> Auf Seite 9.

was du geschrieben hast stimmt zwar, ist ein dem Fall aber irrelevant, 
da er ja die MAC auf dem write Protected Bereich lesen möchte. Da hilft 
auch kein Latch enable und selbst wenn ist 0x03 immernoch eine read und 
keine write Instruction.

: Bearbeitet durch User
von Remo S. (Gast)


Lesenswert?

> Dass der EEPROM SPI Mode 0 verlangt, habe ich dem Timing Diagramm aus
> dem Datenblatt entnommen.
> Bei den meisten ICs steht aber auch konkret drin welche Modes
> unterstützt werden. Schau also einfach mal bei deinem anderen IC im
> Datenblatt ob er vielleicht auch mit Mode 0 läuft.

Also ja das EEPRom benötigt CPHA 1 und der andere Baustein CPHA 0. Das 
konnte ich auch so herauslesen Danke. Der andere Baustein versteht CPHA 
1 gar nicht also ausgeschlossen. Ich versuchte jetzt gerade CPHA wärend 
den jeweiligen Routinen zu ändern dies funktioniert leider nicht auch 
wen ich SPI deaktiviere und wieder jedes mal erneut aktiviere. Was macht 
man in solch einem Fall?

von S. Landolt (Gast)


Lesenswert?

Wird da beim "erneut aktivieren" etwa wieder ver-odert?

>   SPCR |= ...

von Remo S. (Gast)


Lesenswert?

Ja exact geleich eigentlich wie beim initialisieren.
SPCR |= (1 << CPHA);
SPCR |= (0 << CPHA);

von S. Landolt (Gast)


Lesenswert?

Dacht' ich doch - was ist also das Resultat einer Oder-Operation?

von S. Landol (Gast)


Lesenswert?

Oder anders gesagt: generell SPCR komplett neu setzen mit SPCR = ...

Und
> SPCR |= (0 << CPHA);
ist ohnehin wirkungslos.

von Remo S. (Gast)


Lesenswert?

S. Landolt schrieb:
> Dacht' ich doch - was ist also das Resultat einer Oder-Operation?

Stimmt wäre ja dan das gleiche.
Müsste ich in demfalle dies so schreiben.
SPCR &= ~(1<<CPHA);
SPCR &= ~(0<<CPHA);

von Cyblord -. (cyblord)


Lesenswert?

Remo S. schrieb:
> S. Landolt schrieb:
>> Dacht' ich doch - was ist also das Resultat einer Oder-Operation?
>
> Stimmt wäre ja dan das gleiche.
> Müsste ich in demfalle dies so schreiben.
> SPCR &= ~(1<<CPHA);
> SPCR &= ~(0<<CPHA);

Lass doch mal das Nullen rumschieben. Es ist wirkungslos und 
dokumentiert nur einen mittleren bis großen Hirnschaden beim Autor.

von S. Landolt (Gast)


Lesenswert?

Nee, wenn schon kein Neusetzen, dann

SPCR |= (1<<CPHA);
SPCR &= ~(1<<CPHA);

von Remo S. (Gast)


Lesenswert?

S. Landolt schrieb:
> Nee, wenn schon kein Neusetzen, dann
>
> SPCR |= (1<<CPHA);
> SPCR &= ~(1<<CPHA);

Danke!

von TippsUndTricks (Gast)


Lesenswert?

Remo S. schrieb:
> umstädlich den der eine Baustein braucht CPHA 1 und jezt das EEPROM 0.

Auf PIC32 habe ich sowas gemacht:
extern void spi_reconfigure(unsigned char user, unsigned char SMP, 
unsigned char CKP, unsigned char CKE, unsigned int BRG);
und
extern uint8 spi_lock(unsigned char user);
extern uint8 spi_unlock(unsigned char user);

wenn der user den SPI haben möchte, dann erstmal locken. Dann 
umkonfigurieren. Dann CS ziehen, Daten schicken empfangen, dann CS 
loslassen und unlock.

Der lock leifert ja zurück ob man glück gehabt hat (SPI war frei). 
Könnte sein das da jemand schon etwas überträgt und dafür länger 
braucht, sprich acyncrone übertragungen. Das heisst dass der User auch 
hier asychron arbeiten muss. Sonst passt das nicht zusammen. Tcha leider 
sind die Resourcen knapp und man muss die teilen :) Reconfigure braucht 
man immer, weil man ja nicht weiss wer da vorhin etwas gemacht hat.

Es geht ja auch ohne lock, dann kann man aber nicht über längere Zeit 
den SPI blockieren. Zum beispiel aus einer Background task daten zu 
übertragen über n*10ms cyklen.

von M. K. (sylaina)


Lesenswert?

Gustl B. schrieb:
> Das stimmt so allgemein nicht. Es gibt auch ICs, denen genügt es wenn
> man Chipselect runterzeiht und die entsprechende anzahl an Takten über
> SCLK ausgibt und dann Chipselect wieder hoch nimmt. Die brauchen also
> kein MOSI. Beispiel sind ADCs wie der LTC2325.

War auch nicht allgemein gemeint sondern für AVRs. Und die geben den 
SPI-Clock nur aus wenn sie was "Senden" sollen.

Zitat aus dem Datenblatt eines AVRs:

>> Writing a byte to the SPI data register starts the SPI clock generator,
>> and the hardware shifts the eight bits into the Slave. After shifting
>> one byte, the SPI clock generator stops.

Und hier wird ja ein AVR verwendet ;)

von Rudolph R. (rudolph)


Lesenswert?

M. K. schrieb:
> War auch nicht allgemein gemeint sondern für AVRs. Und die geben den
> SPI-Clock nur aus wenn sie was "Senden" sollen.

Welcher Controller macht denn das nicht so das beim Beschreiben des 
Daten-Registers ein Transfer ausgelöst wird?

von Peter D. (peda)


Lesenswert?

Remo S. schrieb:
> Jedoch ist das einwenig sehr
> umstädlich den der eine Baustein braucht CPHA 1 und jezt das EEPROM 0.

Was ist daran umständlich? Du setzt einfach das SPCR vor dem jeweiligen 
/CS = 0 neu. Das sind nur 2 CPU Instruktionen, da muß man nicht geizig 
sein.

von Peter D. (peda)


Lesenswert?

TippsUndTricks schrieb:
> Auf PIC32 habe ich sowas gemacht:
> extern void spi_reconfigure(unsigned char user, unsigned char SMP,
> unsigned char CKP, unsigned char CKE, unsigned int BRG);
> und
> extern uint8 spi_lock(unsigned char user);
> extern uint8 spi_unlock(unsigned char user);

Ja, man kann natürlich für einfache Aufgaben auch einen Boliden nehmen, 
wo man alles super kompliziert machen muß.

von c-hater (Gast)


Lesenswert?

Peter D. schrieb:

> Ja, man kann natürlich für einfache Aufgaben auch einen Boliden nehmen,
> wo man alles super kompliziert machen muß.

Aber es gab dafür eine Wichsvorlage, also wird's genommen...

C&P-"Programmierer" halt, die können nix selbst, nichtmal Sachen im 
Umfang von zwei Maschinen-Instruktionen...

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


Lesenswert?

Gustl B. schrieb:
> Das stimmt so allgemein nicht. Es gibt auch ICs, denen genügt es wenn
> man Chipselect runterzeiht und die entsprechende anzahl an Takten über
> SCLK ausgibt und dann Chipselect wieder hoch nimmt. Die brauchen also
> kein MOSI. Beispiel sind ADCs wie der LTC2325.

 Ja, ja, sicher, wieder mal so eine Aussage...
 Ob man eine Null sendet oder 0xFF, ist in deinem Beispiel (LTC2325)
 vollkommen egal.
 Aber bei Hardware-SPI muss man als Master etwas senden um etwas
 zu empfangen. Clock wird nur beim MASTER erzeugt, es geht also schon
 mal nicht - CLOCK erzeugen, nix senden...

 Ob der Empfänger mit gesendeten Daten oder der Master mit
 empfangenen Daten etwas anfangen kann (und soll), ist eine ganz
 andere Sache.

: Bearbeitet durch User
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.