Forum: Mikrocontroller und Digitale Elektronik Ein an die SPI - Schnittstelle angeschlossener ADC macht Probleme


von Neb N. (bluemorph)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

ich wollte heute bzw. auch schon gestern, einen ADC an meinen ATMEGA8515 
anschließen. Und zwar möchte ich die SPI Schnittstelle dafür nutzen. 
Anscheinend ist das aber schwerer als gedacht.

Also ich habe einen ADC0831 (Datenblatt im Anhang)), das ist ein 
normaler analog - digital Converter, der die digitalen Daten seriel 
sendet. Folgende Verbindungen habe ich hergestellt:

ADC:              PORT B:
CS                PB0
CLK               PB7 (SCK)
Data out          PB6 (MISO)

Dann bekommt der ADC noch VCC und Masse von meinem STK 500 und außerdem 
habe ich die Referenzspannung des ADCs auf VCC gesetzt. Der Vin(-) 
Eingang des ADCs liegt auch auf Masse. Die zu messende / wandelnde 
Spannung liegt am ADC an Vin(+) an.

Der Code, mit dem ich die SPI Schnittstelle anspreche und den ADC Wert 
abfrage sieht wie folt aus:
1
#define ADC_PORT     PORTB
2
#define ADC_PIN      PINB
3
#define ADC_DDR      DDRB
4
#define ADC_CS       SBIT( ADC_PORT, 0)
5
#define ADC_CS_DDR     SBIT( ADC_DDR, 0)
6
#define ADC_CLK     SBIT( ADC_PORT, 7)
7
#define ADC_CLK_DDR           SBIT( ADC_DDR, 7)
8
#define ADC_MISO     SBIT( ADC_PIN, 6)
9
#define ADC_MISO_DDR           SBIT( ADC_DDR, 6)
10
11
unsigned char adc_read (void);
12
13
int main (void){
14
15
  volatile unsigned char adc_wert = 0;
16
  
17
  ADC_CS_DDR     = 1;
18
  ADC_CS      = 1;
19
  ADC_CLK_DDR           = 1;
20
  ADC_MISO_DDR          = 0;
21
  ADC_MISO    = 0;
22
  
23
  SPCR |= (1<<SPE) | (1<<MSTR) | (1<<SPR0) | (1<<DORD);
24
  SPCR &= ~((1<<CPOL) | (1<<CPHA));
25
  DDRD = 0xFF;
26
27
  while(1){
28
      adc_wert = adc_read();
29
      PORTD = ~adc_wert;
30
      
31
    }
32
  
33
  return 0;
34
}
35
36
unsigned char adc_read (void){
37
  SPDR = 0x00;
38
  ADC_CS = 0;
39
  while ( !(SPSR & (1 << SPIF)) );
40
  ADC_CS = 1;
41
  return SPDR;
42
}

Okay, wie man nun leicht sieht,  möchte ich einfach den Wert des ADCs 
auslesen und an PORTD des STK500 ausgeben. Ich habe da die LEDS 
angeschlossen, deshalb soll er den adc_wert nochmal invertieren, damit 
die LEDS bei einer Spg. von 0 alle auch aus sind und bei einer Spg. von 
Vin(+) = Vref sollen alle LEDs leuchten.

Ich weiß nun wirklich nicht mehr weiter, und habe mich auch schon 
totgesucht hier im Forum, aber anscheinend ist es total banal eine ADC 
an die SPI Schnittstelle anzuschließen, dass niemand damit 
Schwierigkeiten hat. Nun gut ich habe sie smile!

Also Mein ADC macht irgendwas ... nur nicht das was er soll. Gebe ich 
auf den Vin(+) Pin des ADCs die halbe Vref Spannung so müsste ja 
eigentlich 127 in SPDR bzw. an meinen LEDs zu finden sein, das ist 
leider nicht so. Er macht ne 5. So zieht sich das egal bei welcher 
Messung auch immer durch.

Auffällig ist, dass wenn ich den Vin(+) Pin des ADCs auf Masse lege, 
dass er eine 1 anzeigt. Bei Vin(+) = Vref zeigt er auch nicht 255 
sondern nur 253 an.

