Forum: Mikrocontroller und Digitale Elektronik ADC LTC2436 an Atmega8, in C


von Werner (Gast)


Lesenswert?

Hallo
ich bemühe mich schon seid Stunden diesen AD Wandler zum laufen zu 
bringen doch bisher leider erfolglos. (Linear Technology)
Datenblatt unter:
http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1155,C1001,C1152,P2287,D3179

Anschluss:
PIN 1,2 auf 5 Volt
PIN 3,5,7-10,14-16 auf 0 V (GND)

PIN 12 auf MISO, Atmega
PIN 13 auf SCK, Atmega
PIN 11 auf PC1, Atmega



Hier ist mein Programm, vielleicht könnt ihr mir helfen, vielen Dank.
(Erklärung unten)


// SPI, Atmega als Master
// MOSI, SS und SCK Ausgang, alle anderen als Eingänge

DDRB |= (1 << DDB3) | (1 << DDB5);  // SPI Ausgänge aktivieren
DDRB |= (1 << DDB2);      // Slave Select Ausgang

DDRC |= (1 << DDC1);      // CS_nicht, ADC LTC2436
PORTC |= (1 << PC1);      // CS_nicht ein


// Enable SPI, Master, Vorteiler 32

SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1);
SPSR = (1 << SPI2X);



uint32_t SPI_lesen() {  // LTC2436

SPCR &= ~(1 << CPOL);  // Polarität der Taktleitung, damit SCK von 
Atmega

//SPCR |= (1 << CPHA);

PORTB |= (1 << PB2);  // SPI aktivieren
PORTC &= ~(1 << PC1);  // CS_nicht aus, ADC starten

unsigned char Daten1, Daten2, Daten3;

SPDR = 0;
while(!(SPSR & (1 << SPIF))) {
    ;  // warten bis Daten übertragen
}
Daten3 = SPDR;


SPDR = 0;
while(!(SPSR & (1 << SPIF))) {
    ;  // warten bis Daten übertragen
}
Daten2 = SPDR;


SPDR = 0;
while(!(SPSR & (1 << SPIF))) {
    ;  // warten bis Daten übertragen
  }
Daten1 = SPDR;


PORTC |= (1 << PC1);  // CS_nicht ein, ADC aus
PORTB &= ~(1 << PB2);  // SPI ausschalten


uint32_t wert = (Daten3 << 11);
wert |= (Daten2 << 3);
wert |= (Daten1 >> 5);

//SPCR &= ~(1 << CPHA);
//SPCR |= (1 << CPOL);  // Polarität der Taktleitung
return  wert;
}




Diese ADC hat nämlich ein 19 Bit Output-format ich hoffe das ich mich 
beim hin und her schieben der Bits nich geirrt habe.
Weiters wird automatisch einmal Channel0 und beim nächsten ansprechen 
Channel1 ausgelesen, d.h. man muss immer 2 mal auslesen pro Zyklus.

Folgendes verhalten hat mein Programm:
Ich mache eine Abtastung über den Timer interrupt alle Sekunden.
nun lege oich einen Spannungswert am Eingang des ADC an.
Komischerweise sprigt der Wert jetzt alle Sekunden hin und her, einmal 
stimmt er, einmal ist er falsch (abwechselnd immer die beiden Werte).

Überprüfen kann ich den Wert auf einem LCD bzw. einen DAC.
Die letzten 16 Bit der Variable wert sollte dann der Integerwert sein.


Vielen Dank im voraus
Werner

von Jörg X. (Gast)


Lesenswert?

die 'einfachste' Lösung wäre:
1
//...
2
uint32_t wert;
3
//lesen ...
4
wert = ((uint32_t)SPDR << 11);
5
//lesen...
6
wert |= ((uint32_t)SPDR << 3);
7
// lesen ...
8
wert |= (SPDR>>5);
9
//...
Aber bei deiner Lösung fehlen wahrscheinlich nur die casts (merke: man 
kein ein Byte nicht 11 Mal schieben ;) )

hth. Jörg

von Werner (Gast)


Lesenswert?

Danke für den Hinweis, war hat aber leider das Ergebnis nicht verändert.
(Lustigerweise gibt der Compiler eine Warnung aus wenn ich in einer 
anderen Routine um 17 Bit verschiebe)

Irgendwas mach ich falsch. Muss wohl nochmal das Datenblatt studieren.
Von den Bits her müsste es aber stimmen.
Werd wohl nochmal ein neues Bauteil probieren.


Werner

von Jörg X. (Gast)


Lesenswert?

kannst du mal ein kompilierbares beispiel posten/anhängen?
Nicht dass am Ende das:
> Komischerweise sprigt der Wert jetzt alle Sekunden hin und her, einmal
> stimmt er, einmal ist er falsch (abwechselnd immer die beiden Werte).
und das:
> wird automatisch einmal Channel0 und beim nächsten ansprechen
> Channel1 ausgelesen,
 zusammenhängen ;)

'ne genauere Fehlerbeschreibung wäre auch schön ;)

hth. Jörg

von Werner (Gast)


Angehängte Dateien:

Lesenswert?

Mit dem neuen Bauteil geht's gleich besser!

Jetzt machen beide Channels grundsätzlich das richtige.
Allerdings steht im Datenblatt dass automatisch beim ersten Aufruf des 
ADCs, Kanal 0 ausgelesen wird, beim 2 Aufruf Kanal 1, dann 0, dann 1, 
immer abwechselnd (Ping Pong mäßig).
Das tut es bei mir noch nicht. Ich lese in meiner Hauptroutine 2x den 
ADC aus, dann schreib ich ihn aufs Display. Blöderweise wird in Zeile 1 
einmal der Wert von Kanal 0, einmal von Kanal 1 angezeigt.
Eigentlich sollte jeder in einer eigenen Zeile dargestellt sein(siehe 
PRG).
Das heißt für mich er liest pro Zyklus nur einmal und nicht die zweimal 
wie es im Prg steht, entweder sind längere Pausen notwendig oder ...?

Über Potis lege ich Spannungen an den ADC zum testen.

Erklärung zum Programm:

Diese Routine wird alle Sekunden aufgerufen (meine Abtastung): 
ISR(TIMER1_COMPA_vect).

Von dort aus werden alle Routinen die benötigt werden aufgerufen.


mfg
Werner

von Werner (Gast)


Lesenswert?

Muss was korrigieren, genau genommen schreibt er lauter einsen in Bit 
15-0.
zeigt nämlich 65535 an, am Display in Zeile 2 an.
Das heißt er liest bei jedem Routinen aufruf (Abtastroutine) einmal den 
ADC aus. Darum toggelt der Wert in der ersten Zeile zw. Kanal 0 und 1 
herum.

Werner

von Jörg X. (Gast)


Lesenswert?

Laut Datenblatt (Diagramm, s. 8) wechselt der ADC den Kanal erst NACH 
dem auslesen. Und die nächste Wandlung dauert rund 150ms (t_conv, S. 5).

Stell doch die Timer-ISR auf die halbe Zeit und lass abwechselnd einen 
Kanal in die passende Zeile ausgeben.

(da deine ISR aber schon ziemlich lang ist, würde ich auf Job-Flag (oder 
sleep-Mode) umstellen: der Timerinterrupt setzt eine Variable/ein Bit, 
die main() fragt diese Variable ab und verarbeitet die Werte, sobald die 
Sekunde rum ist)

hth. Jörg

von Werner (Gast)


Lesenswert?

Danke
das hab ich mir auch schon gedacht mit Zeit halbieren.

Das richtige Programm kommt erst das ist nur ein kleiner Teil davon.
Es kommen noch ein paar Rechnungen hinzu, geht sich aber alles aus, hab 
ich schon getestet mit dem internen ADC.
Wird nämlich eine Temperaturgeregelte Stromregelung wo ein 
Temp.abhängiger Widerstand in einer Brückenschaltung ausgewertet wird, 
... .

Ich habs in der ISR gemacht damit mir kein anderer Interrupt die 
Routinen unterbricht (man kann zwar die Interrupts abstellen, aber naja, 
auch nicht sauber):-).

Auf alle Fälle nochmal vielen Dank für deine Hilfe
Werner

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.