Forum: Mikrocontroller und Digitale Elektronik Analogmessung mehrerer Kanäle hinterteinander, überall gl. Werte?


von Matthias H. (maethes26)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
ich verzeifle gerade daran mehrere Analogeingänge hintereinander mit der 
Funktion aus dem Tutorial abzufragen. Auf meinem LCD-Display gibt er mir 
in jeder Zeile die gleichen Werte aus.

Habt ihr eine Idee warum? Ich lese doch jedes Mal einen anderen Kanal 
ein.


Über einen Tipp wäre ich Euch sehr dankbar.
Ich habe den gleichen Code auch zusätzlich als Datei angehängt.

Viele Grüße und noch einen schönen Sonntag wünscht Matthias.



#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>


#include "E:\Eigene Dateien\Dropbox\Atmel\AVR 
Programme\1aVorlagen\lcd_tools_h.h"



/* ADC initialisieren */
void ADC_ini(void)
{

DDRA=0b00000000;   //PORT muss Eingang sein
PORTA=0b10000000;  //Port muss low seink, oder
uint16_t result=0;


ADMUX |= (1<<REFS0) ;     // interne Spannung nutzen
ADCSRA = (1<<ADPS2) | (1<<ADPS0);      // Frequenzvorteiler: setzen auf 
32 (4 MHz / 32 = 125 kHz) und ADC aktivieren

ADCSRA |= (1<<ADEN);                  // ADC aktivieren


ADCSRA |= (1<<ADSC);                // eine ADC-Wandlung und danach wird 
das Bit wieder auf Null gesetz
while (ADCSRA & (1<<ADSC) )
result=ADCW;

//ADCSRA |= (1<<ADATE);  //free running mode aktivieren
}



/* ADC Einzelmessung */
uint16_t ADC_Read( uint8_t channel )
{
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);

 ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
 while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung 
warten
  return ADCW;                    // ADC auslesen und zurückgeben
}


/* ADC Mehrfachmessung mit Mittelwertbbildung */
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average )
{

 uint32_t result = 0;
 uint8_t i = 0;

  for (i=0; i < average; ++i )
    result += ADC_Read( channel );

  return (uint16_t)( result / average );

 return ADCW;

}





int main(void)
{
uint16_t adcval;


lcd_ini();
ADC_ini();



lcd_writetext(1,1, "Kanal_0: ") ;
lcd_writetext(2,1, "Kanal_1") ;
lcd_writetext(3,1, "Kanal_2: ") ;
lcd_writetext(4,1, "Kanal_3") ;


while( 1 )
 {
 adcval = ADC_Read(0);   lcd_writezahl(1,9, adcval);
 adcval = ADC_Read(1);   lcd_writezahl(2,9, adcval);
 adcval = ADC_Read(2);   lcd_writezahl(3,9, adcval);
 adcval = ADC_Read(3);   lcd_writezahl(4,9, adcval);



  }



return 0;

}

von Jens Strauss (Gast)


Lesenswert?

Bekommst du auch die gleichen Werte angezeigt, wenn du die Funktion 
ADC_Read_Avg nutzt?

Funktionieren die LCD Funktionen sonst ohne Probleme?

Stimmt der Wert des ADC in etwa, der angezeigt wird? Kannst du das 
nachvollziehen? Ist wahrscheinlich der Wert von Channel 0 oder?
1
DDRA=0b00000000;   //PORT muss Eingang sein
2
PORTA=0b10000000;  //Port muss low seink, oder
Sind alle ADC Kanäle an PortA? Warum setzt du die Bits von PortA, sind 
doch Eingänge...

Gruß Jens

von Martin (Gast)


Lesenswert?

Nach der Kanalumschaltung solltest du der Analogelektronik eine Chance 
geben, sich auf die neue Spannung einzustellen, bevor die Wandlung 
gestartet wird.

von Matthias L. (Gast)


Lesenswert?

..oder nach der Messung den Kanal auslesen und anhand dessen zuordnen..

von Stefan T. (tommie)


Lesenswert?

Gibt es dazu Faustregeln, wie lange die Elektronik braucht, um sich auf 
den neuen Kanal einzustellen? Ich habe momentan das gleiche Problem:

Ich habe mir diese Schaltung hier nachgebaut 
http://yveslebrac.blogspot.com/2008/10/cheapest-dual-trace-scope-in-galaxy.html, 
schreibe die Firm- und Software dazu jedoch neu. Ich frage regelmäßig 
die beiden ADC-Eingänge ab und habe dabei festgestellt, dass die 
Messwerte ineinander "überbluten"; lege ich den einen Anschluss auf VCC 
und den anderen auf GND, erhalte ich nicht wie erwartet 255 und 0 als 
Messwerte (ich verwerte nur 8 Bit), sondern
ungefähr Werte um 130 herum. Der Code für den ATTiny85 (mit vUSB) sieht 
folgendermaßen aus:
1
        /* configure ADC */
2
        ADMUX = (1<<ADLAR);
3
        ADCSRA = (1<<ADEN);
Hier die eigentliche sample-Routine:
1
static void sample_data(uint8_t mux, uint8_t i) {
2
        ADMUX &= ~(1<<MUX3 | 1<<MUX2 | 1<<MUX1 | 1<<MUX0);
3
        ADMUX |= mux;
4
        _delay_us(50);
5
        ADCSRA |= 1<<ADSC;
6
        while (ADCSRA & (1<<ADSC)) { /* wait for conversion to finish */ }
7
        samples[i] = ADCH;
8
}

Timergesteuert rufe ich dann irgendwann die Funktion zweimal auf:
1
        /* sample PB3 (yellow) */
2
        sample_data((1<<MUX1 | 1<<MUX0), 2*i+0);
3
        /* sample PB4 (green) */
4
        sample_data((1<<MUX1 | 0<<MUX0), 2*i+1);
Ohne das _delay_us(50) bluten die Messwert-Paare ineinander über, "50" 
ist jetzt einfach willkürlich gewählt - gibt es da konkrete Anweisungen, 
wie der Wert zu wählen ist? Wie würde sich der ADC im Freerunning-Mode 
verhalten?

von N. Müller (Gast)


Lesenswert?

Ich mache sowas immer über die ISR.
Dabei lass ich die erste ADC-Messung von einem Timer triggern. Nach 
erfolgter Messung wird der Wert in einem Array o.ä. abgespeichert und 
gleich die Messung für den nächsten Kanal gestartet.Das wird wiederholt 
bis alle Kanäle gemessen wurden.Pausen zwischen den Messungen gibt es 
nicht. Funktioniert tadellos.

Grüße

von Matthias H. (maethes26)


Lesenswert?

Hallo zusammen,

danke für Eure Beteiligung, mir bei meinem Problem zu helfen.

Ich bin der Sache ein interessantes Stück näher gekommen.

Ich habe es jetzt geschafft den ADC im Free running modus zu betrieben 
und mir von einem!!! ausgewählten Kanal die Werte ausgeben zu lassen.

Sobal ich aber mehr als einen Kanal nur im free running modus !!! wähle 
und beide auf dem LCD ausgebe,

dann sind die beiden Werte vertauscht!!!

Kanal 1 zeigt dann den Wert von Kanal 2 an
Kanal 2 zeigt dann den Werrt von Kanal 1 an!!!!!

ich habe den Fehler ebend gefunden!!

Ich takte den Prozessor mit 4MHZ und mache im Augenblick nur diese 
Mess-Testschleife!!

uint16_t ADC_Read_free_running_mode( uint8_t channel )
{
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
 _delay_us(300);
  return ADCW;                    // ADC auslesen und zurückgeben
}


ich mußte  _delay_us(300); einfügen und dann klappte es!!!

In einem normalen Programm kann ihc vielleicht auf diese _delay routine 
verzichten, weil der Prozessor noch andere sachen macht.

bin ich mit diesen 300us =0,3ms im free running mode immer noch 
schneller als im single mode??

denn darum ging es mir Zeit zu gewinnen.




Viele Grüße,

Matthias.

von Matthias H. (maethes26)


Lesenswert?

Martin schrieb:
> Nach der Kanalumschaltung solltest du der Analogelektronik eine Chance
> geben, sich auf die neue Spannung einzustellen, bevor die Wandlung
> gestartet wird.

Hallo Martin,

ja genau das war der Fehler. Habe nicht lang genug nach der 
Kanalumschaltung gewartet

300us Zeit haben gereicht, jetzt geht es.

von Uwe (de0508)


Lesenswert?

Hallo !

hast du das Datenblatt zu deinem AVR gelesen ?

Beim atMega88 steht das auf Seite 248
Table 23-1. "ADC Conversion Time"

Dort steht dass nach jedem Kanalwechsel ein Dummyread der ersten ADC 
Wandlung gemacht werden muss.