Kann das alles irgendwas damit zutun haben, dass die Programmierung 
meines Atmega8515 ja über das ISP des STK500 gemacht wird und das sich 
da irgendwas beeinflusst??
Wenn ich nämlich nachdem die Programmierung beendet ist, den ISP Stecker 
abziehe, dann verändert sich der gewandelte Wert um -1.
Sprich, wenn ich nun Vin(+) an Masse lege wird aus der vorher 
gewandelten 5 eine 4.

Weiterhin habe ich das ganze Programm mal Simuliert mit dem AVR Studio. 
Dabei ist mir aufgefallen, dass an der Stelle wo diese Zeile steht:
1
while ( !(SPSR & (1 << SPIF)) );
Im zweiten Durchgang das Write Collision Flag gesetzt ist. Laut 
Datenblatt heißt das doch, dass gerade Daten in SPDR geschrieben werden, 
während einer Übertragung. Das hört sich für mich so negativ an!! Aber 
das ist doch richtig oder nicht?? So lange die Übertragung läuft ist 
doch das SPIF Bit auf 0 und Daten werden ins SPDR geschrieben (also muss 
doch das Write Collision Flag angehen), wenn die Übertragung nun fertig 
ist, steht im SPIF ne 1 und er bricht die Schleife ab und schaltet den 
ADC aus und gibt das SPDR zurück.

Ich versteh einfach nicht was falsch ist.

Kann es sein, dass mein ADC irgenwie defekt ist??? Oder meint ihr das 
Problem hat eher was mit der Konfiguration der SPI Schnittstelle zu 
tun??

Ich hoffe ihr könnt mir helfen!!

MfG BlueMorph

von CAnfänger (Gast)


Lesenswert?

setz mal den ADC_CS zuerst und weise dann das SPDR zu - in dem Moment 
(Zuweisung) fängt er nämlich schon an zu "clocken"

while ( !(SPSR & (1 << SPIF)) );

bedeutet, daß das Programm da warten soll, bis die Übertragung fertig 
ist.

von Neb N. (bluemorph)


Lesenswert?

Okay, ich hab den Code nun so geändert:
1
unsigned char adc_read (void){
2
  ADC_CS = 0;
3
  SPDR = 0x00;
4
  while ( !(SPSR & (1 << SPIF)) );
5
  ADC_CS = 1;
6
  return SPDR;
7
}

Hat sich allerdings nichts verändert. Gibt immer noch ne 5 bzw. 4 aus, 
wenn ich die Spg des ADCs auf Masse lege.

von holger (Gast)


Lesenswert?

Wenn man sich die Timing Diagramme reinzieht sieht man
das zwei Dummy Clocks am Anfang nötig sind. Erst dann
kommen die Daten. Einlesen der Daten auf steigender Flanke.
Du brauchst also min. 10 Clock Zyklen um den ADC zu lesen.

von holger (Gast)


Lesenswert?

Nachtrag: MSB kommt zuerst raus. Also stimmt dein DORD nicht.

von Neb N. (bluemorph)


Lesenswert?

OKay, das stimmt!! Wie bekommt man dass denn jetzt hin, dass das 
Programm so lange nichts tut?? Kann ich das mit _delay_us() Anweisungen 
hinbekommen?
Die Zeit die er warten muss ist ja quasi (3686400 / Vorteiler)^-1 * 2 
oder?? Das wären dann 8,68 us.

Das mit dem DORD hab ich nur auf 1 gemacht, wegen der Ausgabe auf die 
LEDs. Sonst hast Du recht müsste ich das auf 0 setzen, denn laut 
Datenblatt kommt ja das MSB zuerst aus dem ADC.

Danke aber schonmal für die Aufklärung!!!

von CAnfänger (Gast)


Lesenswert?

Schicke ihm doch mal zwei Byte (16 Clocks) und guck was passiert...

von holger (Gast)


Lesenswert?

>OKay, das stimmt!! Wie bekommt man dass denn jetzt hin, dass das
>Programm so lange nichts tut?? Kann ich das mit _delay_us() Anweisungen
>hinbekommen?

