mikrocontroller.net

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


Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

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

Autor: Jörg X. (Gast)
Datum:

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

hth. Jörg

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Werner (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

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.