Forum: Mikrocontroller und Digitale Elektronik Atmega2560 SPI-Verbindung zu ext. AD-Wandler


von Matthias H. (streno)


Lesenswert?

Hallo zusammen,

ich bin kurz vorm verzweifeln.

Kurz zu meinem vorhaben:

Ich möchte mit einem Atmega2560 digitale Werte eines externen 14bit 
AD-Wandler einlesen(LTC2356-14) und diese auf einem Display an einem 
Graph anzeigen lassen.

Wie SPI grob funktioniert, verstehe ich. Allerdings nicht wie ich 8bit 
einlesen kann. Ich finde auch keine geeigneten Beispiele dazu im netz. 
Ich probiere es bisher mit einer Library in der es die folgende funktion 
gibt:
1
void spi_transfer_sync (uint8_t * dataout, uint8_t * datain, uint8_t len)
2
// Shift full array through target device
3
{
4
       uint8_t i;      
5
       for (i = 0; i < len; i++) {
6
             SPDR = dataout[i];
7
             while((SPSR & (1<<SPIF))==0);
8
             datain[i] = SPDR;
9
       }
10
}

ich versuche es immer mit
1
spi_transfer_sync(0xAA,Buffer[2],2]

Es will einfach nicht klappen...
Vielleicht hat jemand soetwas schonmal gemacht und ein Beispiel an dem 
ich mir das abschauen kann?

Danke und viele Grüße
Matthias

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Matthias H. schrieb:
> ich versuche es immer mitspi_transfer_sync(0xAA,Buffer[2],2]
> Es will einfach nicht klappen...
> Vielleicht hat jemand soetwas schonmal gemacht und ein Beispiel an dem
> ich mir das abschauen kann?



Das kann nicht klappen....

Bei SPI werden immer pro Übertragung in beide Richtungen Daten 
transportiert.
Sprich gleichzeitig gesendet und empfangen.

  SPDR = dataout[i];                  sendet deine Daten zum Chip

  while((SPSR & (1<<SPIF))==0);       Wartet bis die Übertragung fertig 
ist

  datain[i] = SPDR;                   liest deine Daten erhalten vom 
Chip

spi_transfer_sync(0xAA,Buffer[2],2]

0xAA ist kein Zeiger auf den dataout  buffer sondern NIX
Buffer[2]   ist ein Zeiger  mitten in einen Buffer
das was stimmen könnte ist, dass du 2 Bytes übertragen willst.

Schreib mal was du genau willst

Gruß Thomas

von Peter12 (Gast)


Lesenswert?

Schau dir mal das an. Da sind insbesondere auch die ganzen Register 
beschrieben. Wenn man die einmal verstanden hat ist der Rest 
einleuchtend. Das Datenblatt hilft an dieser Stelle auch. Zumindest 
besser als einfach irgendeine Lib aus dem Netz zu verwenden.

http://maxembedded.com/2013/11/the-spi-of-the-avr/#Master_Init

Übernimmt der 2560 noch weitere Aufgaben? Ansonsten ist er eventuell 
auch ein wenig überdimensioniert. Für "kleinere" Atmels findet man 
relativ viele Beispiele zu SPI.

von Matthias H. (streno)


Lesenswert?

Thomas schrieb:
> das was stimmen könnte ist, dass du 2 Bytes übertragen willst.

Ich möchte die 14bit des AD-Wandlers auslesen. Deshalb muss ich die 
schleife zweimal durchlaufen.

Dem AD-Wandler brauche ich ja nichts senden, deshalb dachte ich ist es 
egal was ich ihm bei DATAOUT übergebe.

Und da SPDR immer 8 Bit groß ist, muss ich das ganze in einen String 
speichern...

von Thomas (Gast)


Lesenswert?

Matthias H. schrieb:
> Dem AD-Wandler brauche ich ja nichts senden, deshalb dachte ich ist es
> egal was ich ihm bei DATAOUT übergebe.

Das Glaube ich nicht, dass der keine Config braucht...
Schreib mal den TYP AD Wandler. dann lese ich mal nach...

Gruß Thomas

von Thomas (Gast)


Lesenswert?

Thomas schrieb:
> Das Glaube ich nicht, dass der keine Config braucht...
> Schreib mal den TYP AD Wandler. dann lese ich mal nach...

Mist   habe es gerade gesehen....
hast du ja ganz oben schon geschrieben

Gruß Thomas

PS ich les mal ....

von Thomas (Gast)


Lesenswert?

So Hab gelesen....

Der hat mit einfachen SPI nicht viel zu tun.