Ich habe gerade heute eine neue 64-Fach Oversample Version mit Trigger 
über Timer1 COMPB geschrieben.

Das sieht dann bei mir so aus:
1
#define ADC_PRESCALE    (1<<ADPS2^1<<ADPS1^1<<ADPS0) // wird abhängig von F_CPU berechnet !
2
#define ADC_TRIGGER    (1<<ADTS2^1<<ADTS0) // Timer1 COMP_B Interrupt
3
4
EMPTY_INTERRUPT(TIMER1_COMPB_vect);
5
6
void init_adc(uint8_t adc)
7
{
8
 // Analog Comparator Disable
9
  ACSR = 1<<ACD;
10
11
  // Digital Input Disable Register 0
12
  DIDR0 = 1<<(adc&0x07);
13
14
  // AVCC with external capacitor at AREF pin
15
  ADMUX = 0<<REFS1 ^ 1<<REFS0 ^ (adc&0x07);
16
17
  // ADCSRB = 0;
18
  ADCSRA = 1<<ADEN ^ 1<<ADSC ^ ADC_PRESCALE;
19
20
 /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, 
21
    man liest also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" 
22
  */
23
  loop_until_bit_is_not_set(ADCSRA, ADSC);
24
  (void)ADCW;
25
26
  // ADC Trigger
27
  ADCSRB = ADC_TRIGGER;
28
29
  // ADC Interrupt Enable
30
  ADCSRA = 1<<ADEN ^ 1<<ADSC ^ 1<<ADATE ^ 1<<ADIE ^ ADC_PRESCALE;
31
}
32
33
#define ADC_AVG_COUNT 64
34
35
ISR(ADC_vect, ISR_NOBLOCK)    // ADC Conversion Complete
36
{
37
  extern uint16_t * adc_result_ptr; // Zeiger auf der Ergebis
38
  extern bit_t adc_new_result; // bool_t Bit Variable
39
40
  static uint8_t adc_count = 0;
41
  static uint16_t adc_avg = 0;
42
43
  adc_avg += ADCW;
44
45
  if (++adc_count == ADC_AVG_COUNT) {
46
    adc_count = 0;
47
48
    if (!adc_new_result) {    // Ergebnis wurde gelesen
49
      *adc_result_ptr = adc_avg / ADC_AVG_COUNT;
50
      adc_new_result = 1;
51
    }
52
    adc_avg = 0;
53
  }
54
}

von Matthias L. (Gast)


Lesenswert?


von Matthias H. (maethes26)


Lesenswert?

Hallo zusammen,

dake für Eure Tipps.

Ich habe leider erst wieder am Wochenende Zeit diese durch zuarbeiten.

Ich melde mich dann ggf. nochmal bei Euch.

Viele Grüße,

Matthias.

von Matthias H. (maethes26)


Lesenswert?

Matthias Lipinsky schrieb:
> ..oder nach der Messung den Kanal auslesen und anhand dessen zuordnen..

Hallo Matthias,

wie kann ich denn bitte nach der Messung den Kanal auslesen?

Ich bin gerade dabei mich wieder damit zu beschäftigen.

Viele Grüße,

Matthias.

von Mitleser (Gast)


Lesenswert?

Matthias,

schaust du auch mal in das Datenblatt?

Das steht doch alles, z.B. für einen atMega88 auf Seite 254f,

Register Description
23.8.1 ADMUX – ADC Multiplexer Selection Register


ADMUX

0000 ADC0
0001 ADC1
:
0111 ADC7

von Matthias H. (maethes26)


Angehängte Dateien:

Lesenswert?

Mitleser schrieb:
> Matthias,
>
> schaust du auch mal in das Datenblatt?
>
> Das steht doch alles, z.B. für einen atMega88 auf Seite 254f,
>
> Register Description
> 23.8.1 ADMUX – ADC Multiplexer Selection Register
>
>
> *ADMUX*
>
> 0000 ADC0
> 0001 ADC1
> :
> 0111 ADC7

Hi, grüß Dich,

danke für Deine Hilfe.

Ja ich schaue auch gerade seit einer Stunde ins Datenblatt und erarbeite 
mir die Analogmessung im Detail, was ich vorher nicht ganz so intensiv 
getan habe.
anbei ein Bild von einem Teil meiner aktuellen Ausarbeitung zum Beweis. 
:-)

ich weiß auch wie man andere Kanäle wählt, habe aber Deine Ausführung 
leider nicht verstanden wie Du nachträglich nach einer Messung einen 
Kanäl zuordnen willst.
Darum ging es nur.

Viele Grüße,

Matthias.

von Mitleser (Gast)


Lesenswert?

Hallo,

du schriebst "wie kann ich denn bitte nach der Messung den Kanal 
auslesen?"
1
uint8_t adc_register = ADMUX & 0x07;

Wenn im Datenblatt R/W steht, dann können Register auch gelesen werden.
Falls das mal nicht geht, einfach eine statische Variable mit dem 
aktuellen ADC-Eingang belegen, dann hat man auch immer die Information 
parat.

Man muss ja auch vorher den ADC-Eingang setzen, dabei muss das ADMUX 
Register verändert werden und ein "dummy readout" folgen !!

Oben steht eine seht eine funktionierendes Beispiel aus einem Programm:

Beitrag "Re: Analogmessung mehrerer Kanäle hinterteinander, überall gl. Werte?"

.

von Mitleser (Gast)


Lesenswert?

Hallo,

du schriebst "wie kann ich denn bitte nach der Messung den Kanal 
auslesen?"
1
uint8_t adc_register = ADMUX & 0x07;

Wenn im Datenblatt R/W steht, dann können Register auch gelesen werden.
Falls das mal nicht geht, einfach eine statische Variable mit dem 
aktuellen ADC-Eingang belegen, dann hat man auch immer die Information 
parat.

Man muss ja auch vorher den ADC-Eingang setzen, dabei muss das ADMUX 
Register verändert werden und ein "dummy readout" folgen !!

Oben steht eine funktionierendes Beispiel aus einem Programm:

Beitrag "Re: Analogmessung mehrerer Kanäle hinterteinander, überall gl. Werte?"

.

von uwe (Gast)


Lesenswert?

64 Byte zur Mittelwertbildung ? Und dann noch eine Division ?
0. Reset
1. in ISR: increment Wandlungszähler
2. in ISR: gucken Wandlungszähler=1 wenn nicht weiter bei 5
3. in ISR: gucken ob Wandlungszähler>Anzahl der gewünschten Mittelungen
4. in ISR: bei der 1. Wandlung Flag setzen und Wert A speichern
5. in ISR: Wert B speichern und auf Wert A addieren und
 um 1 nach rechts shiften (also geteilt durch 2) und in A speichern

speichersparend und einfach

von Matthias H. (maethes26)


Angehängte Dateien:

Lesenswert?

Hallo,

danke für Deine Hilfe.

Mein konkretes Problem sieht folgendermaßen aus, oder ist es gar kein 
Problem und ich packe es nur falsch an???

ich möchte in einem Programm mehrere Kanäle nacheinander abfragen.

Die Temperaturabfrage ist nicht zeitkritisch und wird einmal in der 
Sekunde durchgeführt.

Auch die Strommessung des DC Motors ist nicht zeitkritisch und wird 1x 
die Sekunde ausgeführt

Ein anderer, dritter Kanal, soll die Motorpotiwerte eines 24V DC Motors 
so oft wie möglich messen.

Meine funktionierende Routine für die Aufgabenstellung sieht so aus:

was mich daran stört schreibe ich dann weiter unten

uint16_t ADC_Read_free_running_mode( uint8_t channel )
{
  static uint8_t channel_vorheriger, result;

  if (channel_vorheriger!=channel)
 //wenn ein neuer Kanal gewählt wurde, dann Kanal einstellen und 
Testmessung
  {
   ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
// Kanal waehlen, ohne andere Bits zu beeinflußen
   _delay_us(250);
// 250us Warten, damit die Elektronik sich darauf einstellen kann, 
verlangsamt das Programm unnötig
  result=ADCW;
//eine Messung machen die verworfen wird
  }

  channel_vorheriger=channel;
  return ADCW;                    // ADC auslesen und zurückgeben
}


mich stört die Wartezeit von 250uS nach der Kanaleinstellung. Ich weiß, 
das ich der Elektronik Zeit geben muss sich auf den neuen Kanal 
einzustellen.
Kann ich das Programm technisch nicht aber anders lösen, ohne das das 
Gesamte Programm steht und wartet????

Das Beispiel oben vom Kollegen verwendet einen festen Kanal, da tritt 
das Problem nicht auf.

Ich muss nach der Kanaländerung Wartezeit einbauen, kann ich das aber 
auch ohne DElay lösen???? Welche Möglichkeiten seht ihr da?

