Forum: Mikrocontroller und Digitale Elektronik ATmega8 ADC verarbeitungs problem


von m. S. (marek)


Lesenswert?

Hi @all,

ich hab mal kurz eine frage bzw. breuchte mal euren Rat. Ich hab mir den 
ADC wie im ACC-tutorial beschrieben initiealisiert (Aussenbeschaltung 
und Refferenzspannung sind auch richtig).

ADC C-code:
1
#include "global.h"
2
3
void adc_init (void) // (Hinzugefügt 20.06)
4
{
5
  uint16_t result;      // Zwischenwariable für das erste Auslesen wird dan verworfen
6
7
  ADMUX |= (1<<REFS0);    // AVcc als Referenzspannung nutzen (5V)
8
  ADCSRA |= (1<<ADPS0) | (1<<ADPS1)  | (1<<ADPS2);    //   |  Frequenzvorteiler = 128 -> 125kHz
9
  ADCSRA |= (1<<ADEN);    // ADC Aktievieren
10
11
  /* Nach der Aktivierung des ADC wird ein "Dummy-Readout" empfohlen, man liest
12
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
13
14
  ADCSRA |= (1<<ADSC);    // ADC Wandlung 
15
  while ( (ADCSRA & (1<<ADIF))==0)    // Warten bis Konvertierung fertig
16
  {}
17
  result = ADCW;              
18
19
}
20
21
uint16_t adc_lesen (uint8_t Kanal)  //ADC Einzelmessung  (Hinzugefügt 20.06)
22
{ 
23
  ADMUX |= (ADMUX & ~(0x1F)) | (Kanal & 0x1F);   // Kalnal Auswahl ohne andere Bits zu beeinflussen
24
  ADCSRA |= (1<<ADSC);              // Eine Wandlung "singel conversion"
25
  while ( (ADCSRA & (1<<ADIF))==0)          // Warten bis Konvertierung fertig 
26
  {}
27
  return ADCW;
28
29
}

In meinem Hauptprogramm ruffe ich dann nacheinander zwei Spannungswerte 
ab.
1
adcwert0 = adc_lesen(0); //ADC Kanal 0 (Representativ für Ueff) Wert in adcwert laden 
2
Ueff = (adcwert0  *5.0) / 1024; //Ueff=(adcwert*(U_ref/(Maximalwert+1)))
3
4
adcwert1 = adc_lesen(1);// ADC Kanal 1 (Representativ für Ieff) Wert in adcwert laden 
5
Ihilf = (adcwert1 * 5.0) / 1024;// Ihilf kriegt Wert von adcwert
6
Ieff = (Ihilf*0.1);  // Ihilf durch 10 wegen verstärkung vom differenierer

wenn ich mir beide werte gleichzeitig ausgeben lasse merke ich das die 
Werte nicht stimmen da ich dierekt an den Pins mal nachgemessen habe.
Ich hab auch schon gelesen das es dabei zu problemen kommen kann so das 
die Werte nicht stimmen.
Kann mir jemand einen rat geben wie ich das beheben kann? die interrupts 
sind bei der Berechnung und ausgabe ausgeschaltet die Globalen da es da 
probleme mit einer anderen Berechnung gegeben hat. Ich danke schon mal 
für eure Hilfe.

mfg
m.s.

von m. S. (marek)


Lesenswert?

Achso anzumerken sei noch mit Ueff und Ieff rechne ich weiter die werden 
eigentlich nicht ausgegeben hab das nur mal zu testzwegken gemacht da 
mir das komisch vorkamm.

von Kurt H. (Firma: KHTronik) (kurtharders)


Lesenswert?

m. S. schrieb:

> ADMUX |= (ADMUX & ~(0x1F)) | (Kanal & 0x1F);

Wieso maskierst Du ADMUX mit 0x1f? Ich würde schreiben:
ADMUX |= Kanal & 0x0f;
Mit der Maskierung löschst Du REFS0, REFS1 und ADLAR.

Grüße, Kurt

von Uwe (Gast)


Lesenswert?

Schaltplan ? Innenwiderstand der Signalquelle, Wie ist Avcc gefiltert, 
wie wird die Bandbreite des Signals begrenzt, wie ist die Ground plane 
gelegt bzw. wie sieht das Layout aus ? Was heißt gleichzeitig ausgeben ?

von m. S. (marek)


Lesenswert?

Avcc mit LC siehe Datenblatt ATmega8 seite 200. Bei dem Signal handelt 
es sich um Gleichgerichtetes Signal.
Es handelt sich nur um ein Programmiertechnisches porblem, denke ich. 
Ich meinte nacheinander. Nicht gleichzeitig, sorry.

Eigentlich solte die ausgabe so aussehen:
1
 
2
  adcwert0 = adc_lesen(0);       // ADC Kanal 0 (Representativ für Ueff) Wert in adcwert laden 
3
      Ueff = (adcwert0  *5.0) / 1024;   // Ueff = (adcwert * ( U_ref / (Maximalwert +1)))
4
5
      adcwert1 = adc_lesen(1);      // ADC Kanal 1 (Representativ für Ieff) Wert in adcwert laden 
6
      Ihilf = (adcwert1 * 5.0) / 1024;  // Ihilf kriegt Wert von adcwert
7
      Ieff = (Ihilf*0.1);          // Ihilf durch 10 wegen verstärkung vom differenierer
8
    
9
      Z = ((Ueff+0.0) / Ieff);          // Ermittelung des Scheinwiderstandes |Z|
10
uart_puts("|Z| in Ohm:  ");
11
      uart_puts( dtostrf(Z, 10, 8, s));

@Kurt

hab ich umgeschrieben keine Änderung.

mfg
m.s.

von Kurt H. (Firma: KHTronik) (kurtharders)


Lesenswert?

war auch ein Denfehler. Aber verwirrend und überflüssig ist der ADMUX & 
... schon :-)

Du sprichst von "gleichgerichtet". Messen tust Du natürlich den 
momentanen Wert, und das für Kanal 0 und Kanal 1 mit evtl. merklichem 
Abstand.

Grüße, Kurt

von m. S. (marek)


Lesenswert?

Ja messe tu ich einen momentanen Wert. An ADC0 liegt eine Gleichspannung 
an wenn ich gerade mal messe 456mV an ADC1 liegen 860mV an.

Wenn ich mir nur den ADC0 Spannungswert anzeigen lasse über Hyper 
Terminal (Die zeile mit adcwert1 = adc_lesen(1) Auskommentiert) zeigt er 
mir auch die 456mV an nicht exkt aber tolerier bar genau.
Wenn ich mir aber wie in dem nachfolgenden Code gerade zu sehen beide 
ADC Werte nacheinader ausgeben lasse, ist der eine Ausgabe wert für Ueff 
(ADC0) 0,8496mV und der Wert für Ieff (ADC1)  0,8594mV.
Ihrgend wie scheinen die Beiden Werte zusammen zuhängen. Achso ich 
arbiete übrigens mit 16MHz Taktung für den µC.
Ich weis aber nicht wie ich die unabhängig voneinander machen kann. 
Zudem Zeitpunkt wo ich die Berechnungen mache und die Ausgabe sind 
nemlich alle Interrupts aus. Da es mit einer anderen Berechnung probleme 
gegeben hat.
1
#include "global.h"
2
3
4
int main(void){  
5
//  DDRB |= (1<<DDB0);
6
  adc_init();    // Funkt. Aufruf ADC initialisierung  
7
  uart_init();  // Funkt. Aufruf UART initialisierung
8
  INTRUP_frei();   // Funkt. Aufruf Funktion zur Initialiesierung der INT0 & INT1 interrupts 
9
  sei();      // Globale Interrupts freigeben
10
11
  while(1)
12
  {
13
    if (fertig == 1)
14
      {
15
16
      T0 = (k * 256) + TCNT0;       // Wert ermittelung für T0 mit exaten Werten schiften <<8 = *256
17
      T2 = (j << 8) + TCNT2;        // Wert ermittelung für T2 mit exaten Werten
18
      PHI = ((T2+0.0)/T0) * 360.0;        // Errechnung von Phi in Grad
19
20
      adcwert0 = adc_lesen(0);       // ADC Kanal 0 (Representativ für Ueff) Wert in adcwert laden 
21
      Ueff = (adcwert0  *5.0) / 1024;   // Ueff = (adcwert * ( U_ref / (Maximalwert +1)))      
22
23
      adcwert1 = adc_lesen(1);      // ADC Kanal 1 (Representativ für Ieff) Wert in adcwert laden 
24
      Ihilf = (adcwert1 * 5.0) / 1024;  // Ihilf kriegt Wert von adcwert
25
      Ieff = (Ihilf*0.1);          // Ihilf durch 10 wegen verstärkung vom differenierer
26
27
28
      Z = ((Ueff+0.0) / Ieff);          // Ermittelung des Scheinwiderstandes |Z|
29
    //  f = 16000000.0/T0;
30
  
31
    uart_puts( dtostrf(Ueff, 10, 4, s));
32
      uart_puts("   ");
33
    uart_puts( dtostrf(Ieff, 10, 4, s));
34
       uart_puts("   ");
35
  //    uart_puts("|Z| in Ohm:  ");
36
      uart_puts( dtostrf(Z, 10, 8, s));   // Ausgabe als ASCII für Hyperterm. von Z mit 4 nachkommastellen
37
    //  uart_puts( utoa(T0, s, 10));
38
      uart_puts("   ");
39
    //  uart_puts( utoa(T2, s, 10));
40
    //  uart_puts("   ");
41
  //    uart_puts(" PHI in Grad:");
42
  //    uart_puts( dtostrf(PHI, 10, 4, s));
43
  //    uart_puts("   ");
44
      
45
  //    uart_puts("f in Hz:");
46
  //    uart_puts( dtostrf(f, 10, 4, s)); // Ausgabe als ASCII für Hyperterm. von PHI mit 4 nachkommastellen
47
      uart_puts("\n\r");
48
      
49
      _delay_ms(1000);
50
      fertig = 0;      
51
//      PORTB |= (1<<PB0);          // MOS-FETs leiten lassen zur entladung der Kondensator 
52
      sei();                // Einschalten der Interrupts
53
      }
54
  }
55
56
   return 0;
57
}
58
59
ISR(INT0_vect)      // ISR für INT0 Steigendeflanke
60
  {
61
  static int start=1;    // variable um festzustellen ob 2 Stegende Flanke vorhanden waren
62
  if (start == 1)  
63
    {
64
    start=0;
65
//    PORTB &= ~(1<<PB0);  // MOS-FETs wider sperren lassen damit sich der Kondensator wider laden kann    
66
    Timer0_init();    // Funkti. Aufruf Timer0 initialisieren
67
    Timer2_init();    // Funkti. Aufruf Timer2 initialisieren
68
    }
69
  else
70
    {
71
    TCCR0 &= ~(1<<CS00);        // Timer0 anhalten
72
    fertig = 1;              // (Hinzugefügt 20.06)Variable zur Berechnung und dann Ausgabe        
73
    cli();                // Ausschalten der Interrupts
74
    start=1;
75
    }
76
  }
77
78
79
ISR(INT1_vect)
80
  {
81
  TCCR2 &= ~(1<<CS20);  // Timer2 anhalten
82
  }  
83
84
ISR(TIMER0_OVF_vect)
85
  {
86
  k++;          // Inkrementieren des Überlaufzählers
87
  }  
88
89
ISR(TIMER2_OVF_vect)
90
  {
91
  j++;          // Inkrementieren des überlaufzählers
92
  }

Die Timer arbeiten auch richtig wie sie sollen ich komme ja in die 
Ausgabe rein also in if(fertig == 1).
Die Ausgabe der Timer ist auch richtig gerade gucke ich sie mir bloß 
nicht an da ich weis das das past.
Also ich weis momentan nicht Wirklich weiter.

mfg
m.s.

von Karl H. (kbuchegg)


Lesenswert?

Das
1
  while ( (ADCSRA & (1<<ADIF))==0)          // Warten bis Konvertierung fertig 
2
  {}

hast du so aus dem AVR-GCC-Tutorial? Glaub ich nicht. Und der 
Unterschied zur Tutorialversion ist wichtig!
Einfach mal drüber nachdenken, wann eigentlich Interrupt Flags wieder 
gelöscht werden. Hinweis: Das erneute Starten des ADC tut es nicht.

von Karl H. (kbuchegg)


Lesenswert?

Kurt Harders schrieb:
> war auch ein Denfehler. Aber verwirrend und überflüssig ist der ADMUX &
> ... schon :-)

verwirrend vielleicht.
Aber überflüssig: ganz im gegenteil

von Kurt H. (Firma: KHTronik) (kurtharders)


Lesenswert?

Wie sind denn die Messwerte, wenn Du nur ADC1 abfragst?

Nur als Versuch: kurze Verzögerung zwischen den Messungen einbauen.

Grüße, Kurt

von m. S. (marek)


Lesenswert?

Ne das habe ich nicht aus dem Tutorial das habe ich aus: AVR-RISC 
Embedded Software selbst entwickeln (von Roman Mittermayer Seite 87).
Ich hab das jetzt mal geendert aber es hat keine auswirkung auf das 
Ergebnis es ist immer noch das selbe problem.
Mir ist auch schon aufgefallen wenn ich das Programm auf den µC lade das 
die erste Ausgabe ans Hyper Terminal anscheinet richtig ist also die 
Werte entsprechen so ungefähr dem was auch anliegt. Der rest dann aber 
nicht mehr.

mfg
m.s.

von Karl H. (kbuchegg)


Lesenswert?

m. S. schrieb:
> Ne das habe ich nicht aus dem Tutorial das habe ich aus: AVR-RISC
> Embedded Software selbst entwickeln (von Roman Mittermayer Seite 87).

Ist trotzdem Unsinn.
Es sei denn der Autor kümmert sich in seinem Code irgendwo darum, dass 
das Interrupt Flag wieder zurückgesetzt wird oder aber er macht das 
Auslesen das ADC Ergebnisses per Interrupt.

> Ich hab das jetzt mal geendert aber es hat keine auswirkung auf das
> Ergebnis es ist immer noch das selbe problem.

OK. Dann suchen wir weiter nach dem nächsten Problem.

von Karl H. (kbuchegg)


Lesenswert?

Wie ist dein Signal angekoppelt?

Jetzt könnte das Problem ins Spiel kommen, von dem Knut schon gesprochen 
hat. Nach dem Umschalten des Kanals muss sich die Sample&Hold Stufe erst 
mal auf die neue Spannnung einstellen, was auch ein wenig Zeit benötigt.

Du könntest nach dem Einstellen des Kanals in ADC_Read eine kleine Pause 
einlegen

Oder überhaupt immer 2-mal samplen und die erste Messung verwerfen.


Das ist jetzt allerdings ein Schuss ins Blaue, denn ob das das Problem 
ist, hängt davon ab, wie deine Aussenbschaltung an den ADC Eingängen 
aussieht und wie hochohmig die ist.

von m. S. (marek)


Lesenswert?

Also der ADC1 Wert den ich mir gerade ausgeben lasse ist 0.8594mV 
gemessen mit Digitalmultimeter sind es 0,871mV.

Hab auch gerade ein Delay eingebaut über _delay_ms(1000); zwischen den 
einzelnen adclese() aufrufen. Kein unterschied.

mfg
m.s.

von Karl H. (kbuchegg)


Lesenswert?

m. S. schrieb:
> Also der ADC1 Wert den ich mir gerade ausgeben lasse ist 0.8594mV
> gemessen mit Digitalmultimeter sind es 0,871mV.
>
> Hab auch gerade ein Delay eingebaut über _delay_ms(1000); zwischen den
> einzelnen adclese() aufrufen. Kein unterschied.

Nicht zwischen den Aufrufen. Dort bringt er nichts.

Du brauchst IM ADC_Read eine kleine Pause (wenn die Vermutung stimmt).

Hier

  ADMUX |= (ADMUX & ~(0x1F)) | (Kanal & 0x1F);   // Kalnal Auswahl ohne

stellst du den nächsten ADC Kanal ein. Und zwischen diesem Zeitpunkt und 
dem hier

  ADCSRA |= (1<<ADSC);              // Eine Wandlung "singel conversion"

muss die Sample&Hold Stufe in der Lage sein, die extern anliegende 
Spannung auch 'anzunehmen'.

von m. S. (marek)


Lesenswert?

Also direckt vor den ADC Eingängen ist jeweils ein Verbesserter 
Scheitelwertmesser (Tietze Schenk 13 Auflage seite 1029). Dessen Ausgang 
praktisch aus einem Impedanzwandler kommt mit einem Widerstand am 
Ausgang (Intern) von 200 Ohm.

@ Karl meinst du dierekt in adc.c oder nach dem adcwert0= adclesen(0).

Mit dem zwei mal sempeln wie meinst du das könntest du das vielleicht in 
ein Code beispiel packen das ich mir das mla anschauen kann. oder meinst 
du bei jeder messung den timer neu initialiesieren.

mfg
m.s.

von m. S. (marek)


Lesenswert?

Hab da wie du es beschriebn hast ein delay dazwischen gepackt hat aber 
nichts gebracht.

mfg
m.s.

von Karl H. (kbuchegg)


Lesenswert?

Wie sieht der Code jetzt (nach der MOdifikation) aus? Inkl. ADC Code.

von m. S. (marek)


Angehängte Dateien:

Lesenswert?

Sorry das das gedauert hat. Bin eben erst zuhasue angekommen. Noch eine 
kleine korrecktur muß ich sagen die mV werte vorhin sollen natürlich V 
werte sein. Ich hoffe das o.k. wenn ich die als .c dateien hochlade.

mfg
m.s.

von Karl H. (kbuchegg)


Lesenswert?

OK. sieht nicht schlecht aus (bis auf die cli() / sei() Steuerung, aber 
das sollte jetzt erst mal nichts damit zu tun haben). Die 10 Sekunden 
delay sollten auf jeden Fall reichen :-)

Wie ist dein AREF Pin beschaltet?

Ich hege zwar keine große Hoffnung, aber probiers mal so
1
      adcwert0 = adc_lesen(0);       // ADC Kanal 0 (Representativ für Ueff)
2
      adcwert0 = adc_lesen(0);       // ADC Kanal 0 (Representativ für Ueff) Wert in adcwert laden 
3
      Ueff = (adcwert0  *5.0) / 1024;   // Ueff = (adcwert * ( U_ref / (Maximalwert +1)))      
4
      uart_puts( dtostrf(Ueff, 10, 4, s));
5
    
6
      adcwert1 = adc_lesen(1);      // ADC Kanal 1 (Representativ für Ieff)
7
      adcwert1 = adc_lesen(1);      // ADC Kanal 1 (Representativ für Ieff) Wert in adcwert laden 
8
      Ieff = (adcwert1 * 5.0) / 1024;  // Ihilf kriegt Wert von adcwert
also 2 mal auslesen und den jeweils ersten Wert verwerfen.

Ich denke aber eher, dass du ein Harware-Problem hast (wenn ich nicht 
etwas grundlegendes im Code übersehen habe)

von m. S. (marek)


Angehängte Dateien:

Lesenswert?

Das werde ich morgen mal probieren zuhause geht das schlecht. Im Anhang 
mal der eagle plan wie er uhr sprünglich mal gewesen ist. ICh weis das 
da sind fehler in der µC beschaltung dahingehend das pin2 auf bin pin32 
gelegt wurde und pin9 auf pin1. Der rechte schaltungsteil existiert so 
nicht mehr da er nicht mehr funktioniert hat und ich es aber ähnlich 
nachgebaut habe von daher passt das einegtlich noch auch wenn sich die 
OPVs bischen geendert haben.

mfg
m.s.

von m. S. (marek)


Lesenswert?

Habs gerade ausprobiert hatt nicht geklapt. Ich werde mir mal einen 
neuen code erstellen in dem ich nur den ADC verwende und sonst nichts 
weiter mache.
Malschauen vielleicht stoße ich ja dann auf das problem bzw. auf eine 
lösung.

mfg
m.s.

von Ralf G. (ralg)


Lesenswert?

m. S. schrieb:
> /* Nach der Aktivierung des ADC wird ein "Dummy-Readout" empfohlen, man liest
>      also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
... Nicht nur empfohlen, sondern notwendig. Und auch nach jeder 
Kanalumschaltung, wenn ich mich nicht irre. (Das hab' ich bei dir nicht 
gefunden.)

von ziegenpeter (Gast)


Lesenswert?

Weiss nicht ob das Teil des Problems ist,
aber ich würde diese Zeile ändern von

ADMUX |= (ADMUX & ~(0x1F)) | (Kanal & 0x1F);

in

ADMUX = (ADMUX & ~(0x1F)) | (Kanal & 0x1F);

von m. S. (marek)


Lesenswert?

Scheis auf Spiderman und Superman. Ziegenpeter ist der neu Held am 
firnament. Das steht sogar so im Tutorial und ich hab das überlesen 
sorry. Aber danke an alle die mir geholfen haben. ziegenpeter du hast 
was gut bei mir ( Eigentlich Ihr alle für eure hilfe). Ich werde mal 
paar messungen machen um das ergebnis zu veriffiezieren aber ich denke 
das solte passen.
Noch mal danke an alle Ihr seit die besten. Jetzt kann ich wider berüigt 
schlafen.

mfg
m.s.

von Ralf G. (ralg)


Lesenswert?

m. S. schrieb:
> Jetzt kann ich wider berüigt schlafen.

Jetzt schon? Du hast's gut.

von m. S. (marek)


Lesenswert?

Naja ist noch einiges zu machen. Ist ja erst halb eins. Aber die 
schaltung funktioniert das programm auch. Jetzt geht es halt weiter dir 
auch noch viel erfolg bei dem was du machst.

mfg
m.s.

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.