Schau dir mal Seite 11 von 20 an.

SCK = Low -->
Steigende Flanke von CONV -->
im CONV High erste steigende Flanke von SCK -->
bei der dritten fallenden Flanke von SCK = D13 von SDO
bei der 16'ten  fallenden Flanke von SCK = D0 von SDO
SCK Takt 17 & 18 durchlaufen und gehen ins leere
(insgesamt muss SCK 18 mal Takten)

dann geht es wieder los
SCK = Low -->


Die erste Wandlung kannst du in die Tonne treten.
Also das ganze 2 x Durchlaufen. Dann ist die erste gültige Wandlung 
durch
und du hast einen reellen Wert.

Das kannst du nur zu Fuss   ohne SPI lösen.

Mach das Ganze mit einer Statemachine...

SCK = Low;
CONV = Low;


int State;

switch (State)
  case 0:
    CONV = High;
    ....
    State +=1;
    break;
  case 1:
usw.....




Gruß Thomas

von Matthias H. (streno)


Lesenswert?

Thomas schrieb:
> Das kannst du nur zu Fuss   ohne SPI lösen.

Okay aber warum steht dann im Datenblatt auf Seite 1 3 Wire SPI 
Compatible? Das war der Grund warum ich den AD-Wandler kaufte.

State Machine... so etwas habe ich noch nie gemacht. Enstpricht der 
State dann quasi meiner CLK Sprich State 1=Low State 2=High State3=Low??

Ich versuche den Code mal zu schreiben und werde Fehler machen. 
Vielleicht kannst du ja mal drüber schauen

von Matthias H. (streno)


Lesenswert?

Hallo Thomas,

ich hab es mal versucht, hänge hier und da und weiß vorallem nicht ob 
ich auf einem grünen Zweig bin.
Probleme habe ich vorallem:
-Wie lese ich das Datenwort ein? Muss ich die Bits in einen String 
speichern und wie wandle ich diese in ein int?
-Klappt das, dass ich die FUnktion readdata() aufrufe und dann den Wert 
auf meinem Display anzeige und anschließen wieder die Funktion aufrufe?

Ich hänge meinen Code jetzt mal an. Wie gesagt ich weiß nicht ob das 
überhaupt annähernd richtig ist. Vielleicht ist die Sache für mich als 
Schüler noch ein bisschen zu kompliziert
1
int readdata()
2
{
3
  DDRB|=0x03;
4
  //SCK=PB1 CONV=PB0 SDO=PB3
5
6
  int State=0;
7
  PORTB &= ~(1 << PB1); //SCK=LOW
8
  PORTB &= ~(1 << PB0); //CONV=LOW
9
10
  switch (State)
11
  {
12
  case 0:
13
  PORTB |= (1 << PB0); //CONV=HIGH
14
  State +=1;
15
  break;
16
  case 1:
17
  PORTB |= (1 << PB1); //SCK=High Beginn Sample
18
  State+=1;
19
  break;
20
  case 2:  //Nichts passiert
21
  PORTB &= ~(1 << PB1);
22
  State+=1;
23
  break;
24
  case 3: //Nichts passiert
25
  PORTB |= (1 << PB1);
26
  State+=1;
27
  break;
28
  case 4: //Nichts passiert
29
  PORTB &= ~(1 << PB1);
30
  State+=1;
31
  break;
32
  case 5:  //Nichts passiert
33
  PORTB |= (1 << PB1);
34
  State+=1;
35
  break;
36
  case 6: //D13
37
  PORTB &= ~(1 << PB1);
38
  //D13 einlesen
39
  State+=1;
40
  break;
41
  case 7: 
42
  PORTB |= (1 << PB1);
43
  State+=1;
44
  break;
45
  case 8: //D12
46
  PORTB &= ~(1 << PB1);
47
  //D12 einlesen
48
  State+=1;
49
  break;
50
  case 9: 
51
  PORTB |= (1 << PB1);
52
  State+=1;
53
  break;
54
  case 10: //D11
55
  PORTB &= ~(1 << PB1);
56
  //D11 einlesen
57
  State+=1;
58
  break;
59
  case 11: 
60
  PORTB |= (1 << PB1);
61
  State+=1;
62
  break;
63
  case 12:  //D10
64
  PORTB &= ~(1 << PB1);
65
  //D10 einlesen
66
  State+=1;
67
  break;
68
  case 13:  
69
  PORTB |= (1 << PB1);
70
  State+=1;
71
  break;
72
  case 14: //D9
73
  PORTB &= ~(1 << PB1);
74
  //D9 einlesen
75
  State+=1;
76
  break;
77
  case 15: 
78
  PORTB |= (1 << PB1);
79
  State+=1;
80
  break;
81
  case 16: //D8
82
  PORTB &= ~(1 << PB1);
83
  //D8 einlesen
84
  State+=1;
85
  break;
86
  case 17: 
87
  PORTB |= (1 << PB1);
88
  State+=1;
89
  break;
90
  case 18: //D7
91
  PORTB &= ~(1 << PB1);
92
  //D7 einlesen
93
  State+=1;
94
  break;
95
  case 19: 
96
  PORTB |= (1 << PB1);
97
  State+=1;
98
  break;
99
  case 21: //D6
100
  PORTB &= ~(1 << PB1);
101
  //D6 einlesen
102
  State+=1;
103
  break;
104
  case 22: 
105
  PORTB |= (1 << PB1);
106
  State+=1;
107
  break;
108
  case 23: //D5
109
  PORTB &= ~(1 << PB1);
110
  //D5 einlesen
111
  State+=1;
112
  break;
113
  case 24: 
114
  PORTB |= (1 << PB1);
115
  State+=1;
116
  break;
117
  case 25: //D4
118
  PORTB &= ~(1 << PB1);
119
  //D4 einlesen
120
  State+=1;
121
  break;
122
  case 26: 
123
  PORTB |= (1 << PB1);
124
  State+=1;
125
  break;
126
  case 27: //D3
127
  PORTB &= ~(1 << PB1);
128
  //D3 einlesen
129
  State+=1;
130
  break;
131
  case 28: 
132
  PORTB |= (1 << PB1);
133
  State+=1;
134
  break;
135
  case 29: //D2
136
  PORTB &= ~(1 << PB1);
137
  //D2 einlesen
138
  State+=1;
139
  break;
140
  case 30: 
141
  PORTB |= (1 << PB1);
142
  State+=1;
143
  break;
144
  case 31: //D1
145
  PORTB &= ~(1 << PB1);
146
  //D1 einlesen
147
  State+=1;
148
  break;
149
  case 32: 
150
  PORTB |= (1 << PB1);
151
  State+=1;
152
  break;
153
  case 33: //D0
154
  PORTB &= ~(1 << PB1);
155
  //D0 einlesen
156
  State+=1;
157
  break;
158
  case 34: 
159
  PORTB |= (1 << PB1);
160
  State+=1;
161
  break;
162
  case 35: //warten
163
  PORTB &= ~(1 << PB1);
164
  State+=1;
165
  break;
166
    case 36:
167
    PORTB |= (1 << PB1);
168
    State+=1;
169
    break;
170
    case 37: //warten
171
    PORTB &= ~(1 << PB1);
172
    State=0;
173
    break;
174
}
175
}

: Bearbeitet durch User
von eProfi (Gast)


Lesenswert?

Die erste Entscheidung ist, Hardware- oder Software-SPI.
Nehmen wir mal wegen der 18 Bits SW.
Die Idee mit der State-Machine ist eher akademischer Natur.
Außerdem würde ich -wenn überhaupt- nur 18 States verwenden.

Du musst unterscheiden, ob Du Dummy-Bits überträgst oder Nutzdaten.
Ungetesteter Code aus dem Stegreif:
1
int main(void){
2
  uint16_t mask,data;
3
  DDRB|=0x03; //SCK=PB1 CONV=PB0 SDO=PB3
4
  PORTB &= ~(1 << PB1); //SCK=LOW
5
  PORTB &= ~(1 << PB0); //CONV=LOW
6
  while(1){
7
    PORTB |= (1 << PB0); //CONV=HIGH
8
    PORTB |= (1 << PB1); //SCK=High Beginn Sample #1
9
    PORTB &=~(1 << PB0); //CONV=LOW
10
    PORTB &=~(1 << PB1); //SCK=LOW
11
    PORTB |= (1 << PB1); //SCK=High #2
12
    PORTB &=~(1 << PB1); //SCK=LOW
13
    data=0;mask=0x2000;do{ //14 mal (1 << 13 = 0x2000) #3..16
14
      PORTB |= (1 << PB1); //SCK=High
15
      PORTB &=~(1 << PB1); //SCK=LOW
16
      if(PINB & PBx){data |= mask;}  //PBx: MISO-Portbit angeben!!
17
    }while((mask >>= 1) != 0);
18
19
    PORTB |= (1 << PB1); //SCK=High #17
20
    PORTB &=~(1 << PB1); //SCK=LOW
21
    PORTB |= (1 << PB1); //SCK=High #18
22
    PORTB &=~(1 << PB1); //SCK=LOW
23
  } //while(1)
24
  //hier data graphisch ausgeben
25
} //main(void)