Ein Interruptflag gibt es im free running mode ja auch nicht wie ich 
gelesen habe!!!


Selbst, wenn ich ein Timer für das Messen des Kanals alle 50ms aufsetze, 
braucht die Elektronik nach der Kanaländerung Zeit und das delay würde 
das Programm und somit die Geschwindigkeit der Programmausführung 
verlangsamen.

Oder ich setze den Timer auf 25ms!! Beim ersten Aufruf der ISR setze ich 
nur den Kanal und beim 2. Aufruf nach weiteren 25mS lese ich diesen aus.

Dann muss ich aber 3 Kanäle in die ISR einbauen, die sich zeitlich nicht 
in die Quere kommen.
Temperaturmessung alle 1s und
Strommessung alle 1s

Ich hoffe, ich konnte mich verständlich ausdrücken.

Kann ich den Code hier noch besser darstellen? Bei Euch sah das immer 
anders aus.

Viele Grüße,

Matthias.

von Mitleser (Gast)


Lesenswert?

Hallo uwe Gast,

ich sehe leider nicht, auch durch einen Beweis, dass deine Funktion, dem 
des "arithmetische Mittel" entspricht.

http://upload.wikimedia.org/wikipedia/de/math/b/f/a/bfa76a2b6dffa3618b04ba0f90f42ed6.png

Falls ich mich verlesen habe, kannst Du bitte die Summenfunktion hier 
hinschreiben und einen Rechnung für
1
n=4
2
x1=1,
3
x2=2,
4
x3=3 und
5
x4=4
6
7
x_arith = 2,5

durchrechnen ?

.

von Mitleser (Gast)


Lesenswert?

Hallo Matthias,

Du musst auch die Wandlung mit der Änderung des ADC-Kannals 
syncronisieren.

so auf die schnelle, ohne es zu testen.
1
if (..) {
2
// Warte bis die Wandlung abgeschlossen ist
3
  loop_until_bit_is_not_set(ADCSRA, ADSC);
4
5
// ADC disable, Auto Trigger off
6
  ADCSRA &= (uint8_t)~((1<<ADEN) ^ (1<<ADATE));
7
8
  // AVCC with external capacitor at AREF pin
9
  ADMUX = (ADMUX & ~0x07) | (channel & 0x07);
10
11
// Reset ADC Control and Status Register B
12
  ADCSRB = 0;
13
14
// ADC enable, eine Wandlung starten
15
  ADCSRA |= (1<<ADEN) ^ (1<<ADSC);
16
17
// nach Aktivieren des ADC wird ein "Dummy-Readout" gemacht
18
  loop_until_bit_is_not_set(ADCSRA, ADSC);
19
  (void)ADCW;
20
21
// Free Running mode setzen
22
// ist natürlich == 0, aber so sieht man es besser.
23
  ADCSRB = (0<<ADTS2) ^ (0<<ADTS1) ^ (0<<ADTS0);
24
25
// Starte erste Wandlung, ADC Auto Trigger Enable
26
  ADCSRA |= (1<<ADSC) ^ (1<<ADATE);
27
}

von Mitleser (Gast)


Lesenswert?

Hallo Matthias,

ich habe noch noch einen kleinen Fehler drin:
1
if (..) {
2
// Warte bis die Wandlung abgeschlossen ist
3
  loop_until_bit_is_not_set(ADCSRA, (ADIF);
4
5
// sonst wie oben.
6
}

Im Handbuch steht

[q]
Bits 3:0 – MUX3:0: Analog Channel Selection Bits

The value of these bits selects which analog inputs are connected to the 
ADC. See Table 23-3
for details. If these bits are changed during a conversion, the change 
will not go in effect until this
conversion is complete (ADIF in ADCSRA is set).
[/q]

von Mitleser (Gast)


Lesenswert?

Hallo Matthias,

ich habe noch noch einen kleinen Fehler drin:
1
if (..) {
2
// Warte bis die Wandlung abgeschlossen ist
3
  loop_until_bit_is_set(ADCSRA, (ADIF);
4
5
// sonst wie oben.
6
}

Im Handbuch steht

[q]
Bits 3:0 – MUX3:0: Analog Channel Selection Bits

The value of these bits selects which analog inputs are connected to the 
ADC. See Table 23-3
for details. If these bits are changed during a conversion, the change 
will not go in effect until this
conversion is complete (ADIF in ADCSRA is set).
[/q]

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.