Mit Delays wird das nix. Du hast SPI noch nicht so ganz drin.
Der ATMega ist der Master. Die Clockimpulse werden vom Master
erzeugt wenn er etwas in SPDR schreibt. Schreibst du in SPDR
werden 8 Clockimpulse erzeugt. Mehr nicht! Also was tun?
Daten retten und nochmal in SPDR schreiben. Siehe
CAnfänger. Jetzt stehen deine 8 Bit aber verteilt
in zwei Bytes. Also passend zusammenfügen.

von Neb N. (bluemorph)


Lesenswert?

Meinst Du das so mit zwei Byte senden??
1
unsigned char adc_read (void){
2
  ADC_CS = 0;
3
  SPDR = 0x00;
4
  while ( !(SPSR & (1 << SPIF)) );
5
  SPDR = 0x00;
6
  while ( !(SPSR & (1 << SPIF)) );
7
  ADC_CS = 1;
8
  return SPDR;
9
}

Verändert das Ergebnis der Ausgabe auf den LEDS. Wenn ich nun Vin(+) = 
Vref mache ist das Ergebnis der Wandlung 255. Auf Masse ist das Ergebnis 
0. Bei Halber Referenzspg. ist das Ergebis auch 0.

Also auch nicht ... Mist.

von CAnfänger (Gast)


Lesenswert?

nach den ersten While... mußt Du natürlich das SPDR irgendwo 
zwischenspeichern, wenn Du es später verwenden willst. Und prüfe mal die 
Beschaltung und die Spannungen an deinem ADC.

von Neb N. (bluemorph)


Lesenswert?

Okay, ich hab jetzt den Code so abgeändert:
1
unsigned char adc_read (void){
2
  unsigned char temp1 = 0x00, temp2 = 0x00;
3
  
4
  ADC_CS = 0;
5
  SPDR = 0x00;
6
  while ( !(SPSR & (1 << SPIF)) );
7
  temp1 = SPDR;
8
  SPDR = 0x00;
9
  while ( !(SPSR & (1 << SPIF)) );
10
  temp2 = SPDR;
11
  ADC_CS = 1;
12
  temp1 = temp1 << 2;
13
  temp2 = temp2 >> 6;
14
  temp1 |= temp2;
15
  return temp1;
16
}

Funktioniert leider immer noch nicht richtig!

Muss ich mir das denn jetzt so vorstellen:
Sobald ich das erste Mal SPDR = 0 mache, werden 8 Takte lang Daten 
gesendet / empfangen!
Nachdem also die erste while - Schleife durchgelaufen ist müsste das 
SPDR doch so aussehen:
-------------------------------------
|   |   | MSB | 6 | 5 | | 4 | 3 | 2 |
-------------------------------------

Wenn ich das nun in temp1 speichere und dann zwei nach links schiebe, 
sieht doch dann temp1 so aus:

-------------------------------------
| MSB | 6 | 5 | | 4 | 3 | 2 | X | X |
-------------------------------------

Und das weite SPDR ergibt dann:

-----------------------------------
| 1 | LSB | X | X | X | X | X | X |
-----------------------------------

Stimmt das?? Oder muss sich der ADC auch erst wieder bei zweiten mal 
SPDR senden wieder die zwei Dummy Takte "einstellen"??

von holger (Gast)


Lesenswert?

>Stimmt das??

Ja, perfekt vorgestellt. Die Routine müsste jetzt
auch gehen.

>Oder muss sich der ADC auch erst wieder bei zweiten mal
>SPDR senden wieder die zwei Dummy Takte "einstellen"??

Nein.

Was für einen Takt hat dein ATMega?
Setz doch einfach mal SPR1 auch noch auf 1.

von Neb N. (bluemorph)


Lesenswert?

Mhh, nachdem ich jetzt nochmal das DORD Bit auf 0 gesetzt habe und den 
Vorteiler noch vergrößert habe funktioniert es (geht aber auch mit dem 
anderen Vorteiler). Ich hab mir jetzt die Zahl auch direkt aufs Display 
ausgeben lassen.

Also anscheinend funktioniert es. Super gut von euch gewesen, mich  mal 
aufs Datenblatt hinzuweisen.

Ich muss ehrlich zugeben, dass ich das zuvor nur gemacht habe, um den 
ADC anzuschließen.

Danke besonders an holger. Und ich glaub jetzt hab ich auch die SPI ein 
bisschen besser verstanden!!

Also danke nochmal!!!

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.