Forum: Mikrocontroller und Digitale Elektronik Frequenzmessung mit Atmega328 INT0 und INT1 / Interrups


von Andreas B. (chili0230)


Lesenswert?

Guten Tag,
Ich habe mich mal wieder mit Mikrokontrollern beschäftigt, und bin auch 
bereits sehr weit gekommen, ohne diese schöne Forum mit dummen Fragen zu 
besudeln.

Die Aufgabe besteht darin zwei Frequenzen zu messen.
an INT0 liegen Frequnezen bis 300Hz an.
an INT1 Frequnenzen bis 20kHz.

Es sollen von Flanke zu Flanke gemessen werden und die Ergebnisse (wenn 
zwei Messungen vorliegen) als auch die Zeit zwischen zwei Messungen über 
serielle Schnittstelle gesendet werden.

Bei dem ersten Test (200Hz) auf beide INTs kommt es auf INT0 zu 
sporadischen aussetzern, bzw. Sprüngen im Ergebniss. Das gleiche 
Passiert ab 1kHz auf INT1.

Das Problem wurde geringer, als ich die Prescaler wie im unten 
dargestellten Code eingeführt habe, jedoch kann das ja nicht Sinn und 
Zweck der Sache sein. Ausserdem verliehrt man ja Auflösung.

Auch habe ich versucht, während der Seriellen Kommunikation mit cli() 
und sei() global die Interrups auszuschalten (Am Anfang und Ende der if 
Schleife). Dies hat aber dazu geführt das ich keine vernümpftige Messung 
mehr machen konnte.

Ich denke das es etwas mit den Interrups zu tun hat. Ich wäre euch sehr 
Dankbar wenn ihr mit Tips geben könntet, wieso es nicht funktioniert.

Oszilloskop is vorhanden.

Danke


//----------------------------------------------------------
volatile int ErsteFlanke0=0;
volatile int ErsteFlanke1=0;
volatile uint32_t  StartTime0 = 0;        // ICR1-Wert bei 1.High-Flanke 
speichern
volatile uint32_t  EndTime0 = 0;          // ICR1-Wert bei 2.High-Flanke 
speicher
volatile uint32_t  StartTime1 = 0;        // ICR1-Wert bei 1.High-Flanke 
speichern
volatile uint32_t  EndTime1 = 0;          // ICR1-Wert bei 2.High-Flanke 
speicher
volatile uint32_t  time0 = 0;          //
volatile uint32_t ovlTIM0=0;
volatile int ovlTIM1=0;
volatile int ovlTIM2=0;
volatile int ovlTIM1S=0;
volatile int ovlTIM2S=0;
volatile int Messung0=1;
volatile int Messung1=0;




 int main(void){
      cli();

//--------------------Reset all---------------------
//Timer0
  TCCR0A= 0x00;
  TCCR0B= 0x00;
  TIMSK0= 0x00;
//Timer1
  TCCR1A= 0x00;
  TCCR1B= 0x00;
  TCCR1C= 0x00;
  TIMSK1= 0x00;
 //Timer2
  TCCR2A= 0x00;
  TCCR2B= 0x00;
  TIMSK2= 0x00;

  EICRA = 0x00;

  EIMSK |= (1<<INT0); //enable INT0 interrupts
  EIMSK |= (1<<INT1); //enable INT0 interrupts
  EICRA |= ((1<<ISC01)|(ISC00)); //rising edge of INT0
  EICRA |= ((1<<ISC11)|(ISC10)); //rising edge of INT1

  // TIMER 1
  TIMSK1 |= (1<<TOIE1);                 // Timer 1 Overflow
  TCCR1B |= ((1<<CS10)|(1<<CS11));
  TCCR1B &= ~(1<<CS12);                 //64 prescaler, timer 1 and 
START*/

    //TIMER 0
  TIMSK0 |= (1<<TOIE0);                 // Timer 0 Overflow
  TCCR0B |= (1<<CS01);
  TCCR0B &= ~((1<<CS02)|(1<<CS00));     //8 prescaler, timer 0 and 
START*/

  //TIMER 2
  TIMSK2 |= (1<<TOIE2);                 // Timer 2 Overflow
  TCCR2B |= ((1<<CS20)|(1<<CS21));
  TCCR2B &= ~(1<<CS22);                 //32 prescaler, timer 2 and 
START

//----------Port definition----------
  DDRD |= (1<<PD7) ; // OUTPUT Debug Pin
 // DDRD |= (1<<PD6) ;
  //PORTD ^= (1<<PD7); // DEBUG PIN on PIN7a

  DDRD &= ~(1<<PD2) ; // INPUT for INT0
  PORTD |= (1<<PD2);

  DDRD &= ~(1<<PD3) ; // INPUT for INT1
  PORTD |= (1<<PD3);
//----------SERIAL---------
  Serial.begin(250000);

  sei();
   uint32_t pulse1;
   float freq1;
   uint32_t pulse0;
   float freq0;
   float time2m;

  while(1)  {

    if (Messung1 == 1 && Messung0 ==1  ){
       TCCR1B= 0x00;
       TCCR2B= 0x00;

     time2m=(ovlTIM0*256)+time0;
     time2m=time2m/F_CPU*8;
     ovlTIM0=0;

    pulse1 = EndTime1-StartTime1+(ovlTIM1*65536);
    freq1 = F_CPU/pulse1/64;
    pulse0 = EndTime0-StartTime0+(ovlTIM2*256);
    freq0 = F_CPU/pulse0/32;

   Serial.print(time2m, DEC);
   Serial.print(";");
   Serial.print(freq0, DEC);
   Serial.print(";");
   Serial.print(freq1, DEC);
   Serial.print('\n');
   _delay_ms(50);
  TCNT2=0;
  TCNT1=0;
  TCCR1B |= ((1<<CS10)|(1<<CS11));
  TCCR1B &= ~(1<<CS12);                 //64 prescaler, timer 1 and 
START*/
  TCCR2B |= ((1<<CS20)|(1<<CS21));
  TCCR2B &= ~(1<<CS22);                 //32 prescaler, timer 2 and 
START
  Messung1=0;
  Messung0=0;

   }
  }
 }

ISR(TIMER0_OVF_vect)
   {
    ovlTIM0++;
}

ISR(TIMER1_OVF_vect){
    if(Messung1==0){
    ovlTIM1++;

    }
}
 ISR(TIMER2_OVF_vect){
   if(Messung0 == 0){
    ovlTIM2++;
   }
}




ISR(INT0_vect){
   if(Messung0 == 0){
    if( ErsteFlanke0==1){
  StartTime0 = TCNT2;
  ovlTIM2 = 0;
  ErsteFlanke0 = 0;       // Die naechste Flanke ist das Ende der 
Messung
  }
  else{
   EndTime0 = TCNT2;
   Messung0=1;         // Eine vollstaendige Messung. Sie kann 
ausgewertet werden
   ErsteFlanke0 = 1;    // Bei der naechsten Flanke beginnt der naechste 
Messzyklus
  }
 }
}

ISR(INT1_vect){
 if (Messung1 ==0){
 if( ErsteFlanke1==1){
  StartTime1 = TCNT1;
  ovlTIM1 = 0;
  Messung1=0;
  ErsteFlanke1 = 0;       // Die naechste Flanke ist das Ende der 
Messung
  }
  else{
   EndTime1 = TCNT1;
   Messung1=1;         // Eine vollstaendige Messung. Sie kann 
ausgewertet werden
   ErsteFlanke1 = 1;    // Bei der naechsten Flanke beginnt der naechste 
Messzyklus
   time0=TCNT0;
  }
 }
}

von Amateur (Gast)


Lesenswert?

20 KHz "Frequnenzen" am INT? bedeutet, bei der Verwendung einer 
Hochsprache, leicht mehr als 100 Taktzyklen pro Dingsbums. Also schnell 
mal 2000000 Takte pro Sekunde - somit etwa 10% dessen, was zur überhaupt 
Verfügung steht.
Kommen dann noch "Asynchron" dazu, 300 Stolperer pro Sekunde – also 
30000 Takte jede Sekunde - hinzu, so sollte der Prozessor die restliche 
Zeit höchstens Däumchen drehen.

von Nobody (Gast)


Lesenswert?

Andreas B. schrieb:
> Bei dem ersten Test (200Hz) auf beide INTs kommt es auf INT0 zu
> sporadischen aussetzern, bzw. Sprüngen im Ergebniss. Das gleiche
> Passiert ab 1kHz auf INT1.

Ich sehe ganz viele volatile Variablen, aber nirgendwo, dass sie auch 
ATOMIC ausgelesen werden.
So wird das (sporadisch) zu Fehlern führen.
Quasi mit eingebauter Fehler Garantie.

von Sascha (Gast)


Lesenswert?

Wenn ein Oszi vorhanden ist, dann zeig doch mal wie die Signale 
aussehen.

von Andreas B. (chili0230)


Lesenswert?

Der ersten Antwort kann ich keinerleih konstruktiven Beitrag entnehmen.
@Nobody:
Ich dachte das Variablen die in einer ISR beschrieben werden als 
volatile declariert werden müssen.
Liege ich hier falsch?

@Sache: Was soll das bringen? Das eine hat 200Hz mit 20% duty cycle das 
Andere 1kHz mit 50% duty cycle.
Danke

