Forum: Mikrocontroller und Digitale Elektronik 4 Potis an ATmega


von Loipe (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte gerne 4 Poti an einem ATMega betreiben und die Analogwerte im 
Programm verwenden. Allerdings habe ich das Problem, das mir die Werte 
nicht richtig eingelesen werden.
Wenn ich an einem Poti drehen, dann ändern sich auch die anderen Werte 
ein wenig.
Hab ich einen Denkfehler im Schaltungsaufbau, oder hab ich einen Fehler 
in der Programmierung.

Hier der Ausschnitt des Codes.

Die Funktion wird alle 1ms aufgerufen
1
uint16_t ADC_Read( uint8_t channel )
2
{
3
  // Kanal waehlen, ohne andere Bits zu beeinflußen
4
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
5
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
6
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
7
  }
8
  return ADCW;                    // ADC auslesen und zurückgeben
9
}
10
11
ISR(TIMER2_OVF_vect)                  // 1ms Takt
12
{
13
  switch (Analogkanal)
14
  {
15
    case 0:
16
      Poti_Fernsehpos = ADC_Read(0);
17
      Analogkanal += 1;
18
      break;
19
    case 1:
20
      Poti_Unten = ADC_Read(1);
21
      Analogkanal += 1;
22
      break;
23
    case 2:
24
      Poti_Kueche = ADC_Read(2);
25
      Analogkanal += 1;
26
      break;
27
    case 3:
28
      Poti_Esszimmer = ADC_Read(3);
29
      Analogkanal = 0;
30
      break;
31
  }
32
}
Gruß Alex

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Bis darauf, dass du den GND Pin 53 nicht angeschlossen hast, seh ich 
erst mal nichts schlimmes.

Ich denke, dein Problem hängt mit der Sample und Hold Stufe im ADC 
zusammen. Wenn du den Kanal wechselst, muss sich erst ein kleiner 
Kondensator in der S&H Stufe auf den neuen Spannungswert einstellen. Das 
dauert seine Zeit. Beginnst du zu früh damit, den ADC zu starten, dann 
ist der Kondensator noch nicht auf die neue Spannung eingestellt und du 
hast ein kleines 'Übersprechen'.

Ich würd die Sache anders aufziehen.
In der ISR würde ich die Reihenfolge umdrehen
1
ISR( ... )
2
{
3
  ADC starten und auf fertig warten
4
  Wert vom ADC holen und auf die richtige Variable verteilen
5
  Den Mutiplexer auf den nächsten Kanal einstellen
6
}

Auf die Art hat die S&H Stufe Zeit bis zum nächsten ISR Aufruf (nach 
1ms) um sich auf den neuen Kanal einzustellen. Die eigentliche Wandlung 
findet dann erst im nächsten ISR Aufruf statt. Bis dahin hat sich aber 
die ganze analoge Eingangsstufe schon auf die neue Spannung vom nächsten 
Poti eingestellt.

von spess53 (Gast)


Lesenswert?

Hi

Mit welchem Takt läuft dein ADC?

Außerdem dürfte die Belastung von ADREF mit 2,5k etwas zu hoch sein.

Ein 100n Kerko von den ADC-Eingängen nach Masse dürfte auch nicht 
schaden.

MfG Spess

von MWS (Gast)


Lesenswert?

Ist Analogkanal volatile deklariert? Ansonsten höchst ineffektiv, in der 
ISR auf das Ende der Wandlung zu warten. Sinnvoller wäre, den ADC 
freilaufend zu konfigurieren und den ADCC-Interrupt nutzen. Spart auch 
den Timer.

von Loipe (Gast)


Lesenswert?

Super für die schnell Hilfe,

den Pin 53 hab ich ja voll übersehen. Werde ich dann mal auf GND 
brücken.
Die Routine werde ich auch anpassen und dann hoffe ich es passt.
Ist es sinnvoll einen Kanal mehrere mal auszulesen und dann einen 
Mittelwert zu bilden, oder sind diese recht stabil. Ich habe 25 Gang 
Potis eingesetzt.

Gruß ALex

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Es schadet nicht, an AREF noch einen 100nF gegen Masse zu legen, um die 
Referenz stabiler und störunanfälliger zu machen.
Ein weiterer Trick, um den internen S&H Kondensator schnell umzuladen, 
ist es, an jeden AD Eingang noch einen externen C gegen Masse zu legen. 
Das kann z.B. ein 1nF-10nF sein. Dieser wird beim Umschalten des Mux 
rapide in den internen C geladen und verringert so das 'Mitziehen' von 
Nachbarkanälen.

Loipe schrieb:
> Ist es sinnvoll einen Kanal mehrere mal auszulesen und dann einen
> Mittelwert zu bilden, oder sind diese recht stabil

