ATmega8 ADC verarbeitungs problem

OP #2272672
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.
Gast #2272681
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 ?
OP #2272702
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.
(Firma: KHTronik) #2272786
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
OP #2272825
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.
#2272847
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.
OP #2272861
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.
#2272863
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.
#2272871
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.
OP #2272877
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.
#2272882
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'.
OP #2272895
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.
#2273024
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)
OP #2273035
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.
Angehängte Dateien:
OP #2273652
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.
#2273686
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.)
Gast #2273727
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);
OP #2273804
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 15 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.