Forum: Mikrocontroller und Digitale Elektronik SPDR Register Funkmodul - Daten auslesen


von T.K. (Gast)


Lesenswert?

Servus ;)

Habe zu dem Thema zwar einige andere Beiträge gefunden, die haben mir 
allerdings relativ wenig weitergeholfen.

Ich benutze den ATmega1284P und möchte aus dem SPDR Register Daten eines 
Funkmoduls auslesen, glaube ich habe da einen Denkfehler in der 
Read-Routine, vielleicht kann mir ja jemand weiterhelfen.
Den Transmitter habe ich bereits in Betrieb genommen und der 
funktioniert einwandtfrei, also daran kann es nicht liegen.

Hier mal der Code-Abschnitt:
1
void rx_fifo(){
2
  uint8_t i;
3
  uint8_t a[64];
4
  uint8_t b;
5
  PORTB &= ~(1<<SCS);
6
  SPDR |= 0x45;
7
  while(!(SPSR & (1<<SPIF)));
8
   for(i=0; i<64; i++){
9
     a[i]=SPDR;
10
   }
11
  PORTB |= (1<<SCS);
12
  b=a[5];
13
  if(b==166){
14
    PORTD &= ~(1<<LEDGELB);
15
    PORTA |= (1<<LEDGRUEN);
16
  }
17
  else{
18
    PORTD |= (1<<LEDGELB);
19
    PORTA &= ~(1<<LEDGRUEN);
20
  }
21
}
Habe zusätzlich LED's eingebaut, um anzuzeigen, welcher Wert nun 
ankommt, da ich kein Display habe (hab einfach irgendeinen Wert 
verwendet, in diesem Fall A6 = 166). Jedoch erhalte ich immer 0 oder 
255...

Meine Funktion zum Auslesen von Registern funktioniert allerdings 
genauso wenig, also geh ich mal von nem Verständnisfehler meinerseits 
aus :'D
1
uint8_t Register_read(uint8_t addr){
2
  uint8_t tmp;
3
  PORTB &= ~(1<<SCS);
4
  SPDR |= (addr | 0x40);
5
  while(!(SPSR & (1<<SPIF)));
6
  tmp = SPDR;
7
  PORTB |= (1<<SCS);
8
  return tmp;
9
}
Vielen Dank schonmal im Vorraus ;)

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>   for(i=0; i<64; i++){
>     a[i]=SPDR;

So funktioniert das nicht. Der Takt zum Lesen muss vom Master erzeugt 
werden. Und das geschieht durch Senden eines Dummybytes.

MfG Spess

von T.K. (Gast)


Lesenswert?

spess53 schrieb:
> So funktioniert das nicht. Der Takt zum Lesen muss vom Master erzeugt
> werden. Und das geschieht durch Senden eines Dummybytes.

Hmmm, hab ich eigentlich schon ausprobiert, das wollte aber auch nicht 
so recht funktionieren :/ Kannst du mir evtl. mal ein Codebeispiel dafür 
geben?

danke :)

von Karl H. (kbuchegg)


Lesenswert?

T.K. schrieb:
> spess53 schrieb:
>> So funktioniert das nicht. Der Takt zum Lesen muss vom Master erzeugt
>> werden. Und das geschieht durch Senden eines Dummybytes.
>
> Hmmm, hab ich eigentlich schon ausprobiert, das wollte aber auch nicht
> so recht funktionieren :

Das mag schon sein, dass da noch mehr im Argen liegt.
Daaaas ist allerdings mit Sicherheit eines deiner Probleme.


> Kannst du mir evtl. mal ein Codebeispiel dafür
> geben?

Ist doch simpel.
Der Slave kann nur senden, wenn ihm der Master einen Takt generiert. 
Damit der Master den Takt generiert, muss er selber was senden.

Ergo
1
  for(i=0; i<64; i++) {
2
     SPDR = 0x00;   // wenn es nur darauf ankommt, dass der Slave 1 Byte loswerden kann
3
                    // ist es normalerweise wurscht, was der Master sendet
4
     while(!(SPSR & (1<<SPIF)))
5
       ; 
6
     a[i]=SPDR;
7
   }

