Analogmessung mehrerer Kanäle hinterteinander, überall gl. Werte?

#2374826
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;

}
Angehängte Dateien:
Gast #2374914
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
#2374968
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?
Gast #2375009
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
#2375085
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.
#2375101
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
}
#2386117
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.
Angehängte Dateien:
Gast #2386124
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?"

.
Gast #2386126
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?"

.
Gast #2386192
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
#2386210
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.
Angehängte Dateien:
Gast #2386235
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 ?

.
Gast #2386268
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
}
Gast #2386277
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]
Gast #2386278
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]

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren