Timer Input Capture Unit Problem

Gast #236823
Lesenswert?
• ▲
▼
Hallo,
bin mal wieder auf ein Problem gestoßen. Ich habe vor die Zeit zwischen
zwei Impulsen zu messen um darauß auf die Drehzahl eines Motors zu
kommen (mit ATmega16).
Klingt ja eigentlich einfach, dachte ich mir auch aber leider hackt es
noch.
Die Einstellungen:

TCCR1B = (1<<ICNC1) | (1<<ICES1) | (1<<CS11) | (1<<CS10);

//Noise Canceler -->ICNC1
//Aufsteigende Flanke -->ICES1
//Prescaler-->64

SREG |= (1<<7);  //Global Interrupt Enable (Muss das überhaupt sein?)

Ich habe mir vorgestellt die Input Capture Flag abzufragen. Wenn die
Flag gesetzt ist (1) dann wird doch das Zählergebnis von TCNT1L/H nach
ICR1L/H kopiert.

Aber die If-Anweisungen funktionieren irgendwie nicht:

if(ICF1==1){ //Input Capture Flag
...
das selbe bei der Overflow Flag
if(TOV1==1){
...
Ich bekomme keine Ausgabe über das LCD.
Im Anhang ist das Programm, darin enthalten ist noch eine ADC Routine
die ich aber mit einer while(0) zum testen übersprungen habe.
Gast #236826
Lesenswert?
• ▲
▼
Hi,

ich bin nun kein Profi werde aber versuchen Dir zu helfen.

>SREG |= (1<<7);  //Global Interrupt Enable (Muss das überhaupt sein?)

Das geht einfacher mit: sei();

>sreg = SREG;

Hierzu habe ich selber eine Frage: Wieso rettest du das SREG ? Das
sollte doch der Compiler selber machen.

Zu deinem eigentlichen Problem bin ich mir recht unsicher.

Ich wuerde dieses ganz anders machen.

Ich wuerde eine Variable nehmen und diese als Flag Register fuer deine
Sachen nehmen.

Beispiel:
#define ICF_FLAG 0
#define TIMER0_OVFFLAG 1



// Globale Variable als Flag Register
volatile unsigned char  Status_byte


SIGNAL(InputCapture)
{
  Status_byte |= (1<<ICF_FLAG);
}

SIGNAL(SIG_OVERFLOW0)
{
  Statusbyte |= (1<<TIMER0_OVFFLAG);
}

Jetzt kannst du in der Main diese Flags abfragen

int main(void)
{
   if (Status_byte & (1<<ICF_FLAG))
   { // ICF FLAG WURDE GESETZT }

   if (Status_byte & (1<<TIMER0_OVFFLAG))
   { // TIMER0_OVFFLAG WURDE GESETZT }
}

Mfg
Dirk
Gast #236827
Lesenswert?
• ▲
▼
Hallo Dirk,

das mit dem SREG hab ich aus dem AVR Tutorial entnommen. Als ich deinen
Vorschlag gelesen hatte, bin ich auf die Idee gekommen die If
Anweisungen so zu formulieren:

if(TIFR & (1<<ICF1)){
...
}
if(TIFR & (1<<TOV1)){
...
}
Das Ergebnis, er hat anscheindend die Overflow Flag erkannt und gibt
mir jetzt "Motor aus" auf dem LCD aus. Sobald ich mit einem Taster
ein Signal am ICP simuliere (schnelles betätigen des Tasters),
verschwindet die Meldung auch wieder. Aber eigentlich sollte er mir
anstatt eines leeren LCD die Drehzahl ausgeben.

Ansonsten werde ich mal deine Variante mit Interrupt ausprobieren.
Gast #236831
Lesenswert?
• ▲
▼
Ich habe den Fehler gefunden warum er nicht die if Anweisung mit ICF1
ausführt. Ich hatte noch den Interrupt für Input Capture (TICIE1)
"eingeschaltet".
Allerdings sind die Werte total kaotisch. Bei 200Hz (Signal an ICP)
z.B. bekomme ich -22528 RPM, dabei müssten es eigentlich genau 6000 RPM
sein.

RPM=78125/timer_wert*60

Wieso wird das Ergbnis manchmal negativ?
Bei 1kHz schwangt der Wert sehr stark und leider auch total falsch.
Gast #236832
Lesenswert?
• ▲
▼
Der neuste Stand:

Ich hab das ganze mal mit den Interrupts (Input Capture und Overflow)
umgeschrieben.
Ich lass mir jetzt auch den Timerwert ausgeben um besser zu debugen.
Den Code hab ich mir etwas abgeschaut und umgeschrieben.

Hier das Ergebnis:
Bei einer Frequenz von 200Hz (Signal) und der Frequenz 156,25kHz des
Timers, müsste der Timerwert (16Bit) genau 780 betragen. Aber auf dem
Display sind es nur 78 und die errechnete Umdrehung ist -25600!
Etwas eigenartig denn wenn ich richtig liege entspricht der Wert des
Timers = (1/200Hz)/(1/156250Hz) = 0,005s/0,0000064 = 780!
Was ist da Faul?

Hab ich einen Fehler in der Berechnung? Wieso bekomme ich solche Werte
bei der Umdrehung raus?
Angehängte Dateien:
Gast #236833
Lesenswert?
• ▲
▼
Hm, naja...

Möglicherweise wieder 'nur' ein Anzeigeproblem!?

[..]
float rpm=0;
[..]
sprintf(g_temp," RPM %d", rpm); // zwischen_erg anstatt rpm
[..]

Das passt nicht wirklich zusammen, oder? ;)
Probiere es mal mit

sprintf(g_temp," RPM %f", rpm); // zwischen_erg anstatt rpm
Gast #236834
Lesenswert?
• ▲
▼
Also daran liegt es nicht, allerdings hast du mich auf einen Fehler beim
deklarieren der Variablen hingewiesen!
Es muss natürlich so sein:

float zwischen_erg;
unsigned int rpm = 0;

Was ich nicht verstehe, selbst wenn der Inhalt vom Timer falsch ist
(und das ist er) müsste er mir doch trotzdem ein dazu passendes
Ergebnis errechnen. Macht er aber nicht. :-(

Bei den sprintf() Funktionen gibt er mir diese Warnung raus,

"implicit declaration of function `sprintf'"

hat das was zu bedeuten, also hat das Einfluß auf die Anzeige?
Gast #236835
Lesenswert?
• ▲
▼
Na super...

Du hast wahrscheinlich vergessen, die printf-Lib einzubinden (dem
Linker bekannt machen).
Trotzdem: unsigned int kann ebenfalls nicht mit dem %d Formatstring
korrekt angezeigt werden. Dafür gibt es %u!
Gast #236836
Lesenswert?
• ▲
▼
Ich muss dich enttäuschen, das Programm arbeitet Einwandfrei!
Ich hab den Fehler gefunden, das Problem lag am Clock ich hatte die
falschen fuse bits gesetzt.

Oh man bin ich froh das es funktioniert. :-)

Aber trotzdem an alle nochmal vielen Dank!!!!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren