Forum: Compiler & IDEs Takt des µC's wird durch ADC verändert!?


von Manuel (Gast)


Lesenswert?

Hallo!

Ich hab teilweise beim messen mit meinem Programm festegestellt, dass 
ich bei Spannungen nahe der Versorgungsspannung (Uref = AVcc) scheibar 
meinen Mikrocontroller übertakte, auf jeden Fall arbeitet er dann alle 
Befehle deutlich schneller ab. Andersherum: Nehme ich die Spannung 
ruckartig weg, handelt er alle Befehle deutlich langsamer ab, ein 
Neustart führt wieder zur gewünschten Geschwindigkeit. Wie kann soetwas 
denn passieren??

Ich verwende einen ATmega2561 mit 14745600Hz, hier der Ausschnitt aus 
dem Programm, falls ich dort irgendetwas vergessen habe/ falsch mache.
1
 /****************************************************************/
2
//Initialisierung der ADC-Schnittstelle
3
4
void ADC_Init (void) 
5
{
6
  uint16_t result; 
7
8
  ADMUX = (1<<REFS0);                  // Uref = Avcc
9
  ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler 128
10
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
11
 
12
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
13
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
14
 
15
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
16
  while (ADCSRA & (1<<ADSC) );          // auf Abschluss der Konvertierung warten
17
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
18
     Wandlung nicht übernommen. */
19
  result = ADCW;
20
}
21
22
23
24
/****************************************************************/
25
// ADC Mehrfachmessung mit Mittelwertbbildung 
26
27
uint16_t ADC_Read_Avg(uint8_t channel, uint8_t average)
28
{
29
  uint32_t result;
30
 
31
  // Kanal waehlen, ohne andere Bits zu beeinflußen
32
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
33
 
34
  // Eigentliche Messung - Mittelwert aus 'average' aufeinanderfolgenden Wandlungen 
35
  result = 0; 
36
 
37
  for (uint8_t i = average; i>0; i-- ) {
38
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
39
    while (ADCSRA & (1<<ADSC) );    // auf Abschluss der Konvertierung warten
40
    result += ADCW;                 // Wandlungsergebnisse aufaddieren
41
  }
42
 
43
  result /= average;               // arithmethischer Mittelwert
44
  return (uint16_t)result;
45
}

von (prx) A. K. (prx)


Lesenswert?

Manuel schrieb:

> auf jeden Fall arbeitet er dann alle Befehle deutlich schneller ab.

Woran erkennst du das?

von Manuel (Gast)


Lesenswert?

Ich erkenne, dass die ADC-wandlung sehr viel schneller geht, was allein 
ja nicht gleich der gesamte Takt sein muss.

Verlangsamt sich der Takt nun aber, sehe ich gleichzeitig an einer 
Echtzeituhr, die in Main angezeigt wird auch nur noch alle 4-5 Sekunden 
eine Veränderung...hm wobei selbst das könnte daran liegen, dass die 
AD-Wandlungen einfach so lange dauern, dass in der Zeit nichts anderes 
mehr gemacht wird.

Kann auch sein, dass sich nur die AD-Wandler Frequenz ändert...Auf jeden 
Fall werden alle Werte immernoch scheinbar "gleichzeitig" aktualisiert 
nur seltener.

Also habs gerad nochmal bisschen genauer ausprobiert, es funktioniert 
alles normal bis ich über 4,5V anlege (noch unter 1023), dann 
aktualisieren sich die Werte joa vermutlich mit maximaler 
Geschwindigkeit. Drehe ich die Spannung dann wieder unter 4,4 V 
verlangsamt sich sämtliche Aktualisierung auf 4-5s -> ursprünglich 
sollen alle 0,25s die Messwerte aktualisiert werden.

von (prx) A. K. (prx)


Lesenswert?

14,7MHz klingt nach Baudratenquarz, also nach Quarz, und dass ein 
Quarzoszillator spürbar spannungsabhängig wäre, das darf man wohl 
getrost ausschliessen. Es sei denn der Quarz wäre im Eimer oder total 
falsch betrieben (Schaltung? Layout?).

Apropos Betrieb: Ich hoffe der Oszillator ist korrekt gefused, also als 
full swing osc, nicht als Stromsparoszillator für langsamere Quarze. Und 
hat seine Kondensatoren dran.

Wenn du dem nicht traust, dann häng einen autark ohne Interrupts 
laufenden Hardware-Sekundentimer auf einen OCxy-Ausgang, dort eine LED 
dran, und guck zu.

Du bist auch nicht der Erste, der mit dem ADC an einem 5V-AVR Eingänge 
Spannungen im Bereich 4,5V misst. Es könnte sich also auch um einen 
Schmutzeffekt des Programms handeln, wenn Messungen schneller oder 
langsamer laufen.

von Rolf Magnus (Gast)


Lesenswert?

Manuel schrieb:
> Ich erkenne, dass die ADC-wandlung sehr viel schneller geht, was allein
> ja nicht gleich der gesamte Takt sein muss.

Hast du vielleicht eine sehr hochohmige Quelle angeschlossen? Da können 
sich meines Wissens Wandlungen evtl. verlängern.

> Verlangsamt sich der Takt nun aber, sehe ich gleichzeitig an einer
> Echtzeituhr, die in Main angezeigt wird auch nur noch alle 4-5 Sekunden
> eine Veränderung... hm wobei selbst das könnte daran liegen, dass die
> AD-Wandlungen einfach so lange dauern, dass in der Zeit nichts anderes
> mehr gemacht wird.

Ähm, wie machst du denn diese "Echtzeituhr". Ich hoffe doch, über einen 
Timer-Interrupt? Alles andere wäre sowieso unbrauchbar. Eine Wandlung 
dauert im Verhältnis zum Prozessortakt übrigens sehr lange.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:

> Hast du vielleicht eine sehr hochohmige Quelle angeschlossen? Da können
> sich meines Wissens Wandlungen evtl. verlängern.

Die Dauer der Wandlung hängt nicht vom Signal ab. Nur die Genauigkeit 
vom Ergebnis leidet mit dem Innenwiderstand.

von Peter D. (peda)


Lesenswert?

Manuel schrieb:
> hier der Ausschnitt aus
> dem Programm, falls ich dort irgendetwas vergessen habe/ falsch mache.

Wichtiger währen aber die Teile, mit denen Du feststellst, daß der ADC 
den Quarz beeinflussen können soll.


Peter

von Rolf Magnus (Gast)


Lesenswert?

Also ich hab von diesem Prozessor kein Datenblatt da, aber in dem vom 
ATmega8 steht zumindest:

"The ADC is optimized for analog signals with an output impedance of 
approximately 10 kΩ or less. If such a source is used, the sampling time 
will be negligible. If a source with higher impedance is used, the 
sampling time will depend on how long time the source needs to charge 
the S/H capacitor, with can vary widely."

von Hc Z. (mizch)


Lesenswert?

Die ADC-Wandelzeit ist fix: 25 Zyklen für die erste Konversion, danach 
13|13,5|13..14 ADC-Zyklen (normal|Auto-Trigger|differentiell).  Eine 
Abhängigkeit vom Eingangssignal gibt es nicht.

Es muss sich also um einen Dreckeffekt an anderer Stelle handeln.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus zitierte im Beitrag #1825210:

> will be negligible. If a source with higher impedance is used, the
> sampling time will depend on how long time the source needs to charge
> the S/H capacitor, with can vary widely."

Jo, aber das ist dein Problem, nicht das vom ADC. Woher soll der denn 
das wissen? Was die damit sagen wollen ist: Warte nach der Umschaltung 
vom Muxer gefälligst lange genug bis sich das Signal am S&H-C 
stabilisiert hat. Bei einem Quellwiderstand bis 10K geschieht dies durch 
das eingebaute starre Timing automatisch, darüber muss man selber dafür 
sorgen.

von Manuel (Gast)


Lesenswert?

hm also ich messe bisher einfach direkt am Netzgerät, welches ja schon 
sehr hochohmig sein dürfte, da soll ja schließlich nicht wirklich Strom 
fließen.

Ich muss allerdings auch zustimmen, dass ich nicht bei allen Programmen 
dieses Problem habe, es also durchaus ganz gut an dem Quellcode liegen 
kann...

Dass der Quarz nicht korrekt angeschlossen ist oder die Fuses falsch 
sind kann ich im Prinzip ausschließen, da vom Hersteller 
funktionstüchtig so geliefert.

von Rolf Magnus (Gast)


Lesenswert?

Manuel schrieb:
> hm also ich messe bisher einfach direkt am Netzgerät, welches ja schon
> sehr hochohmig sein dürfte,

Du meinst sicher "niederohmig".

> Ich muss allerdings auch zustimmen, dass ich nicht bei allen Programmen
> dieses Problem habe, es also durchaus ganz gut an dem Quellcode liegen
> kann...

Darüber, wie deine "Echtzeituhr" funktioniert, hast du allerdings noch 
nichts geschrieben.

von Peter D. (peda)


Lesenswert?

Manuel schrieb:
> es also durchaus ganz gut an dem Quellcode liegen
> kann...

Da Du uns den relevanten Code ja nicht zeigst, wird es mit Sicherheit 
daran liegen.


Peter

von Manuel (Gast)


Lesenswert?

keine ahnung was der relevante code sein soll... die echtzeituhr dürfte 
damit auf jeden fall nichts zu tun haben, die funktioniert über externen 
32khz quarz mit lithiumbatterie und rufe ich über (bei mir timer 2) den 
asynchronen timer auf.

von U.R. Schmitt (Gast)


Lesenswert?

Manuel schrieb:
> keine ahnung was der relevante code sein soll... die echtzeituhr dürfte
> damit auf jeden fall nichts zu tun haben,

Genau das ist die Vermutung der meisten hier, daß deine Echtzeituhr 
nämlich je nachdem was Dein Controller zu tun hat falsch geht.
Nur um das zu sehen, müsste man exakt sehen wie Du diese Echtzeituhr 
realisiert hast (Hard und Software).
Wenn die Uhr über einen Timerinterrupt ralisiert wurde müsste man sehen 
ob Du evt. irgendwo im restlichen Code die Interrupts so lange 
deaktivierst, daß welche verschluckt werden.

von Klaus (Gast)


Lesenswert?

Manuel schrieb:
> keine ahnung was der relevante code sein soll.

Na welcher wohl? Der Code, den du geschrieben hast, und der nun in einem 
Controller läuft! Leute gibts....

von Manuel (Gast)


Lesenswert?

folgende routinen beinhaltet mein programm:

void delay_ms(unsigned int period);
void ADC_Init (void);
uint16_t ADC_Read(uint8_t channel);
uint16_t ADC_Read_Avg(uint8_t channel, uint8_t average);
void PWM_Init(void);
uint16_t PWM_Set(uint16_t adc, uint8_t channel);
void Steuerung (uint8_t channel);
uint16_t Strommessung (void);
uint16_t Spannungsmessung(void);
void Tiefenmessung (void);
void Display_Dimm(uint8_t endwert);
void RTC (void);

in main rufe ich alles auf, Steuerung liest adc ein und gibt in 
umgewandelter form an pwm weiter, Strommessung, spannungsmessung und 
tiefenmessung lesen adc ein, verarbeitet den wert und geben etwas aufm 
display aus.
display dimm steuert die helligkeit der lcd des displays

von Manuel (Gast)


Lesenswert?

und hier noch main:
1
/****************************************************************/
2
//Interrupt-Routine zum aktualisieren der Echtzeituhr
3
ISR(TIMER2_COMPA_vect)  //Bei Compare-Match mit OCR2A 
4
{
5
  interrupt = 1;        //RTC in main aufrufen lassen
6
}
7
8
/****************************************************************/
9
//MAIN-Funktion
10
11
int main (void)
12
 {
13
14
  uint16_t strom = 0;
15
  uint16_t spannung = 0;
16
  uint8_t endwert = 130;
17
18
  TIMSK2 = 0x00;                             //Interupts disabled
19
  ASSR    = (1<<AS2);                        //Externer Takt an TOSC1
20
  TCCR2A = (1<<WGM21);                      //Clear Timer OC2A on Comp match (CTC) 
21
  TCCR2B = (1<<CS22) | (1<<CS21) | (1<<CS20);//Prescaler auf 1024
22
  TIMSK2 = (1<<OCIE2A);                      //Compare match interrupt enable  
23
  OCR2A   = 31;                              //Zähler geht bis 32, dann Compare match
24
  sei();                                    //Global Interrupt enable
25
26
  
27
  LCD_Init();               // Display initialisieren
28
  ADC_Init();                //AD-Converter initialisieren
29
  PWM_Init();
30
  
31
  Display_Dimm(endwert);  
32
33
              
34
  while(1)                  // Endlosschleife
35
  {  
36
    // Wenn ein Interrupt ausgelöst wurde
37
    if (interrupt == 1)
38
    {
39
      RTC();                
40
      interrupt = 0;        //Interrupt wurde abgehandelt, warten bis wieder 1
41
    }
42
    Steuerung (0); 
43
    Steuerung (1);
44
45
    strom = Strommessung();       
46
    spannung = Spannungsmessung();    
47
    Tiefenmessung();        
48
49
    delay_ms(250);        
50
  }
51
}

von gerd (Gast)


Lesenswert?

Manuel schrieb:
1
> // Wenn ein Interrupt ausgelöst wurde
2
>     if (interrupt == 1)
3
>     {
4
>       RTC();                
5
>       interrupt = 0;        //Interrupt wurde abgehandelt, warten bis wieder 1
6
>     }
7
>     Steuerung (0); 
8
>     Steuerung (1);
9
> 
10
>     strom = Strommessung();       
11
>     spannung = Spannungsmessung();    
12
>     Tiefenmessung();        
13
> 
14
>     delay_ms(250);

Die RTC wird nicht besonders genau sein und ist vom Programmablauf 
abhängig.

- gerd

von gerd (Gast)


Lesenswert?


von Manuel (Gast)


Lesenswert?

die uhr ist schon genau, ich frag sie nur nicht genau ab, das ist mir 
bewusst

von gerd (Gast)


Lesenswert?

Manuel schrieb:
> die uhr ist schon genau, ich frag sie nur nicht genau ab

?? Bahnhof?
In deinem Timer2-Interrupt setzt du nur eine Flag, welche irgendwann 
(im schlechtesten Fall >250ms) mal abgearbeitet wird. Deine RTC wird 
also nicht genau laufen - kann sie garnicht - da die Aktualisierung 
immer irgendwann während der Programmlaufzeit passiert. Relativ genau 
wäre sie, wenn sie in der ISR aktualisiert würde. Schau dir mal an, wie 
das in dem oben verlinkten Artikel gemacht wurde.

- gerd

von Manuel (Gast)


Lesenswert?

die uhr ist daher annähernd genau, da sie hardware-gesteuert ist und ich 
sie nur über den asynchronen timer abfrage und dann durchaus starke 
verzögerungen rein bekomme, bis ich sie in main aktualisiere, aber das 
reicht mir momentan erstmal aus.

wollte ich das so machen, wie in dem beitrag den du gepostet hast, 
müsste ich die uhr bei veränderung jedes mal neu stellen, ich will sie 
aber erstma nur mit dem wert, den ich ihr über twi eingestellt habe so 
weiter laufen lassen und aktualisiere mir ab und zu ma die uhrzeit, die 
ich mir anzeigen lasse.

von Floh (Gast)


Lesenswert?