von eProfi (Gast)


Lesenswert?

Korrektur: graphische Ausgabe muss innerhalb der while(1)-Schleife 
stehen, also eine Zeile oberhalb.

von eProfi (Gast)


Lesenswert?

if(PINB & PBx){data |= mask;}  //PBx: MISO-Portbit angeben!!
soll natürlich heißen
      if(PINB & (1<<PBx)){data |= mask;}  //PBx: MISO-Portbit angeben!!

von Matthias H. (streno)


Lesenswert?

Danke Eprofi,

so ist es für mich verständlich. Ich kann es leider erst heute Abend 
teesten, werde aber auf jedenfall berichten.


Viele Grüße
Matthias

von Matthias H. (streno)


Lesenswert?

Also leider kam ich erst heute zum testen.

Das Programm habe ich wie von eprofi übernommen und getestet. die 
einzelnen Schritte arbeitet er sauber ab. Dennoch erhalte ich immer 
16383 als Data. Ich habe gelesen der AD-Wandler gibt mir Data als 
Zweierkomplement aus. Sprich ich erhalte immer 0.

So wo kann jetzt der Fehler liegen?
Ist der Code doch nicht richtig?
Oder liegt der Fehler eventuell am AD-Wandler? Kann ich die richtige 
funktionsweise des Wandlers denn testen?

Danke und viele Grüße
Matthias

von Matthias H. (streno)


Lesenswert?

Mein Code:
1
x=0;
2
uint16_t mask,data;
3
DDRB|=0x03; //SCK=PB1 CONV=PB0 SDO=PB3
4
while(1)
5
      {
6
          
7
           
8
                PORTB &= ~(1 << PB1); //SCK=LOW
9
                PORTB &= ~(1 << PB0); //CONV=LOW
10
            PORTB |= (1 << PB0); //CONV=HIGH
11
            PORTB |= (1 << PB1); //SCK=High Beginn Sample #1
12
            PORTB &=~(1 << PB0); //CONV=LOW
13
            PORTB &=~(1 << PB1); //SCK=LOW
14
            PORTB |= (1 << PB1); //SCK=High #2
15
            PORTB &=~(1 << PB1); //SCK=LOW
16
            data=0;mask=0x2000;
17
            do{ //14 mal (1 << 13 = 0x2000) #3..16
18
              PORTB |= (1 << PB1); //SCK=High
19
              PORTB &=~(1 << PB1); //SCK=LOW
20
   
21
              if(PINB & (1<<PB3)){data |= mask;} 
22
            }while((mask >>= 1) != 0);
23
24
            PORTB |= (1 << PB1); //SCK=High #17
25
            PORTB &=~(1 << PB1); //SCK=LOW
26
            PORTB |= (1 << PB1); //SCK=High #18
27
            PORTB &=~(1 << PB1); //SCK=LOW
28
            
29
            buf1[x]=data;
30
           myGLCD.setColor(VGA_BLACK);
31
            myGLCD.printNumI(buf1[x],500,10);
32
            myGLCD.printNumI(x,300,10);
33
x++;
34
if(x==750)
35
        {
36
37
          x=0;}
38
}

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Matthias H. (streno)


>Das Programm habe ich wie von eprofi übernommen und getestet. die
>einzelnen Schritte arbeitet er sauber ab. Dennoch erhalte ich immer
>16383 als Data.

Dann sit was faul. Auch beimbesten AD-Wandler wackeln die untersten 
Bits, erst recht bei 14 Bit.
Wie sieht dein Hardwareaufbau aus? Zeig uns ein Bild davon. Und einen 
Schaltplan.

>So wo kann jetzt der Fehler liegen?
>Ist der Code doch nicht richtig?

Wahrscheinlich.

>Oder liegt der Fehler eventuell am AD-Wandler?

Eher nicht. Bestenfalls an der Verdrahtung zu deinem Arduino.

> Kann ich die richtige
>funktionsweise des Wandlers denn testen?

Nennt sich Fehlersuche.

Beitrag "Re: Atmega2560 SPI-Verbindung zu ext. AD-Wandler"

Dein Code ist GRAUSAM! Außerdem gehören lange Quelltexte in den Anhang, 
siehe Netiquette.

Der IC ist zwar nicht 100% normales SPI, aber man kann ihn mit normalem 
SPI abfragen.