von Lurchi (Gast)


Lesenswert?

Wenn beide Interrupts zusammenfallen können sie sich stören, weil eine 
kleine Verzögerung dazu kommt. Das selbe kann auch mit dem Interrupt vom 
timer überlauf passieren. Das kann es also gelegentlich zu kleinen 
Fehlern kommen.

Seltener kann ein großer Fehler austreten, wenn der Interrupt vom Signal 
zusammen mit dem Timer Überlauf auftritt. Da liegt man dann ggf. um 
einen Überlauf daneben - das sind dann seltene große Fehler. Beim Timer 
0 dürfte der Fehler schon relativ oft auftreten. Den Fehler kann man 
abfangen.

Auch wenn man zwei Frequenzen messen will, kommt man auch mit einem 
Timer aus.

Für eine genaue Zeitmessung hat der Timer1 die input capture Funktion. 
Damit kann man eine genaue Messung bis auf +-1 Zyklus machen.

von m.n. (Gast)


Lesenswert?

Ohne den Code näher angesehen zu haben, so geht es nicht.
Nimm nur Timer1 zur Zeitmessung und lasse ihn frei durchlaufen.
Ein Beispiel, wie man es machen kann, findest Du hier: 
Beitrag "4-Kanal Drehzahlmessung mit ATmega88"
Bring ein wenig Zeit mit, um die Funktion nachzuvollziehen ;-)

von Nobody (Gast)


Lesenswert?

Andreas B. schrieb:
> Ich dachte das Variablen die in einer ISR beschrieben werden als
> volatile declariert werden müssen.
> Liege ich hier falsch?
Das ist total richtig!
Und das habe ich auch nicht angezweifelt!

Ich sprach vom auslesen dieser Variablen in der Hauptschleife.
Das muss ATOMIC erfolgen!
(Bin ich ein Papagei?)

von Andreas B. (chili0230)


Lesenswert?

Hallo Lurchi,
Das heist ich sollte eine Frequenz mit dem ICP und Timer 1 messen?
Dennoch benötige ich doch zwei Timer, da ich ja für jedes Signal den 
Overflowflow mitbestimmen muss. Oder denke ich zu kompliziert?

Oder ein Signal mit dem ICP auswerten und den Overflow bei einer neuer 
Messung auf Null setzen. Das zweite Signal dann beim INT0 Interrupt, 
Timer1 auslesen und den Stand der Overflowvariable mir merken?

Hmm..das klingt gut.

von Mein grosses V. (vorbild)


Lesenswert?

Andreas B. schrieb:
> Hmm..das klingt gut.

Nein, das klingt beschissen, weil es totaler Unsinn ist.

Die 20KHz misst du über den ICP-Pin des Timer1. Prescaler = 1.

Für die 200Hz-Messung stellst du den Prescaler auf 8 und setzt den 
Analog-Comparator als Trigger für den  Input Capture.

Overflow vergisst du ganz schnell und tust so, als hättest du noch nie 
etwas davon gehört.

von Lurchi (Gast)


Lesenswert?

Wenn man mit 16 Bit Auflösung klar kommt, sollte man ohne den Overflow 
arbeiten. Sonst sucht man sich im Netz eine Lösung wo der Overflow 
richtig berücksichtigt wird. Das geht, ist aber halt nicht ganz einfach.

So wie das Programm ist, ließt man bei der Flanke die Zeit aus und merkt 
sie sich. Die Periodenlänge ist dann halt die Differenz der Zeiten. 
Dabei läuft der Timer ja ungestört weiter, d.h. man kann mit der einen 
Uhr auch 2 oder mehr Frequenzen gleichzeitig bestimmen. Bei der Lösung 
mit ICP hat man aber nur eine Einheit und muss es dann halt nacheinander 
machen.

von Andreas B. (chili0230)


Lesenswert?

Ich muss overflows beachten da die frequenz bei wenigen Herz los geht.

Ok icp mache ich, aber wieso soll ich den analog comparator als trigger 
nehmen. Wieso nicht den INT?

Wieso wird es mit ICP besser funktionieren als mit INT?

von Mein grosses V. (vorbild)


Lesenswert?

Andreas B. schrieb:
> Wieso wird es mit ICP besser funktionieren als mit INT?

Wenn der ICP, je nach Einstellung, auf H oder L geht, wird TCNT1 im ICR1 
"eingefroren". Bis über einen externen Interrupt der TCNT1 ausgelesen 
wird, vergeht jedoch einige Zeit. Vor allen Dingen vergeht nicht immer 
die gleiche Zeit.