Eigentlich sollten sie nach den o.a. Massnahmen schon recht stabil sein. 
Ich mache meistens nur ein '2-fach Oversampling', d.h. ich addiere das 
neue Ergebnis zum vorigen Ergebnis und teile dann durch 2. Dann steht 
das alles recht stabil.

: Bearbeitet durch User
von Loipe (Gast)


Angehängte Dateien:

Lesenswert?

Ok, danke für den Tip.
Hab die Schaltung nochmal angepasst.
Ist das jetzt so in Ordnung

von Thomas E. (thomase)


Lesenswert?

Schalte deine Potis "oben" an VCC und miss auch gegen Vcc. Dann hast du 
auch kein Problem, dass dir was zusammenbricht. Eine besondere Referenz 
brauchst du nicht.Es werden nur Verhältnisse gemessen. Die sind bei 
jeder Spannung gleich.

mfg.

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Oh, das sehe ich erst jetzt: AREF kann auf keinen Fall eine Last von 
2,5K versorgen. Das ist viel zu viel Strom.
Du musst also entweder die Potis über einen Opamp als Spannungsfolger 
aus AREF speisen oder dich für z.B. Vcc als AREF entscheiden.
Edit: thomase war schneller :-)

: Bearbeitet durch User
von MWS (Gast)


Lesenswert?

Kann es sein, dass die Potis NUR an AREF gehen? Das klappt nicht, AREF 
kann nicht belastet werden.

von Loipe (Gast)


Lesenswert?

Und wenn ich 4 100k Potis verwende, kann ich dann AREF verwenden ?

Den ADC habe ich wie folgt Initialisiert.

void ADC_Init(void) {

  // die Versorgungsspannung AVcc als Refernz wählen:
  ADMUX = (1<<REFS0);
  // oder interne Referenzspannung als Referenz für den ADC wählen:
  // ADMUX = (1<<REFS1) | (1<<REFS0);

  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
  // schon auf 0, also single conversion
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man 
liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu 
lassen" */

  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der 
Konvertierung warten
  }
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
     Wandlung nicht übernommen. */
  (void) ADCW;
}


Mir ist gerade aufgefallen, dass ich gegen die falsche 
Versorgungsspannung messe. Ich habe im Programm AVCC eingestellt. Dort 
muss ich auf interne Referenz stellen, oder ???

von Thomas E. (thomase)


Lesenswert?

Loipe schrieb:
> Mir ist gerade aufgefallen, dass ich gegen die falsche
> Versorgungsspannung messe. Ich habe im Programm AVCC eingestellt. Dort
> muss ich auf interne Referenz stellen, oder ???

Nein, du brauchst keine extra Referenz. Nimm AVcc für die Potis und als 
Referenz.

mfg.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>ADCSRA = (1<<ADPS1) | (1<<ADPS0);

Das ist ein Prescaler von 8. Wenn du wirklich 14,7456MHz hast ist das 
ein ADC-Takt von 1,843 MHz. Das ist viel zu hoch. Setze den Prescaler 
auf 128.

MfG Spess

von Loipe (Gast)


Angehängte Dateien:

Lesenswert?

Hab die Schaltung nochmal angespasst. Stimmt das jetzt so.
Soll ich dann besser 10k oder 100k Potis verwenden ?

von Thomas E. (thomase)


Lesenswert?

Loipe schrieb:
> Soll ich dann besser 10k oder 100k Potis verwenden ?
Das ist jetzt egal. 100K verbraucht aber weniger Strom.

mfg.

von spess53 (Gast)


Lesenswert?

Hi

>Soll ich dann besser 10k oder 100k Potis verwenden ?

ATMEL empfiehlt als Quellimpedanz für die ADC-Eingänge <=10k.

MfG Spess

von Thomas E. (thomase)


Lesenswert?

spess53 schrieb:
> ATMEL empfiehlt als Quellimpedanz für die ADC-Eingänge <=10k.

Richtig. Lass die 10er drin.

mfg.

von spess53 (Gast)


Lesenswert?

Hi

Dann bleibt immer noch die Frage zum ADC-Takt.

MfG Spess

von Thomas E. (thomase)


Lesenswert?

spess53 schrieb:
> Dann bleibt immer noch die Frage zum ADC-Takt.
1
#if (F_CPU / 2) <= 200000
2
  #define ADC_DEFAULT_PRESCALER (1 << ADPS0)
3
#elif (F_CPU / 4) <= 200000
4
  #define ADC_DEFAULT_PRESCALER (1 << ADPS1)
5
#elif (F_CPU / 8) <= 200000
6
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS1) | (1 << ADPS0))
7
#elif (F_CPU / 16) <= 200000
8
  #define ADC_DEFAULT_PRESCALER (1 << ADPS2)
9
#elif (F_CPU / 32) <= 200000
10
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS2) | (1 << ADPS0))
11
#elif (F_CPU / 64) <= 200000
12
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS2) | (1 << ADPS1))
13
#elif (F_CPU / 128) <= 200000
14
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))
15
#endif

mfg.

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.