PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Propleme bei mehreren ADC Eingänge



Jackeder
21.04.2015, 18:19
Hallo zusammen,

ich versuche mich seit paar Tagen mit ADC Wandler.
Ich schreibe in C und nutze Atmelstudio 6. Zum testen habe ich mir ein kleines Kästchen mit einem ATmega8 und einem 20x4 Display gebastelt.

Vor ein paar Tagen habe ich einen alten analogen Joystick (boeder P-7 Joystick) auf dem Speicher gefunden und wollte diesen nun mit dem Atmega8 über ADC auslesen und vorerst auf dem LCD die Werte anzeigen lassen.
Allerdings musste ich ersteinmal paar Kabel umlöten, da die beiden 50Kohm Potis keine Masse hatten. 2. hab ich noch beide Tasten mit einem Pull-UP Wiederstand versehen.

Joystick ATmega8
X_Achse -- -- -- -- > PC1
Y_Achse -- -- -- -- > PC2
Taste A -- -- -- -- > PD2 (INT0)
Taste B -- -- -- -- > PD3 (INT1)

Tasten abfragen funktioniert super, nur die ADC_werte spinnen irgendiwe rum, wenn ich beide auslesen will. Einzeln auslesen ist ohne Proplem so wie ich das wünsche.
hier mal meine AT8adc.h Datie, denke mal das ich irgendwo einen Fehler habe oder es doch auf diese Weise nicht funktioniert.


/*
* AT8adc.h
*
* Created: 18.04.2015 17:29:41
* Author: Hendrik
*/


#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h> // für dtostrf

// Variablen

unsigned long ADC_result;
double XAchse; // double wegen return "XAchse" ins Hauptprogramm
double YAchse;
volatile int x;
char xstr[4];
char ystr[4];

void initADC (void)
{
// ADC Konfigurieren

ADCSRA |= (1<<ADEN); // ADC-Wandler aktivieren
ADCSRA |= (1<<ADPS1);// Teiler 64 (zw. 50khz und 200khz)
ADCSRA |= (1<<ADPS2);// Teiler 64 (8000000hz / 64 = 125000hz

}
// X-Achse messen am ADC1
double getADC1 (void)
{
ADMUX = 0x01; // ADC1 selekt
ADMUX |= (1<<REFS0); // Referenzspannung = VCC = 5Volt
ADC_result = 0;

//for (unsigned int i=0; i<8; i++)
//{
ADCSRA |= (1<<ADSC); // single conversion
while (ADCSRA & (1<<ADSC)) // warten bis ADC fertig
ADC_result = ADCW;
//ADC_result += ADCW; // adieren der conversion
//}
XAchse = ADC_result;
//XAchse = ADC_result/8; // 8mal messen, addieren und durch 8 teilen für Mittelwert
dtostrf(XAchse, 4, 0, xstr);
lcd_setcursor(5,2);
lcd_string(xstr);
x = XAchse-512; // umwandeln in -512 > 0 < +512
lcd_setcursor(15,2);
dtostrf(x,4,0,xstr);
lcd_string(xstr);
return XAchse;
}
// Y-Achse messen am ADC2
double getADC2 (void)
{
ADMUX = 0x02; // ADC2 selekt
ADMUX |= (1<<REFS0); // Referenzspannung = VCC = 5Volt
ADC_result = 0; // ADC_result zurücksetzen

//for (unsigned int i=0; i<8; i++)
//{
ADCSRA |= (1<<ADSC); // single conversion
while (ADCSRA & (1<<ADSC)) // warten bis ADC fertig
ADC_result = ADCW; // adieren der conversion
//}
YAchse = ADC_result; // 8mal messen, addieren und durch 8 teilen für Mittelwert
dtostrf(YAchse, 4, 0, ystr);
lcd_setcursor(5,3);
lcd_string(ystr);
return YAchse;
}

Im Main Programm frage ich dann wie folgt ab



while(1)
{
getADC1();
_delay_ms(300);
getADC2();
_delay_ms(300);
}


Die 300 ms hab ich nur rein genommen um zu sehen was das Proplem sein könnte. Lässt sich jetzt schlecht erklären, aber die beiden werte springen wild hin und her und lassen sich nicht zur jeweiligen Achse zuordnen.

Würde mich freuen wenn jemand mir auf die Sprünge helfen könnte und ne Idee hat.

Vielen Dank Hendrik

seite5
21.04.2015, 18:51
Hi,
evtl. ein kleines delay zwischen MUX-Einstellung und ADC-Start.
mfg
Achim

Jackeder
21.04.2015, 19:14
gerade versucht, _delay_MS(50); eingefügt vor Single Messung ... leider Ergebnis das gleiche