Da du 2 Frequenzen von verschiedenen Quellen messen willst, brauchst du 
auch 2 Eingänge. Der Controller hat aber nur einen ICP-Eingang. 
Allerdings kann der AC auch den Input Capture triggern. Damit kannst du 
dir einen zweiten ICP-Eingang "basteln". Der kommt zwar verzögert 
gegenüber dem richtigen ICP, aber die Verzögerung ist immer die gleiche. 
Und bei 200Hz hast du ohnehin alle Zeit der Welt zur Auswertung.

Einziger Nachteil ist, daß die Messungen nicht zusammen erfolgen können. 
Das sollte aber in der Praxis keine Rolle spielen. Denn wenn das für die 
20KHz-Messung von Bedeutung wäre, hättest du ohnehin kaum Zeit für etwas 
anderes.

In jedem fall sind beide Messungen maximal genau. Wenn du ohnehin den 
Overflow auswerten musst, kann die 200Hz-Messung auch mit Prescaler = 1 
erfolgen.

: Bearbeitet durch User
von Andreas B. (chili0230)


Lesenswert?

@Mein grosses Vorbild:
Danke für die Erklärung.
Also 20kHz mit ICP und Timer1
200Hz mit AC und input capture und Timer1
Overflow: Richtige Overflow Behandlung programmieren. (Tips/Beispiele?)

Was ich noch nicht ganz verstehe ist wieso kann ich beide Signale nicht 
gleichzeitig auswerten.
Gibt es dann nur ein ISR für ICP und AC?

Danke für deine Hilfe.

von Mein grosses V. (vorbild)


Lesenswert?

Andreas B. schrieb:
> Was ich noch nicht ganz verstehe ist wieso kann ich beide Signale nicht
> gleichzeitig auswerten.
> Gibt es dann nur ein ISR für ICP und AC?

Über beide Eingänge wird der ICP-INT ausgelöst. Das ist aber nicht das 
eigentliche Problem, sondern du hast nur ein ICR-Register, in dem TCNT 
eingefroren wird.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Andreas B. schrieb:
> Ich dachte das Variablen die in einer ISR beschrieben werden als
> volatile declariert werden müssen.
> Liege ich hier falsch?

Nein, da liegst du richtig. Wenn du diese Variablen aber ausliest musst 
du sicher stellen, dass sie während des Ausleseprozesses nicht neu 
beschrieben werden. Der Atmega kann nämlich immer nur 8-bit-Register 
lesen. Hast du z.B. eine 16-bit-Variable wird diese in zwei Schritten 
ausgelesen und es wäre ja irgendwie blöd wenn beim Lesen des ersten 
Teils die ISR anfängt die Variable neu zu beschreiben. Also verhindern, 
dass während des Auslesens die ISR die Variable neu beschreibt. Am 
Einfachsten ist das indem man direkt vor dem Auslesevorgang den 
entsprechenden Interupt für die ISR, die die Variable beschreibt, 
deaktiviert und direkt nach dem Auslesen der Variable den entsprechenden 
Interrupt wieder aktiviert.

von Lurchi (Gast)


Lesenswert?

Wenn man kleine Fehler bei der niedrigen Frequenz akzeptiert, kann man 
auch die höhere Frequenz per ICP auslesen und die niedrigere Frequenz 
per Interrupt und auslesen des Timers per Hand nutzen. Auch da kann man 
beides mal Timer1 nutzen - das Auslesen der Zeit stört die Zeitmessung 
ja nicht, wenn man auch bei der Zahl der Überläufe den Wert ausließt und 
nicht zurücksetzt.

Beim Auslesen von Hand hat man halt eine kleine Unsicherheit, weil ggf. 
eine ISR dazwischen kommen, bevor man den Timer auslesen kann. Das könne 
schon mal an die 100 Zyklen werden - für nur 200 Hz vielleicht nicht so 
das große Problem. Je nachdem wo das Signal her kommt ist ja auch da 
ggf. Jitter drauf. Ob man immer in Wechsel messen kann, hängt halt von 
der Anwendung ab.

von m.n. (Gast)


Lesenswert?

Andreas B. schrieb:
> Overflow: Richtige Overflow Behandlung programmieren. (Tips/Beispiele?)

Ich habe Dir oben einen kompletten Code gezeigt: 4 Kanäle gleichzeitig 
und jeweils 5-stellige Auflösung. ICP ist dabei noch frei geblieben. 
Ohne genauen, stabilen Quarz (TCXO) reicht das völlig aus.

Ist aber vielleicht doch etwas zu schwierig zu verstehen.

von Andreas B. (chili0230)


Lesenswert?

Das ist schon ok.
Wenn es da richtig gemacht ist, dann kriege ich das schon hin.
Vielen Dank für eure Hilfe. Ich berichte über meinen fortschritt.

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.