www.mikrocontroller.net

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


Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
 /****************************************************************/
//Initialisierung der ADC-Schnittstelle

void ADC_Init (void) 
{
  uint16_t result; 

  ADMUX = (1<<REFS0);                  // Uref = Avcc
  ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler 128
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
 
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
 
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
  while (ADCSRA & (1<<ADSC) );          // auf Abschluss der Konvertierung warten
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
     Wandlung nicht übernommen. */
  result = ADCW;
}



/****************************************************************/
// ADC Mehrfachmessung mit Mittelwertbbildung 

uint16_t ADC_Read_Avg(uint8_t channel, uint8_t average)
{
  uint32_t result;
 
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
 
  // Eigentliche Messung - Mittelwert aus 'average' aufeinanderfolgenden Wandlungen 
  result = 0; 
 
  for (uint8_t i = average; i>0; i-- ) {
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while (ADCSRA & (1<<ADSC) );    // auf Abschluss der Konvertierung warten
    result += ADCW;                 // Wandlungsergebnisse aufaddieren
  }
 
  result /= average;               // arithmethischer Mittelwert
  return (uint16_t)result;
}

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel schrieb:

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

Woran erkennst du das?

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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."

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: U.R. Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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....

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
und hier noch main:
/****************************************************************/
//Interrupt-Routine zum aktualisieren der Echtzeituhr
ISR(TIMER2_COMPA_vect)  //Bei Compare-Match mit OCR2A 
{
  interrupt = 1;        //RTC in main aufrufen lassen
}

/****************************************************************/
//MAIN-Funktion

int main (void)
 {

  uint16_t strom = 0;
  uint16_t spannung = 0;
  uint8_t endwert = 130;

  TIMSK2 = 0x00;                             //Interupts disabled
  ASSR    = (1<<AS2);                        //Externer Takt an TOSC1
  TCCR2A = (1<<WGM21);                      //Clear Timer OC2A on Comp match (CTC) 
  TCCR2B = (1<<CS22) | (1<<CS21) | (1<<CS20);//Prescaler auf 1024
  TIMSK2 = (1<<OCIE2A);                      //Compare match interrupt enable  
  OCR2A   = 31;                              //Zähler geht bis 32, dann Compare match
  sei();                                    //Global Interrupt enable

  
  LCD_Init();               // Display initialisieren
  ADC_Init();                //AD-Converter initialisieren
  PWM_Init();
  
  Display_Dimm(endwert);  

              
  while(1)                  // Endlosschleife
  {  
    // Wenn ein Interrupt ausgelöst wurde
    if (interrupt == 1)
    {
      RTC();                
      interrupt = 0;        //Interrupt wurde abgehandelt, warten bis wieder 1
    }
    Steuerung (0); 
    Steuerung (1);

    strom = Strommessung();       
    spannung = Spannungsmessung();    
    Tiefenmessung();        

    delay_ms(250);        
  }
}

Autor: gerd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel schrieb:
> // Wenn ein Interrupt ausgelöst wurde
>     if (interrupt == 1)
>     {
>       RTC();                
>       interrupt = 0;        //Interrupt wurde abgehandelt, warten bis wieder 1
>     }
>     Steuerung (0); 
>     Steuerung (1);
> 
>     strom = Strommessung();       
>     spannung = Spannungsmessung();    
>     Tiefenmessung();        
> 
>     delay_ms(250);

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

- gerd

Autor: gerd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Manuel (Gast)
Datum:

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

Autor: gerd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Manuel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bin der meinung, dass ich mir die sachen sogar durchgelesen hatte...wie 
dem auch sei, bekomm ich so weniger mecker? :)  :
int main (void)
 {

  uint16_t strom = 0;
  uint16_t spannung = 0;
  uint8_t endwert = 130;

  TIMSK2 = 0x00;                             //Interupts disabled
  ASSR    = (1<<AS2);                        //Externer Takt an TOSC1
  TCCR2A = (1<<WGM21);                      //Clear Timer OC2A on Comp match (CTC) 
  TCCR2B = (1<<CS22) | (1<<CS20);            //Prescaler auf 128
  TIMSK2 = (1<<OCIE2A);                      //Compare match interrupt enable  
  OCR2A   = 63;                              //Zähler geht bis 64, dann Compare match
  sei();                                    //Global Interrupt enable

  
  LCD_Init();               // Display initialisieren
  ADC_Init();                //AD-Converter initialisieren
  PWM_Init();
  
  Display_Dimm(endwert);  

              
  delay_ms(1000);

  while(1)                  // Endlosschleife
  {  
    // Wenn ein Interrupt ausgelöst wurde
    if (interrupt == 1)
    {
      i++;
      if ( i == 4 ) 
      {
        RTC();                //Dann Uhr aktualisieren 
        i = 0;
      }      

      Steuerung (0); 
      Steuerung (1);

      strom = Strommessung(); 
      spannung = Spannungsmessung();
      Tiefenmessung();      
    
      interrupt = 0;        //Interrupt wurde abgehandelt, warten bis wieder 1
    }
  
  }

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nö.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.