""fast wie im wahren Leben, jeder macht was er will und keiner was er soll""

Unregistriert
21.04.2015, 19:25
AVCC angeschlossen?

Jackeder
21.04.2015, 19:41
ja hab ich , sag ja , einzeln messen von ADC1 oder ADC2 geht und wird auch richtig angezeigt, beide zusammen messen wirds Chaos.
Wenn ich Joystick Hebel nach oben bewege liegen am PC2(ADC2) 5Volt an, auf dem LCD zeigt ADC2: 1023 an, was ja richtig ist, allerdings wechselt ADC1: auch ständig von 492 auf 1023 wieder auf 492....

RoboHolIC
21.04.2015, 21:55
ADC-Eingänge der Microcontroller sind oft relativ nierderohmig, im Bereich weniger Kiloohm. Ein 50k-Poti als Quelle ist da eigentlich ungünstig.
Abhilfe ist einerseits möglich durch das bereits beschriebene Delay zwischen MUX-Umschaltung und Start der Messung, um ausreichend Samplingzeit zu gewähren.
Zum anderen kann ein Pufferkondensator pro Kanal, der sich beständig auf den aktuellen Spannungswert des Kanals auflädt, die effenktive Impedanz der Quelle merklich reduzieren, weil er binnen kurzer Zeit den internen Samplingkondensator des ADC auf (fast) gleichen Pegel bringen kann.

Holomino
22.04.2015, 07:39
Ich frag mal ganz dumm:
while (ADCSRA & (1<<ADSC)) // warten bis ADC fertig

muss da nicht ein Semikolon hinter? Soll doch warten, bis ADSC zurückgesetzt ist und dann erst die folgende Codezeile zum Lesen des AD-Wertes ausführen. Ohne Semikolon wird doch die folgende Zeile oder der durch {} eingefasste folgende Block mit dem while ausgeführt, oder bin ich jetzt ganz im falschen Film?

damfino
22.04.2015, 09:31
Du musst mal die Variablentypen gleich ziehen:
es wird ein "double" mit "int" und "unsigned long" vermischt (Fließkommazahlen mit vorzeichenbehafteten Integer mit nicht vorzeichenbehafteten Integer), da sollte der Compiler doch schon einige Warnings anzeigen.
Meines wissens ist ein "int" nicht exakt definiert, es kann je nach Compiler ein short oder sonstwas sein.

Von der Rechengenauigkeit her kommst mit "short" Variablen oder "int16_t" aus, die haben einen Zahlenbereich von −32.768 bis +32.767.

dtostrf: schaut etwas knapp aus, probier mal mehr Speicher bereitzustellen (zB char ystr[6] )

Add: AT8adc.h
*.h bedeutet einen Header Datei, da steht schon sicher mal kein Code drinnen, sondern nur zB Definitionen von Funktionen: "extern void lcd_home(void);"
erst in einer *.c Datei steht dann die genaue Funktion:
void lcd_home(void)
{
lcd_command(1<<LCD_HOME);
}

So gesehen sollte das Programm nie funktionieren, oder ist es doch unter *.c gespeichert?

LG!

Klebwax
22.04.2015, 10:35
Ich frag mal ganz dumm:
while (ADCSRA & (1<<ADSC)) // warten bis ADC fertig

muss da nicht ein Semikolon hinter? Soll doch warten, bis ADSC zurückgesetzt ist und dann erst die folgende Codezeile zum Lesen des AD-Wertes ausführen. Ohne Semikolon wird doch die folgende Zeile oder der durch {} eingefasste folgende Block mit dem while ausgeführt, oder bin ich jetzt ganz im falschen Film?

Da rächt sich die "dumme" Angewohnheit, leere while-Schleifen in einer Zeile und ohne geschweifte Klammern zu schreiben.


ADCSRA |= (1 << ADSC); // single conversion
while (ADCSRA & (1 << ADSC)) // warten bis ADC fertig
ADC_result = ADCW;
//ADC_result += ADCW;
da ist schlecht zu erkennen, was da abgeht, da nun auch noch der Kommentar dicht dahinter steht. Auch beim Lesen ist schlecht zu erkennen, daß es um einen Loop geht, insbesondere wenn man in ein paar Wochen an den Code noch mal ran muß. Und ohne Klammern wird da ganz schnell mal eine Semikolon reingeschrieben, obwohl die nächste Zeile eigentlich in die Schleife gehört. wenn man das grundsätzlich so schreibt:

ADCSRA |= (1 << ADSC); // single conversion
while (ADCSRA & (1 << ADSC)) { // warten bis ADC fertig
;
}
ADC_result = ADCW;
//ADC_result += ADCW;