Das größte MIssverständnis bei SPI schein es zu sein, dass es da sowas 
wie senden und empfangen getrennt gäbe. Tatsächlich ist es so, dass es 
eigentlich nur einen Byteaustausch gibt. Der Master übergibt 1 Byte an 
den Slave und zur gleichen Zeit übergibt der Slave 1 Byte an den Master. 
Die beiden Dinge sind unterennbar miteinander verbunden, wobei der 
Master zu jeder Zeit die Kontrolle hat. D.h. der Slave kann von sich aus 
überhaupt nichts tun, der Master muss alles anleiern.

Sinnigerweise steckt man den 'Baustein'
1
   SPDR = ....  1 Byte an den Slave übergeben
2
   while(!(SPSR & (1<<SPIF)))    warte, bis der Byteaustausch vollständig ist
3
     ; 
4
   mach was mit dem vom Slave erhaltenene SPDR;
in eine eigene Funktion, anstatt die jedesmal an allen Ecken und Enden 
wieder neu auszuprogrammieren.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

Da dir Assembler wahrscheinlich nichts nutzt, hier eine Routine aus 
einer Atmel Appnote:
1
char spi_transfer(volatile char data)
2
{
3
  SPDR = data;                    // Start the transmission
4
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
5
   {};
6
  return SPDR;                    // return the received byte
7
}

Zum Lesen ist der Inhalt von data irrelenant.

MfG Spess

von T.K. (Gast)


Lesenswert?

spess53 schrieb:
> Hi
>
> Da dir Assembler wahrscheinlich nichts nutzt, hier eine Routine aus
> einer Atmel Appnote:
> char spi_transfer(volatile char data)
> {
>   SPDR = data;                    // Start the transmission
>   while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
>    {};
>   return SPDR;                    // return the received byte
> }
>
> Zum Lesen ist der Inhalt von data irrelenant.
>
> MfG Spess

