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


von Andreas V. (sevenup)


Lesenswert?

Ich muss eine Kommunikation zwischen einem ATmega644
http://www.atmel.com/dyn/resources/prod_documents/doc2593.pdf

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

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:
1
// Definitionen
2
#define CS_ADIS16201_L()   PORTD  &=~ 0b01000000  // CS auf 0 (PortD PIN5)
3
#define CS_ADIS16201_H()   PORTD  |=  0b01000000  // CS auf 1 (PortD PIN5)
4
#define SUPPLY_OUT 0x02                           // Register fuer "Power Supply Data"
5
6
7
void SPI_Master_Init(void)
8
{
9
     /* Set MOSI, SCK and SS output, all others input */
10
     DDRB |= (1<<DDB5)|(1<<DDB7)|(1<<DDB4);        
11
     /* Enable SPI, Master, set clock rate fck/64, SPI Mode 0*/
12
     SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (0<<SPR0) | (0<<CPOL) | (0<<CPHA);
13
}
14
15
16
void SPI_MasterTransmit(char cData)
17
{
18
  /* Start transmission */
19
  SPDR = cData;
20
  /* Wait for transmission complete */
21
  while(!(SPSR & (1<<SPIF)));
22
}
23
24
25
/**********************************************************************/
26
/*                           main Funktion                            */
27
/**********************************************************************/
28
int main(void)
29
{
30
     SPI_Master_Init();
31
32
33
while(1)
34
{
35
     int16 data16 = 0;
36
     uint8 dataH = 0, dataL = 0;
37
38
39
     CS_ADIS16201_L();  // ChipSelect auf LOW
40
     SPI_MasterTransmit((SUPPLY_OUT & 0b00111111)); // RW-Bit auf 0 (lesen). Zero-Bit auf 0. Alle anderen Bits bleiben unveraendert
41
     SPI_MasterTransmit(0x00);
42
     CS_ADIS16201_H();  // ChipSelect auf HIGH
43
44
45
     CS_ADIS16201_L();  // ChipSelect auf LOW
46
     SPI_MasterTransmit(0x00);
47
     dataH = SPDR; // MSB
48
     SPI_MasterTransmit(0x00);
49
     dataL = SPDR; // LSB
50
     CS_ADIS16201_H();  // ChipSelect auf HIGH
51
52
53
     data16 = ( (dataH << 8) | dataL ); // 2 Bytes zu einem Wert zusammenfuegen
54
     data16 = data16 & 0x0FFF; // die ersten 4 Bits zu 0 setzen, da die Daten nur in den letzten 12-Bit sind
55
     printf("%.2f\n", (data16 * 1.22 / 1000)); // Umrechnen in "Volt" und ausgeben
56
57
     return 0;
58
}
59
}

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.


???

von Bensch (Gast)


Lesenswert?

CS low, 16 bit raus, CS high

So einfach geht's

von Andreas V. (sevenup)


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

von holger (Gast)


Lesenswert?

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

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

von Bensch (Gast)


Lesenswert?

2x8=16

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

Aha......

von Matthias L. (Gast)


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.

von Matthias (Gast)


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 ....

von Andreas V. (sevenup)


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 :-(

von Matthias (Gast)


Lesenswert?

Ich hab da so ein Verdacht:

Versuch mal das da unten:
1
void SPI_MasterTransmit(char cData)
2
{
3
  SPSR |= (1<<SPIF); // SPIF Flag löschen bevor eine weitere
4
                     // Aktion ausgeführt wird....
5
  
6
  /* Start transmission */
7
  SPDR = cData;
8
  /* Wait for transmission complete */
9
  while(!(SPSR & (1<<SPIF)));
10
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.)

von Andreas V. (sevenup)


Lesenswert?

Matthias schrieb:
> Ich hab da so ein Verdacht:
>
> Versuch mal das da unten:
>
>
1
> 
2
> void SPI_MasterTransmit(char cData)
3
> {
4
>   SPSR |= (1<<SPIF); // SPIF Flag löschen bevor eine weitere
5
>                      // Aktion ausgeführt wird....
6
> 
7
>   /* Start transmission */
8
>   SPDR = cData;
9
>   /* Wait for transmission complete */
10
>   while(!(SPSR & (1<<SPIF)));
11
> }
12
> 
13
> 
14
>
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.

von holger (Gast)


Lesenswert?

Beitrag "Re: ATmega 644v und SPI: Problem mit 16-Bit"

Vieleicht mag er das ja doch noch mal ausprobieren ;)

von Andreas V. (sevenup)


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....

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Matthias (Gast)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Andreas V. (sevenup)


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 :-)

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.