passieren nicht so leicht Fehler und man kann ganz schnell noch einen Print zum Debuggen oder einen Nop für einen Breakpoint einfügen und auch wieder herauskommentieren, ohne daß die Programmstruktur kaput geht.

In diesem Fall wird hier während der Konvertierung dauernd das Resultat ausgelesen, ob das dem ADC gefällt, weiß ich nicht.


]Meines wissens ist ein "int" nicht exakt definiert, es kann je nach Compiler ein short oder sonstwas sein.

Es ist schon definiert, mindestens 16 Bit. Wenn aber die CPU schneller mit 64 Bit umgehen kann, kanns auch mal 64 Bit sein. Short ist da schon eher nicht definiert. Für einen Joystick reicht aber auch ein int8_t, selbst die Auflösung kann man mit den Fingern gar nicht erreichen.

MfG Klebwax

Jackeder
22.04.2015, 14:54
Ich frag mal ganz dumm:
while (ADCSRA & (1<<ADSC)) // warten bis ADC fertig

muss da nicht ein Semikolon hinter? Soll doch warten, bis ADSC zurückgesetzt ist und dann erst die folgende Codezeile zum Lesen des AD-Wertes ausführen. Ohne Semikolon wird doch die folgende Zeile oder der durch {} eingefasste folgende Block mit dem while ausgeführt, oder bin ich jetzt ganz im falschen Film?


stimmt das Semikolon hab ich vergessen, sorry und danke ... wundert mich das ATmelstudio da nicht gemeckert hat ^^

Peter(TOO)
22.04.2015, 15:00
Hallo,

Add: AT8adc.h
*.h bedeutet einen Header Datei, da steht schon sicher mal kein Code drinnen, sondern nur zB Definitionen von Funktionen: "extern void lcd_home(void);"
erst in einer *.c Datei steht dann die genaue Funktion:
void lcd_home(void)
{
lcd_command(1<<LCD_HOME);
}


Dem Compiler ist das .h oder .c total egal!
Das ist nur eine Konvention und man kann als Dateiendung verwenden was das Dateisystem zu lässt.

Genau genommen werden die #include-Dateien vom Präprozessor verwaltet.
Im Prinzip macht dieser eine Art "search & replace", überall wo er #include findet löscht er dieses und kopiert die angegebene Datei rein.
Der Compiler bekommt dann diese aufgeblasene Datei gefüttert.

Früher, als der Präprozessor noch ein eigenständiges Programm war, hat man den gerne auch z.B. in der Textverarbeitung eingesetzt.
Man konnte dann einzelne Kapitel als eigene Dateien verwalten und mit den entsprechenden #includes dann das Buch zusammenstellen.

MfG Peter(TOO)

Jackeder
22.04.2015, 15:13
Hallo erstmal und danke für die ganzen Antworten.

Muss mich aber erstmal berichtigen, denn in dem Joystick sind nicht 50kohm Potis eingebaut sondern 100kohm Potis.
Denke mal das damit auch mein Fehler sich erklären lässt, werde erstmal 2 10Kohm Potis verdrahten und diese dann messen, wenn das funktioniert ohne Propleme liegts am Joystick.

- - - Aktualisiert - - -


Du musst mal die Variablentypen gleich ziehen:
es wird ein "double" mit "int" und "unsigned long" vermischt (Fließkommazahlen mit vorzeichenbehafteten Integer mit nicht vorzeichenbehafteten Integer), da sollte der Compiler doch schon einige Warnings anzeigen.
Meines wissens ist ein "int" nicht exakt definiert, es kann je nach Compiler ein short oder sonstwas sein.

Von der Rechengenauigkeit her kommst mit "short" Variablen oder "int16_t" aus, die haben einen Zahlenbereich von −32.768 bis +32.767.

dtostrf: schaut etwas knapp aus, probier mal mehr Speicher bereitzustellen (zB char ystr[6] )

Add: AT8adc.h
*.h bedeutet einen Header Datei, da steht schon sicher mal kein Code drinnen, sondern nur zB Definitionen von Funktionen: "extern void lcd_home(void);"
erst in einer *.c Datei steht dann die genaue Funktion:
void lcd_home(void)
{
lcd_command(1<<LCD_HOME);
}

So gesehen sollte das Programm nie funktionieren, oder ist es doch unter *.c gespeichert?

LG!

natürlich habe ich alles in einer Joystick.c datei gespeichert, aber da ich alles was den ADC angeht in der AT8adc.H geschrieben habe hab ich auch nur diese hier aufgelistet, kann aber gerne auch noch meine *.c vorzeigen, fals es erwünscht wird.

Klebwax
22.04.2015, 15:40
stimmt das Semikolon hab ich vergessen, sorry und danke ... wundert mich das ATmelstudio da nicht gemeckert hat ^^