CONV Signal HIGH
CONV SIGNAL LOW
3x1 Byte senden/empfangen. Die Sendedaten sind beliebig, die kommen 
sowieso nicht am ADC an
Aus den 3 empfangenen Bytes durch entsprechendes Bitschieben das 
Ergebnis extrahieren, ggf. vorzeichenrichtig erweitern.

Ist nicht wirklich schwer, wenn gleich auch kein totales 
Anfängerprojekt.

Noch ein Tip. Mehrere Daten speichert man in einem Array, nicht in einem 
String. Denn ein String ist im Normalfall ein Array, das ASCII-Zeichen 
(Text) enthält, aber keine Binärdaten.

von Matthias H. (streno)


Angehängte Dateien:

Lesenswert?

Falk B. schrieb:
> Dann sit was faul. Auch beimbesten AD-Wandler wackeln die untersten
> Bits, erst recht bei 14 Bit.
> Wie sieht dein Hardwareaufbau aus? Zeig uns ein Bild davon. Und einen
> Schaltplan.


Hier ein Bild meines Aufbaus ab dem AD-Wandler. Davor ist denke ich 
alles richtig, da die Spannung am AD-Wandler messbar anliegt...

Falk B. schrieb:
> Eher nicht. Bestenfalls an der Verdrahtung zu deinem Arduino.

Ich programmiere den Atmega nicht als Arduino, sondern in AtmelStudio. 
DIe UTFT Bibliothek habe ich mir nur umgeschrieben

von Falk B. (falk)


Lesenswert?

@Matthias H. (streno)

>Hier ein Bild meines Aufbaus ab dem AD-Wandler.

Kaum. Das ist der Schaltplan. Mit Aufbau meine ich den realen 
Hardwareaufbau. Deinen Arduino + Kabel + AD-Wandler-Board.

>Davor ist denke ich
>alles richtig, da die Spannung am AD-Wandler messbar anliegt...

Denken? Du GLAUBST! Nur wenn du es gewissenhaft PRÜFST, kann es 
vielleicht stimmen. Papier ist geduldig.

>Ich programmiere den Atmega nicht als Arduino, sondern in AtmelStudio.

OK.

>DIe UTFT Bibliothek habe ich mir nur umgeschrieben

Dieser komische Pegelwandler KANN im Einzelfall Probleme machen. Da muss 
ich mal ins Datenblatt schauen.

von Falk B. (falk)


Lesenswert?

Hmm so könnte es gehen. Probier mal.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define CONV_HIGH PORTB |=  (1<<PB0)
5
#define CONV_LOW  PORTB &= ~(1<<PB0)
6
7
int16_t read_LT2356(void) {
8
    int16_t tmp;
9
    
10
    // short start conversion pulse
11
    CONV_HIGH;
12
    _delay_us(1);
13
    CONV_LOW;
14
15
    // read 3 bytes using hardware SPI
16
    // last dummy byte is for fullfilling the minimum of 17 clocks / cycle rule
17
18
    SPDR = 0x00;        // dummy data
19
    while (!(SPSR & (1<<SPIF)));   // wait for transmission end
20
    tmp = (int16_t)SPDR << 8;
21
22
    SPDR = 0x00;        // dummy data
23
    while (!(SPSR & (1<<SPIF)));   // wait for transmission end
24
    tmp |= SPDR & 0xFF;
25
26
    SPDR = 0x00;        // dummy data
27
    while (!(SPSR & (1<<SPIF)));   // wait for transmission end
28
    SPDR;               // dummy read
29
30
    // sign extention
31
32
    tmp &= 0x3FFF;  // clear MSBs
33
    if (tmp & (1<<13)) tmp |= 0xC000;   // copy bit 13 to 14 and 15
34
   
35
    return tmp; 
36
}
37
38
39
int main(void) {
40
41
    // IO init
42
43
    DDRB |= (1<<PB1) | (1<<PB0);        // set SCK und SS to output 
44
45
    // SPI init
46
47
    SPCR = (1<<SPE) | (1<<MSTR);
48
    SPSR = (1<<SPI2X);
49
50
51
    read_LT2356();
52
}

Aber der MAX3391 ist mir nicht ganz geheuer. Diese bidirektionalen 
Pegelwandler ohne Richtungsumschaltung sind tückisch! Da kann viel 
schief gehen, denn dort sind diverse Tricks eingebaut! Bei 
unidirektionalen Signalen sollte so weit wie immer möglich immer 
unidirektionale Pegelwandler nehmen!

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.