Ok danke ich werd damit mal etwas rumprobieren ;)
Da scheint wohl irgendwo anders noch ein Fehler zu sein, denn ich kann 
nichtmal richtig Register auslesen (bekomme im Debugger an betreffender 
Stelle immer 0 angezeigt), obwohl sie richtig beschrieben sein müssen, 
da es auf der Transmitter-Seite funktioniert :o

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
void rx_fifo(){
2
  uint8_t i;
3
  uint8_t a[64];
4
  uint8_t b;
5
  PORTB &= ~(1<<SCS);
6
  SPDR |= 0x45;
kommt mir allerdings schon sehr seltsam vor.
Wozu soll man da eine 0x45 in den unbekannten Inhalt von SPDR rein 
odern?

Auch das hier
1
uint8_t Register_read(uint8_t addr){
2
  uint8_t tmp;
3
  PORTB &= ~(1<<SCS);
4
  SPDR |= (addr | 0x40);
erregt mein Misstrauen, obwohl ich mir das noch eher vorstellen kann, 
als die 0x45 von oben. Immerhin wird da nur ein einziges Bit zusätzlich 
gesetzt.

von Karl H. (kbuchegg)


Lesenswert?

T.K. schrieb:

> Da scheint wohl irgendwo anders noch ein Fehler zu sein, denn ich kann
> nichtmal richtig Register auslesen (bekomme im Debugger an betreffender
> Stelle immer 0 angezeigt), obwohl sie richtig beschrieben sein müssen,
> da es auf der Transmitter-Seite funktioniert :o

Na ja.
überleg mal.
Wie soll denn der Slave den Registerinhalt zur Datenübertragung bereit 
stellen, wenn ee noch gar nicht weiß, welches Register du haben willst?

SPI ist ein Byteaustausch!

mit dem ersten Austausch, teilst du dem Slave mit, was du von ihm 
willst. Mit dem zweiten Austausch hat der Slave dann die Gelegenheit das 
gewünschte zu liefern.

Das hier
1
uint8_t Register_read(uint8_t addr){
2
  uint8_t tmp;
3
  PORTB &= ~(1<<SCS);
4
  SPDR |= (addr | 0x40);
5
  while(!(SPSR & (1<<SPIF)));
6
  tmp = SPDR;
7
  PORTB |= (1<<SCS);
8
  return tmp;
9
}
kann nicht funktionieren. Denn dazu müsste der Slave schon Hellseher 
sein um zu wissen, was er übertragen soll, noch ehe er vom Master die 
Anforderung vollständig 'in der Hand hat'.

SPI ist ein Austausch von Informationen.
So wie du und dein Kumpel, die ihr euch am Tisch gegenüber sitzt. Jeder 
hat einen Zettel in der rechten Hand auf den jeder was schreiben kann. 
Gleichzeitig streckt jeder die rechte Hand aus und schiebt den Zettel 
zum Gegenüber hinüber, den der dann mit der linken Hand nimmt. Ihr 
tauschr IMMER die Zettel aus. Es gibt nicht den Fall, dass du den Zettel 
rüberschiebst und dein Kumpel tut nichts.Es ist IMMER ein Austausch.
D.h. du kannst auf deinen Zettel dfraufschreiben: Gib mir deine 
Kontonummer.
Dann tauscht ihr die Zettel aus. Dein Kumpel kann jetzt deine 
Anforderung lesen und seinerseits die Info auf den Zettel schreiben, den 
er jetzt hat.
Damit die Information zu dir kommt, müsst ihr nochmal die Zettel 
austauschen.
Und wahrscheinlich ist es auch eine gute Idee, wenn du deinem Kumpel ein 
wenig Zeit gibst die angeorderte Information auch bereit zu stellen. 
Denn der braucht ja auch ein bischen Zeit, um die Kontonummer von der 
EC-Karte auf den Zettel abzumalen. Wenn du also sofort nach dem ersten 
Austausch den zweiten anleierst, dann wird der noch nicht fertig sein, 
und der Zettel den er dir im Austausch rüberschieben muss, wird noch 
leer sein, weil er ganz einfach in der Zeit nicht fertig geworden ist.

So funktioniert SPI.

: Bearbeitet durch User
von T.K. (Gast)


Lesenswert?

Karl Heinz schrieb:
> kommt mir allerdings schon sehr seltsam vor.
> Wozu soll man da eine 0x45 in den unbekannten Inhalt von SPDR rein
> odern?

0x40 ist der Befehl zum Auslesen und verodert mit 0x05, dem Register, 
indem die FIFO-Daten sind.

Karl Heinz schrieb:
> kann nicht funktionieren. Denn dazu müsste der Slave schon Hellseher
> sein um zu wissen, was er übertragen soll, noch ehe er vom Master die
> Anforderung vollständig 'in der Hand hat'.

Ja genau das versteh ich nicht. Eigentlich bekommt er doch den Befehl, 
dass er jetzt die Daten aus dem FIFO senden soll, also müsste ich sie 
doch im nächsten Zug über SPDR Stück für Stück auslesen können oder etwa 
nicht? :o

von T.K. (Gast)


Lesenswert?

Karl Heinz schrieb:
> So wie du und dein Kumpel, die ihr euch am Tisch gegenüber sitzt. Jeder
> hat einen Zettel in der rechten Hand auf den jeder was schreiben kann.
> Gleichzeitig streckt jeder die rechte Hand aus und schiebt den Zettel
> zum Gegenüber hinüber, den der dann mit der linken Hand nimmt. Ihr
> tauschr IMMER die Zettel aus. Es gibt nicht den Fall, dass du den Zettel
> rüberschiebst und dein Kumpel tut nichts.Es ist IMMER ein Austausch.
> D.h. du kannst auf deinen Zettel dfraufschreiben: Gib mir deine
> Kontonummer.
> Dann tauscht ihr die Zettel aus. Dein Kumpel kann jetzt deine
> Anforderung lesen und seinerseits die Info auf den Zettel schreiben, den
> er jetzt hat.
> Damit die Information zu dir kommt, müsst ihr nochmal die Zettel
> austauschen.
> Und wahrscheinlich ist es auch eine gute Idee, wenn du deinem Kumpel ein
> wenig Zeit gibst die angeorderte Information auch bereit zu stellen.

Die Erklärung ist TOP ;) aber was meinst du mit "Zeit geben"? reichen 
die while-Schleifen aus, oder muss beim Einlesen auch auf etwas gewartet 
werden?

von Karl H. (kbuchegg)


Lesenswert?

T.K. schrieb:
> Karl Heinz schrieb:
>> kommt mir allerdings schon sehr seltsam vor.
>> Wozu soll man da eine 0x45 in den unbekannten Inhalt von SPDR rein
>> odern?
>
> 0x40 ist der Befehl zum Auslesen und verodert mit 0x05, dem Register,
> indem die FIFO-Daten sind.

Schon. Und das ganze verodert mit dem momentanten INhalt von SPDR, den 
du nicht kennst.
Wozu verodern?
Wenn du 0x45 rüberschieben musst, dann doch ganz einfach
1
   SPDR = 0x45;

Ohen oder.

von Karl H. (kbuchegg)


Lesenswert?

T.K. schrieb:


> Die Erklärung ist TOP ;) aber was meinst du mit "Zeit geben"?