du hast timer 2 im asynchronen Modus mit nem Uhrenquarz dran?
Wenn ja, dann hat gerd recht.
Szenario:
Int2 löst aus.
Programm kommt zur Abfrage, passt.
Int2 löst aus.
Programm rädelt grad länger mit ADC und anderem.
Int2 löst aus.
Programm kommt zur Abfrage, ein Interrupt wurde aber nicht 
berücksichtigt.

Vertstehst du das Problem?

von (prx) A. K. (prx)


Lesenswert?

Nebenbei: Wenn man den Timer asynchron betreibt, dann gibt es ein paar 
Kleinigkeiten zu beachten. Die hängen damit zusammen, dass dann nicht 
nur der Oszillator des Timers sondern der gesamte Timer asynchron läuft. 
Steht alles fein säuberlich im Datasheet, muss man bloss lesen.

von (prx) A. K. (prx)


Lesenswert?

Es spricht übrigens nichts dagegen, den Zeitupdate in der Mainloop zu 
lassen. Man muss das nur anders organisieren. Ohne programmiertem delay.

In deinem Fall könnte man beispielsweise den Timer auf 250ms statt 
1000ms Interrupt konfigurieren und die Mainloop darauf warten lassen. 
Und in jedem vierten Durchlauf die Uhr hochzählen, als erste Aktion in 
der Mainloop. Solange der übrige Kram nicht länger dauert als 250ms geht 
das in Ordnung.

von Manuel (Gast)


Lesenswert?

jo das hab ich auch schon überlegt^^ so werd ich das wohl auch machen. 
aber was meinst du mit dingen, die man noch beachten muss wenn man den 
asynchronen timer betreibt? dass die delay-routine in meinem programm 
keinen sinn ergibt sehe ich ein und hatte ich auch noch vor zu ändern.

von (prx) A. K. (prx)


Lesenswert?

Ich meine jene Dinge, für die das Datasheet des ATmega32 unter 
"Asynchronous Operation of Timer/Counter2" ca. 1,5 Seiten verwendet. 
Wird beim ATmega2561 wohl ähnlich aussehen.

von Manuel (Gast)


Lesenswert?

bin der meinung, dass ich mir die sachen sogar durchgelesen hatte...wie 
dem auch sei, bekomm ich so weniger mecker? :)  :
1
int main (void)
2
 {
3
4
  uint16_t strom = 0;
5
  uint16_t spannung = 0;
6
  uint8_t endwert = 130;
7
8
  TIMSK2 = 0x00;                             //Interupts disabled
9
  ASSR    = (1<<AS2);                        //Externer Takt an TOSC1
10
  TCCR2A = (1<<WGM21);                      //Clear Timer OC2A on Comp match (CTC) 
11
  TCCR2B = (1<<CS22) | (1<<CS20);            //Prescaler auf 128
12
  TIMSK2 = (1<<OCIE2A);                      //Compare match interrupt enable  
13
  OCR2A   = 63;                              //Zähler geht bis 64, dann Compare match
14
  sei();                                    //Global Interrupt enable
15
16
  
17
  LCD_Init();               // Display initialisieren
18
  ADC_Init();                //AD-Converter initialisieren
19
  PWM_Init();
20
  
21
  Display_Dimm(endwert);  
22
23
              
24
  delay_ms(1000);
25
26
  while(1)                  // Endlosschleife
27
  {  
28
    // Wenn ein Interrupt ausgelöst wurde
29
    if (interrupt == 1)
30
    {
31
      i++;
32
      if ( i == 4 ) 
33
      {
34
        RTC();                //Dann Uhr aktualisieren 
35
        i = 0;
36
      }      
37
38
      Steuerung (0); 
39
      Steuerung (1);
40
41
      strom = Strommessung(); 
42
      spannung = Spannungsmessung();
43
      Tiefenmessung();      
44
    
45
      interrupt = 0;        //Interrupt wurde abgehandelt, warten bis wieder 1
46
    }
47
  
48
  }

von (prx) A. K. (prx)


Lesenswert?

Nö.

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.