Warum sollte der Compiler meckern? Syntaktisch ist das Programm ja in Ordnung: wenn man keine Klammern setzt, wird die auf das while() folgende Anweisung solange ausgeführt, bis der Audruck im while() false wird. Wenn da nichts weiter passieren soll, muß dort das C-Equivalent eines NOPs stehen, nämlich das Semikolon. In deinem Code steht aber das Lesen des ADC-Resultats, und das macht er dann auch im Loop. Das ist zwar sinnloser aber gültiger Code.

Eine Hilfe ist, sich den Code automatisch von der IDE formatieren zu lassen. An der Einrückung hätte man das sofort gesehen.

MfG Klebwax

Jackeder
22.04.2015, 17:02
Es lag wirklich an dem Semikolon, messe beide ADC Eingänge im Abstand von 50ms ohne Störungen. Somit würde ich meinen Thema als erledigt, danke .

naja Atmelstudio meckert bei mir meist wenn ich irgendwo in einer Zeile ein Semikolon vergesse zum Satzende, scheint bei einer While-Schleife warscheinlich eine Ausnahme zu sein ... wieder was dazugelernt

Klebwax
22.04.2015, 17:22
naja Atmelstudio meckert bei mir meist wenn ich irgendwo in einer Zeile ein Semikolon vergesse zum Satzende, scheint bei einer While-Schleife warscheinlich eine Ausnahme zu sein ... wieder was dazugelernt

Du hast offensichtlich meinen Text nicht gelesen. Das Semikolon ist ja da:




ADCSRA |= (1<<ADSC); // single conversion
while (ADCSRA & (1<<ADSC)) // warten bis ADC fertig
ADC_result = ADCW; <------------- Semikolon
//ADC_result += ADCW;

Nur steht es leider hinter der Anweisung, die ADCW liest. Ein Semikolon ohne Anweisung davor ist ein NOP in C, und das wolltest du.

MfG Klebwax

Holomino
22.04.2015, 18:04
Jo, C kann schon ganz schön fies sein;)

Wenn's jetzt rennt, is' ja schön. Hab ich meine Pfadfindermission für heute erfüllt.

Jackeder
23.04.2015, 19:08
hey
sorry Klebwax,aber so langsam verstehe ich worauf du hinaus willst
ADCSRA |= (1<<ADSC); // single conversion
while (ADCSRA & (1<<ADSC)); <-- das Semikolon hat gefehlt, nach dem ich es gesetzt habe geht alles
{
// warten bis ADC fertig (also wenn ichs so schreiben würde muss ich das Semikolon hinter dem Wihle() weglassen)
}
ADC_result = ADCW; <------------- Semikolon
//ADC_result += ADCW;

Klebwax
23.04.2015, 21:32
hey
sorry Klebwax,aber so langsam verstehe ich worauf du hinaus willst
ADCSRA |= (1<<ADSC); // single conversion
while (ADCSRA & (1<<ADSC)); <-- das Semikolon hat gefehlt, nach dem ich es gesetzt habe geht alles
{
// warten bis ADC fertig (also wenn ichs so schreiben würde muss ich das Semikolon hinter dem Wihle() weglassen)
}
ADC_result = ADCW; <------------- Semikolon
//ADC_result += ADCW;

Ich schreib das dann noch etwas anders, so wie das K&R mal angefangen haben: die öffnende Klammer auf die selbe Zeile wie das if oder while, dann einrücken, und die schließende auf die selbe Ebene wie das while. Die automatische Formatierung meiner IDE ist auch auf dieses Format eingestellt. Beispiel:


while (Bedingung) {
Anweisung;
print(....); // zum Debuggen, kann schnell rauskommentiert werden
}

/* die Anweisung kann auch leer sein, trotzdem sieht man auf den ersten Blick
dass hier eine Schleife ist */

while (das Reday-Bit ist false) {
; // einfach warten
// print(...); // Debug, auskommentiert oder gelöscht
Nop(); // später eingefügt, um einen Breakpoint zu setzen, wird wieder gelöscht
}

// am Ende sieht es so aus

while (Bedingung) { // warten bis ready
; // Einrückung erkennt man auch beim schnellen Lesen / Scrollen
}

// das ist zwar equivalent zu
while (Bedingung) ;
/* hier rutscht einem schnell ein fehlendes Semikolon durch und der Compiler kann einem da nicht helfen. */



Das Ziel ist, daß man Code wie einen "Text" lesen kann, und sofort versteht, was gemeint ist. C ist zwar davon meilenweit entfernt, aber ein wenig kann man sich daran orientieren.

MfG Klebwax