Na Zeit geben. Zwischen 'Ich will das und das von dir' und 'rück mal mit 
der Info rüber', muss dein Gegenüber Zeit haben, die Info zu besorgen.

> reichen
> die while-Schleifen aus

die while Schleife behandelt nur den Fall, dass das Hand ausstrecken und 
Zettel rüberschieben ja auch Zeit braucht. Mit
1
  SPDR = ....
beginnt das Ausstrecken der rechten Hand.
Von diesem Zeitpunkt an dauert es aber eine gewisse Zeit, bis du dann in 
der linken Hand den Zettel vom Gegenüber tatsächlich verfügbar hast.
Diese Zeit ist rein nur die Zeit, die der Datenaustausch an sich 
braucht.
1
uint8_t Register_read(uint8_t addr)
2
{
3
  uint8_t tmp;
4
5
  PORTB &= ~(1<<SCS);
6
7
  spi_transfer( addr | 0x40 );                  // Anforderung schicken
8
9
  _delay_us( ..... );   // hängt vom Slave und/oder vom konkreten 'Befehl'  ab
10
                        // die zeit muss ich dem Slave geben, damit der die gewünschte
11
                        // Information auch bereit stellen kann
12
13
  tmp = spi_transfer( 0x00 );                   // Ergebnis abholen
14
15
  PORTB |= (1<<SCS);
16
17
  return tmp;
18
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

T.K. schrieb:

> Ja genau das versteh ich nicht.

Da sich die Posts überschnitten haben:
Hast du es jetzt? Verstehst du, wo in deinem Code das Problem ist?

> doch im nächsten Zug über SPDR Stück für Stück auslesen können oder etwa
> nicht? :o

Schon.
Aber jedes 'Stück' beinhaltet, dass der Master seinerseits etwas 
rüberschiecken muss. Denn von sich aus kann der Slave nichts tun.

Oder beim Beispiel:
Dein Kumpel kann nicht von sich aus den rechten Arm ausstrecken und 
einen Zettel rüberschieben. Die Aktion geht IMMER von dir aus. Aber wenn 
du den Arm ausstreckst, dann muss das dein Kumpel auch tun. Er hat keine 
andere Wahl. Egal ob er den Zettel schon mit der angeforderten 
Information schon beschrieben hat oder nicht. Du hingegen hast rein vom 
Mechanismus her keine Möglichkeit festzustellen, ob er mit der 
Informationsbeschaffung schon fertig ist oder nicht. Du kannst ihm 
lediglich deiner Ansicht nach genügend Zeit geben (bzw. im Handbuch 
nachsehen, ob da was steht, wie lange du warten musst)

: Bearbeitet durch User
von T.K. (Gast)


Lesenswert?

Karl Heinz schrieb:
> Da sich die Posts überschnitten haben:
> Hast du es jetzt? Verstehst du, wo in deinem Code das Problem ist?

Ahhh ok. Ja glaube schon, das macht Sinn ;D
Habe nach wie vor ein Problem mit dem Auslesen der Register es kommt 
immernoch tmp=0, aber bin denk ich schon ein großes Stück vorwärts 
gekommen vor allem vom Verständnis her ;)

Danke :)

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.