Hallo zusammen, versuche zur Zeit eine Messwerterfassung mit dem ATmeag16A zu realisieren. Dabei versuche ich mittels Poti (10k) die Eingangsspannung von 0 .... 5 V zu simmulieren um später z.B. Drucksensoren anschließen zu können. Mit drei Poti's gehe ich auf die ADC - Eingänge ADC0, ADC1, ADC2. 2 der 3 Kanäle funktionieren auch soweit ganz gut aber bei einem der 3 treten ziemlich starke Schwankungen auf, obwohl ich am Eingang eine konstante Poti - Spannung anliegen habe. Habe schon versucht einen 100nF Kondensator zwischen Analogeingang (ADC0) und GND zu setzen, kein Erfolg. Auch sonst habe ich einen weiteren 100nF Kondensator an AREF und zwischen AVCC und GND, kein Erfolg. Kann mir vielleicht jemand helfen bei meinem Problem? Danke schonmal im Voraus. Gruß David
Ist AVCC konstant? Du solltest auch vermeiden, irgendwelche I/O Pins zu verändern, während der ADC eine Messung durchführt. Was meinst Du mit "schwanken"? Wie schnell und wie stark schwankt es?
David schrieb: > aber bei einem der 3 treten ziemlich starke Schwankungen auf Bei welchem? Und warum schreibst du da so einen Spaghetti-Code? Du machst dreimal fast das selbe, da wäre ein Schleife oder ein Zustandsautomat die richtige Lösung. Und wenn du jetzt sagst "Zustandsautomat? Zu kompliziert!" dann lass dir gesagt sein: du hast schon einen gebaut. Aber eben einen umständlichen:
1 | if (ADMUX & (1<<MUX0)) |
2 | :
|
3 | ADMUX &= ~(1<<MUX0); |
4 | ADMUX |= (1<<MUX1); |
5 | }
|
6 | else
|
7 | {
|
8 | if (ADMUX & (1<<MUX1)) |
9 | :
|
10 | ADMUX &= ~(1<<MUX0); |
11 | ADMUX &= ~(1<<MUX1); |
12 | ADMUX |= (1<<MUX2); |
13 | |
14 | }
|
15 | else
|
16 | {
|
17 | if(ADMUX & (1<<MUX2)) |
18 | :
|
19 | ADMUX &= ~(1<<MUX2); |
20 | ADMUX |= (1<<MUX0); |
21 | _delay_ms(100); |
22 | }
|
23 | }
|
24 | }
|
Das ist ein Automat, weil bei jedem Schleifendurchlauf anhand der "Zustandvariablen" ADMUX ausgesucht wird, welcher Kanal jetzt gewandelt werden soll. BTW, das finde ich allerliebst:
1 | if (ADMUX & (1<<MUX0)) // erster wert jeder Wandlung verwerfen, damit sich ADC Multiplexer einschwingen kann |
Leider hast du da offenbar das Datenblatt nicht ganz oder nicht an der richtigen Stelle gelesen. Das wäre ja ein total vermurkster uC, wenn man nach dem Umschalten des MUX jedesmal eine Wandlung wegwerfen müsste...
David schrieb: > Eingänge ADC0, ADC1, ADC2. Guck ins Datenblatt. Da ist eine Tabelle drin. Da siehst du, was du dafür einstellen musst. Und was du dafür bekommst: ADMUX &= ~(1<<MUX0); ADMUX &= ~(1<<MUX1); ADMUX |= (1<<MUX2); Kleiner Tip: MUX2 wird erst ab ADC4 interessant. mfg.
Also das Problem mit dem Schwanken habe ich denke ich hinbekommen bzw. meinen Fehler erkannt: Es lag an MUX 2 , danke Thomas ;) Mit dem Automaten verstehe ich allerdings noch nicht so ganz, wie kann ich denn die ADC - Kanäle anders ansteuern? Mein Porblem was ich jetzt habe ist diese hier: if((ADMUX & (1<<MUX0)) && (ADMUX & (1<<MUX1))) . . . Was daran ist falsch? Sorry habe noch nicht so viel Ahnung von µC, bin noch nicht so lange dabei.
Falsch ist das nicht, nur unelegant. Weil du mehrmals fast identischen Code wiederholt hast. Wirklich elegant wäre, den ADC Interrupt zu nutzen:
1 | #include <avr/interrupt.h> |
2 | #include <stdint.h> |
3 | |
4 | // Werte vom ADC, für die vier Kanäle
|
5 | static volatile uint16_t adc_value[4]; |
6 | |
7 | // Unterbrechung vom ADC
|
8 | ISR(ADC_vect) { |
9 | // Lese den ADC aus
|
10 | uint8_t channel = ADMUX & 7; |
11 | adc_value[channel] = ADC; |
12 | |
13 | // Schalte auf den nächsten Kanal um.
|
14 | if (++channel > 3) { |
15 | channel=0; |
16 | }
|
17 | ADMUX=(ADMUX & ~7) | channel; |
18 | |
19 | // Starte nächste Messung
|
20 | ADCSRA |= (1<<ADSC); |
21 | }
|
22 | |
23 | void main() { |
24 | // Initialisiere den ADC
|
25 | ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE) | (1<<ADSC); |
26 | ADMUX = REF_VCC; |
27 | |
28 | // Erlaube Unterbrechungen
|
29 | sei(); |
30 | |
31 | uint8_t channel=0; |
32 | while(1) { |
33 | _delay_ms(100); |
34 | // gib den Wert von adc_value[channel] hier aus
|
35 | ...
|
36 | // Zum nächsten Kanal wechseln
|
37 | if (++channel > 3) { |
38 | channel=0; |
39 | }
|
40 | }
|
41 | }
|
Hi >Wirklich elegant wäre, den ADC Interrupt zu nutzen: Noch eleganter wäre es mit Timergesteuertem Autotrigger. > if (++channel > 3) { > channel=0; Er braucht aber nur Channel 0,1 und 2. @David (Gast) Den Prescaler würde ich auf 128 erhöhen. Damit liegst du dann im empfohlenen Bereich von 50...200kHz für den ADC-Takt. MfG Spess
> Er braucht aber nur Channel 0,1 und 2.
Oh, hab ich übersehen. Aber das macht nichts, mein Codebeispiel ist ja
leicht auf beliebige Anzahl von Kanälen umstellbar.
Perfekt! @Stefan Frings Danke das hat echt geholfen. Der Programmcode ist jetzt um einiges zusammengeschrumpft. Danke euch allen.
Eine letzte Frage vielleicht noch: Kann mir jemand erklären was das hier aufgeschlüsselt bedeutet? ADMUX=(ADMUX & ~7) | channel; Danke im Voraus. Gruß David
David schrieb: > Danke das hat echt geholfen. DAS würde mir zu denken geben. Denn es wurde ja gar nicht dein Problem behoben, sondern dein Code überarbeitet... David schrieb: > ADMUX=(ADMUX & ~7) | channel; Da werden zuerst 3 Bits gelöscht ("und nicht"), anschließend der channel gesetzt ("oder"). Siehe Bitmanipulation
> ADMUX=(ADMUX & ~7) | channel;
Oder in deutsch formuliert: Stelle die Kanalnummer ein und lasse dabei
die anderen Bits in dem ADMUX Register unverändert.
Stefan Frings schrieb: > ADMUX=(ADMUX & ~7) | channel; Wieso hab ich nicht selber dran gedacht m( Danke dir! Schon lustig, wie man per Zufall die Lösung zu nem Problem findet, an dem man vor ner Weile gegrübelt hat.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.