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; } } }
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.
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.
Wenn ein Oszi vorhanden ist, dann zeig doch mal wie die Signale aussehen.
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
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.
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 ;-)
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?)
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.
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.
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.
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?
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
@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.
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
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.
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.