www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATmega 644v und SPI: Problem mit 16-Bit


Autor: Andreas V. (sevenup)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich muss eine Kommunikation zwischen einem ATmega644
http://www.atmel.com/dyn/resources/prod_documents/...

und einem Sensor von Analog Devices, ADIS16201
http://www.analog.com/static/imported-files/Data_S...

durch Hardware-SPI des Mikrocontrollers herstellen.
Das Problem (für mich) ist dabei, dass der Controller 8-Bit SPI besitzt, 
und der Sensor 16-Bit Daten erwartet.

Folgendes habe ich ausprobiert:
// Definitionen
#define CS_ADIS16201_L()   PORTD  &=~ 0b01000000  // CS auf 0 (PortD PIN5)
#define CS_ADIS16201_H()   PORTD  |=  0b01000000  // CS auf 1 (PortD PIN5)
#define SUPPLY_OUT 0x02                           // Register fuer "Power Supply Data"


void SPI_Master_Init(void)
{
     /* Set MOSI, SCK and SS output, all others input */
     DDRB |= (1<<DDB5)|(1<<DDB7)|(1<<DDB4);        
     /* Enable SPI, Master, set clock rate fck/64, SPI Mode 0*/
     SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (0<<SPR0) | (0<<CPOL) | (0<<CPHA);
}


void SPI_MasterTransmit(char cData)
{
  /* Start transmission */
  SPDR = cData;
  /* Wait for transmission complete */
  while(!(SPSR & (1<<SPIF)));
}


/**********************************************************************/
/*                           main Funktion                            */
/**********************************************************************/
int main(void)
{
     SPI_Master_Init();


while(1)
{
     int16 data16 = 0;
     uint8 dataH = 0, dataL = 0;


     CS_ADIS16201_L();  // ChipSelect auf LOW
     SPI_MasterTransmit((SUPPLY_OUT & 0b00111111)); // RW-Bit auf 0 (lesen). Zero-Bit auf 0. Alle anderen Bits bleiben unveraendert
     SPI_MasterTransmit(0x00);
     CS_ADIS16201_H();  // ChipSelect auf HIGH


     CS_ADIS16201_L();  // ChipSelect auf LOW
     SPI_MasterTransmit(0x00);
     dataH = SPDR; // MSB
     SPI_MasterTransmit(0x00);
     dataL = SPDR; // LSB
     CS_ADIS16201_H();  // ChipSelect auf HIGH


     data16 = ( (dataH << 8) | dataL ); // 2 Bytes zu einem Wert zusammenfuegen
     data16 = data16 & 0x0FFF; // die ersten 4 Bits zu 0 setzen, da die Daten nur in den letzten 12-Bit sind
     printf("%.2f\n", (data16 * 1.22 / 1000)); // Umrechnen in "Volt" und ausgeben

     return 0;
}
}

Nach meiner Theorie sollte es funktionieren ( :-) ), tut es aber nicht. 
Ich bekomme von Sensor nicht die Daten, die ich erwarte (Wert seiner 
Versorgungsspannung).

Wo liegt mein Fehler?
In den ersten 8Bits setze den R/W-Bit auf "Read" (Bit7), setze den 
zero-Bit auf 0 (Bit6), und setze danach die Adresse des auszulesenden 
Registers (Bit5-0).
In den nächsten 8Bits schicke ich einfach Nullen.
Danach wird die Übertragung beendet, und der Sensor müsste nun 
"verstehen" was ich von ihm will :-)

Beim nächsten ChipSelect müsste er mir die Antwort auf meine Anfrage 
senden.
Dabei speichere ich das ganze als 2x8Bit und verknüpfe es danach 
zusammen.


???

Autor: Bensch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
CS low, 16 bit raus, CS high

So einfach geht's

Autor: Andreas V. (sevenup)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das wäre so, wenn ich in den Sensor schreiben möchte.
Wie mache ich es aber, wenn ich AUS dem Sensor auslesen will?

Sensor: 16Bit
Atmega: 8Bit

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>     data16 = ( (dataH << 8) | dataL ); // 2 Bytes zu einem Wert

     data16 = ( ((int16)dataH << 8) | dataL ); // 2 Bytes zu einem Wert

Autor: Bensch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
2x8=16

Schau doch mal im Datenblatt nach, wie man in den uC einliest ohne zu 
schreiben.

Aha......

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>CS low, 16 bit raus, CS high
>>So einfach geht's
>das wäre so, wenn ich in den Sensor schreiben möchte.
>Wie mache ich es aber, wenn ich AUS dem Sensor auslesen will?
>Sensor: 16Bit
>Atmega: 8Bit

Genauso.

Du schiebst zweimal acht Bit raus, und liest gleichzeitig die 
hereingekommenen zweimal acht Bit ein.

Das ist dein Ergenbis.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bist Du Dir sicher, dass CPOL und CPHA auf 0 sein müssen?

Ich würde sagen, dass die Teile beide 1 sein müssen. Im 644er Datenblatt 
(Seite 168) unteres Diagramm (Fig. 17-4). Die SCK Variante mit CPOL = 1 
sieht dem im Sensordatenblatt gezeigten Diagramm ähnlicher als das obere 
mit CPHA = 0 und CPOL = 0...

Könnte mich allerdings auch täuschen ;-)

Und dann im Sensordatenblatt auf Seite 5 ganze unten der Satz:

"Utilizing SPI Settings Typically Identified as Phase = 1, Polarity = 
1)"


=> Phase = CPHA und Polarity = CPOL würde ich dazu sagen ....

Autor: Andreas V. (sevenup)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bensch schrieb:
> Schau doch mal im Datenblatt nach, wie man in den uC einliest ohne zu
> schreiben.


Ich müsste ja dem Sensor aber mitteilen, aus welchem Register ich lesen 
möchte. Dann wäre es ja quasi ein "schreiben"?




Matthias Lipinsky schrieb:
>>>CS low, 16 bit raus, CS high
>>>So einfach geht's
>>das wäre so, wenn ich in den Sensor schreiben möchte.
>>Wie mache ich es aber, wenn ich AUS dem Sensor auslesen will?
>>Sensor: 16Bit
>>Atmega: 8Bit
>
> Genauso.
>
> Du schiebst zweimal acht Bit raus, und liest gleichzeitig die
> hereingekommenen zweimal acht Bit ein.
>
> Das ist dein Ergenbis.

Dieses Prinzip habe ich verstanden :-)
Aber mache ich mit meinem Code nicht das Gleiche?


Matthias schrieb:
> Bist Du Dir sicher, dass CPOL und CPHA auf 0 sein müssen?
>
> Ich würde sagen, dass die Teile beide 1 sein müssen. Im 644er Datenblatt
> (Seite 168) unteres Diagramm (Fig. 17-4). Die SCK Variante mit CPOL = 1
> sieht dem im Sensordatenblatt gezeigten Diagramm ähnlicher als das obere
> mit CPHA = 0 und CPOL = 0...
>
> Könnte mich allerdings auch täuschen ;-)
>
> Und dann im Sensordatenblatt auf Seite 5 ganze unten der Satz:
>
> "Utilizing SPI Settings Typically Identified as Phase = 1, Polarity =
> 1)"
>
>
> => Phase = CPHA und Polarity = CPOL würde ich dazu sagen ....

Jep, da war ich mir auch nicht sicher, welchen SPI-Modi ich aussuchen 
muss. Habe nun auf Modus 3 umgestellt.
Leider immer noch kein Erfolg :-(

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab da so ein Verdacht:

Versuch mal das da unten:

void SPI_MasterTransmit(char cData)
{
  SPSR |= (1<<SPIF); // SPIF Flag löschen bevor eine weitere
                     // Aktion ausgeführt wird....
  
  /* Start transmission */
  SPDR = cData;
  /* Wait for transmission complete */
  while(!(SPSR & (1<<SPIF)));
}



Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas V. schrieb:

> Ich müsste ja dem Sensor aber mitteilen, aus welchem Register ich lesen
> möchte. Dann wäre es ja quasi ein "schreiben"?

Das ist eine Grundeigenschaft von SPI: kein Lesen ohne zu schreiben.
Du schreibst in einer ersten Phase die 16-bittige Registeradresse,
in einer zweiten Phase schreibst du 16 dummy-Bits, damit du die
16-bittige Antwort einlesen kannst.  Die select-Leitung bleibt dabei
die ganze Zeit gezogen.

(Disclaimer: ich habe mir das Datenblatt des Slaves jetzt nicht
angesehen.  Das ist eine Beschreibung der allgemeinen Funktionsweise
von SPI.)

Autor: Andreas V. (sevenup)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias schrieb:
> Ich hab da so ein Verdacht:
>
> Versuch mal das da unten:
>
>
> 
> void SPI_MasterTransmit(char cData)
> {
>   SPSR |= (1<<SPIF); // SPIF Flag löschen bevor eine weitere
>                      // Aktion ausgeführt wird....
> 
>   /* Start transmission */
>   SPDR = cData;
>   /* Wait for transmission complete */
>   while(!(SPSR & (1<<SPIF)));
> }
> 
> 
> 
Habe die Zeile eingefügt, keine Veränderungen :-(
Der Controller funktioniert mit einem anderen Sensor (8-Bit) mit den 
gleichen SPI-Funktionen einwandfrei. Aber mit dem 16-bittigen klappt es 
nicht.


Jörg Wunsch schrieb:
> Andreas V. schrieb:
>
>> Ich müsste ja dem Sensor aber mitteilen, aus welchem Register ich lesen
>> möchte. Dann wäre es ja quasi ein "schreiben"?
>
> Das ist eine Grundeigenschaft von SPI: kein Lesen ohne zu schreiben.
> Du schreibst in einer ersten Phase die 16-bittige Registeradresse,
> in einer zweiten Phase schreibst du 16 dummy-Bits, damit du die
> 16-bittige Antwort einlesen kannst.  Die select-Leitung bleibt dabei
> die ganze Zeit gezogen.
>
> (Disclaimer: ich habe mir das Datenblatt des Slaves jetzt nicht
> angesehen.  Das ist eine Beschreibung der allgemeinen Funktionsweise
> von SPI.)
Stimmt nicht gant. Zwischen der ersten und der zweiten Phase geht die 
select-Leitung kurzzeitig wieder auf High.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beitrag "Re: ATmega 644v und SPI: Problem mit 16-Bit"

Vieleicht mag er das ja doch noch mal ausprobieren ;)

Autor: Andreas V. (sevenup)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
holger schrieb:
> Beitrag "Re: ATmega 644v und SPI: Problem mit 16-Bit"
>
> Vieleicht mag er das ja doch noch mal ausprobieren ;)

ups, habe vergessen darauf zu antworten :-/
Ich habe das natürlich ausprobiert, danke für dein Tipp!
Hat aber keine Verbesserung gebracht :-(


Was mir jetzt auffällt:
Bei dem oben von mir aufgeführten Code (Auslesen des 
SUPPLY_OUT-Registers) erhalte ich folgende Werte:
MSB: 0x00
LSB: 0xCA

Resette ich den Controller, dann bekomme ich
MSB: 0x00
LSB: 0x00

Nach einem nochmaligen Reset dann wieder
MSB: 0x00
LSB: 0xCA

usw....

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas V. schrieb:

> Stimmt nicht gant. Zwischen der ersten und der zweiten Phase geht die
> select-Leitung kurzzeitig wieder auf High.

Der ist in der Tat ein wenig eigenwillig hier.  Die Registerzugriffe
selbst sind eigentlich gar keine 16-bit-Zugriffe, sondern letztlich
zwei 8-bit-Zugriffe innerhalb eines SPI-Zyklus.  Wenn man aber Daten
lesen will, dann muss man einen weiteren 16-bit-Zugriff anhängen,
der dann die Daten liefert, wobei man ihm auf DIN bereits das nächste
Kommando senden kann.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt könnte es noch sein, dass er ein Problem hat, wenn man Ihm
die Pins für den CS zu schnell schaltet. Evtl. sollte auch zwischen
dem "kurzen" H (zwischen den beiden Phasen) ein kurzes delay()
rein. Im Zweifelsfall ca. das dreifache der Periodendauer des SPI 
Taktes.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias schrieb:
> Jetzt könnte es noch sein, dass er ein Problem hat, wenn man Ihm
> die Pins für den CS zu schnell schaltet.

Datenblatt Seite 5, das Teil hat ziemlich lange Chipselekt-Zyklen-
zeiten.

Autor: Andreas V. (sevenup)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias schrieb:
> Jetzt könnte es noch sein, dass er ein Problem hat, wenn man Ihm
> die Pins für den CS zu schnell schaltet. Evtl. sollte auch zwischen
> dem "kurzen" H (zwischen den beiden Phasen) ein kurzes delay()
> rein. Im Zweifelsfall ca. das dreifache der Periodendauer des SPI
> Taktes.
das war der etnscheidene Tipp!
Danke schön!

Habe zwischen zwei Phasen eine Warteschleife von 100µs eingefügt, und 
bekommen nun meine richtigen Daten!

juhu :-)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.