Hallo.
Ich mache mit MEGA128 eine Frequenzmessung. Die Frequenz kann dabei
zwischen 1Hz und 20Hz liegen, bleibt aber während der Messung konstant.
Ich habe das mit INT2 und 16bit-Counter ralisiert. FCPU ist 16MHz.
Cunter-Prescaler setze ich auf 1024.
Hier ist ein Ausschnitt meines Programms:
1
voidmain(void)
2
{
3
while(1)
4
{
5
uint8_ttemp,flag;
6
7
//if 2 rising adge occured
8
if(cnt==2)
9
{
10
//stop counter
11
TCCR1B=0x00;
12
13
temp=SREG;
14
cnt=0;
15
cli();
16
17
//disable INT2
18
EIMSK&=~(1<<INT2);
19
20
//save counter-value
21
uint16_tTimer;
22
Timer=(uint16_t)(TCNT1L+((uint16_t)(TCNT1H<<8)));
23
24
//write once to eeprom
25
if(flag!=5)
26
{
27
eeprom_write_block(&Timer,0x80,sizeof(Timer));
28
flag=5;
29
}
30
31
//init TCNT1
32
TCNT1L=0;
33
TCNT1H=0;
34
35
//pull SREG
36
SREG=temp;
37
}
38
39
if(flag!=5)
40
{
41
42
temp=SREG;
43
cli();
44
45
//enable interrupt
46
EIMSK|=(1<<INT2);
47
48
//rising adge
49
EICRA|=(1<<ISC20)|(1<<ISC21);
50
51
SREG=temp;
52
}
53
}
54
return0;
55
}
hier fange ich die steigenden Falnken, und zähle sie ab.
1
ISR(INT2_vect)
2
{
3
//start counter
4
if(TCCR1B!=0x05)
5
{
6
GTCCR|=(1<<TSM);
7
8
//Prescaler = 1024
9
TCCR1B=0x05;
10
}
11
12
//count rising edges
13
cnt++;
14
}
Die Frequenzwerte speichere ich im EEPROM und schaue sie mir nachher an.
Meiner Idee nach muss die Umrechnung so sein:
zumessenefreq = FCPU/Prescaler/CounterValue
= 16MHz/1024/Timer
Mein Problem: Meine berechnete Frequenz ist immer 8 mal so groß wie die
tatsächliche. Eigentlich habe ich alles richtig gemacht. Stimmt evetuell
der Precsaler im Datenblatt nicht? Kann ich mir fast nicht vorstellen..
Meine FCPU ist 100%tig 16MHz. Prescaler ist auf 1024 gesetzt. Irgendwas
stimmt trotzdem nicht.
Hab mich mittlerweile bissle verrant in der Sache. Ein Blick von der
Seite würde evtl. schneller den Bug erkennen.
Sieht jemand was ich falsch gemacht hab? Danke im Voraus...
Lass dir erstmal den Ermittelten COunterwert ausgeben.
Also wieviele Takte werden denn gemessen.
Die Umrechnung erfolgt dann so:
f(1/min) = 937500 / X
f(1/sek) = 15625 / X
X: gemessene Timercount zwischen DENSELBEN Flanken..
Siehe Rechnung im vorletzten Post:
Beitrag "Periodendauer messen und in Frequenz umwandeln"
Du meinst also die umrechung stimmt nicht?
>Lass dir erstmal den Ermittelten COunterwert ausgeben.
Die Werte speichere ich im EEPROM und schaue sie mir nachher an.
>Also wieviele Takte werden denn gemessen
Ich messe zwei steigende Flanken, also ein Takt der zu messenden
Frequenz.
Dann speichere ich die Werte im EEPROM. Nachdem die ganze Messung vorbei
ist. Schaue ich EEPROM an und rechne (noch) manuel die zu messende
Frequenz aus.
>Die Umrechnung erfolgt dann so:>f(1/sek) = 15625 / X
genau so mach ich doch auch: 15625 = 16MHz/1024
z.B
bei 10Hz bekomme ich X=195 entspricht f=80 (also um faktor 8 größer
bei 1Hz bekomme ich X=1953 entspr. f=8 (also um faktor 8 größer)
Also die Messung an sich scheint zu stimmen, nur habe ich eine
OFFSET-Steigung = 8
Da sie konstant ist, kann ich sie problemlos rausrechnen, aber mir ist
ihre ursache nicht ganz klar.
> Dann speichere ich die Werte im EEPROM. Nachdem die ganze Messung vorbei> ist. Schaue ich EEPROM an und rechne (noch) manuel die zu messende> Frequenz aus.
Du weißt, dass das EEPROM nur eine begrenzte Anzahl Lösch-Schreib-Zyklen
hat? Wenn bei Messungen permanent Daten dort gespeichert werden, kann
das recht schnell zu Problemen führen. Ins EEPROM sollte man nach
Möglichkeit nur schreiben, wenn es unbedingt erforderlich ist.
@ Johannes M. (johnny-m)
>das recht schnell zu Problemen führen. Ins EEPROM sollte man nach>Möglichkeit nur schreiben, wenn es unbedingt erforderlich ist.
Ausserdem ist es relativ langsam. Zum Debuggen sollte man lieber den
SRAM verwenden, ist auch einfacher und logischer.
MfG
Falk
Ist cnt volatile? Außerdem: Was machst Du da mit dem SREG? Du weißt gar
nicht, was man mit solchen Manipulationen am SREG anrichten kann. Es
kann passieren, dass Du damit dem Compiler ins Handwerk pfuschst. Als
Hochsprachen-Programmierer hat man am SREG eigentlich nichts zu suchen.
Einzig das Setzen und Löschen des I-Bits mittels sei() und cli() ist
gestattet!
> TCNT1L = 0;> TCNT1H = 0;
Und da haben wir vermutlich die Ursache (oder zumindest einen Teil
davon) für die Fehlfunktion... Das High-Byte des TCNT1 MUSS vor dem
Low-Byte geschrieben werden. Allerdings bieten sämtliche mir bekannten
Compiler die Möglichkeit, diese 16-Bit-Register als ganzes anzusprechen.
Dann nimmt einem der Compiler die Sache mit der Reihenfolge ab. Und das
sollte man auch nutzen. Also
1
TCNT1=0;
EDIT:
Zitat aus einem AVR-Datenblatt:
"To do a 16-bit write, the High byte must be written before the Low
byte. For a 16-bit read, the Low byte must be read before the High
byte."
NOCH EIN EDIT:
Auch wenn das obige Problem sicher zu einer Fehlfunktion führen dürfte,
steht immer noch die Frage im Raum, warum es immer der 8-fache Wert ist,
der rauskommt. Das ist dadurch, soweit ich das jetzt übersehe, nicht
erklärbar.
Vieleicht ergibt sich bei 5hz ein anderer Offset... Zwei Beispiele sind
denk ich keien Garantie das es IMMER *8 ist...
Außerdem würd ich die abfrage ob count==2 ist lieber in den Interupt
paken!
Danke für die kritische Resonanz.
>Du weißt, dass das EEPROM nur eine begrenzte Anzahl Lösch-Schreib-Zyklen>hat?
JA, ca 100 000 Zyklen sind garantiert. Ich schreibe ja nicht permanent
über längere Zeit. Sondern beschreibe nur einmal lediglich 4 Zellen die
ich später nie wieder brauche.
>Ist cnt volatile?
nein, danke für hinweis
>Außerdem: Was machst Du da mit dem SREG?
kommt aus dem Datenblatt S.124: Sie nennen es atomic write
1
unsignedcharsreg;
2
unsignedinti;
3
/* Save global interrupt flag */
4
sreg=SREG;
5
/* Disable interrupts */
6
__disable_interrupt();
7
/* Set TCNTn to i */
8
TCNTn=i;
9
/* Restore global interrupt flag */
10
SREG=sreg;
> TCNT1L = 0;> TCNT1H = 0;
habe ich total übersehen. Werde sofort korigieren.
Ich vermute, dass der Prescaler nicht richtig gesetzt wird.
>Vieleicht ergibt sich bei 5hz ein anderer Offset... Zwei Beispiele sind>denk ich keien Garantie das es IMMER *8 ist...
Hier sind meine Messergebnisse:
tatsächliche freq. TimerWert 16MHz/1024/8/TimerWert
1Hz 1953 1,000064Hz
2Hz 976 2,00115Hz
3Hz 651 3,00019Hz
4Hz 480 4,0690Hz
5Hz 390 5,0080Hz
6Hz 325 6,0096Hz
10Hz 195 10,016Hz
20Hz 97 20,135Hz
>Außerdem würd ich die abfrage ob count==2 ist lieber in den Interupt>paken!
Wäre evtl. besser/ klienere Fehler
Leigt bestimmt am Prescaler
Karl wrote:
>>Außerdem: Was machst Du da mit dem SREG?> kommt aus dem Datenblatt S.124: Sie nennen es atomic write>
1
>unsignedcharsreg;
2
>unsignedinti;
3
>/* Save global interrupt flag */
4
>sreg=SREG;
5
>/* Disable interrupts */
6
>__disable_interrupt();
7
>/* Set TCNTn to i */
8
>TCNTn=i;
9
>/* Restore global interrupt flag */
10
>SREG=sreg;
11
>
Das macht aber auch nur dann Sinn, wenn man nicht zu 100% die Kontrolle
über die Interrupt-Freigabe hat. I.d.R. genügt ein cli() vor und ein
sei() nach dem 16-Bit-Zugriff. Wie gesagt, in 99,9% der Fälle sollte man
vom SREG die Finger lassen (was direkte Zugriffe angeht).
> Ich vermute, dass der Prescaler nicht richtig gesetzt wird.
Aha. Wie kommst Du da drauf?
BTW:
> Timer = (uint16_t)(TCNT1L+((uint16_t)(TCNT1H<<8)));
Auch hier ist die Lesereihenfolge nicht gewährleistet. Wenn Du das noch
nicht geändert hast, dann hol das bitte nach. Nimm auch bei dieser
Operation das 16-Bit-Register.
Ca ab hier, hat die gemessene Frequenz größeren Fehler, was auf die
Laufzeiten zurückzuführen ist.
ferq. Timer-Wert berechnet
60Hz 30 65,104Hz
100Hz 14 139,508Hz
Aber in meinen Anwendung kommen Frequenzen von 0,5Hz-10Hz vor.
@ Karl (Gast)
>Ca ab hier, hat die gemessene Frequenz größeren Fehler, was auf die>Laufzeiten zurückzuführen ist.
AUA. Wie kann man mit einem 16MHz!!! uC so einen riesigen Fehler bei
dermassen niedrigen Frequenzen machen. Unverständlich.
MfG
Falk
>> Ich vermute, dass der Prescaler nicht richtig gesetzt wird.>Aha. Wie kommst Du da drauf?
Ist eine Vermutung, was sonst kann das sein. Wenn ich konstant statt
1024 den Wert 128 (1024/8) habe...
>> Timer = (uint16_t)(TCNT1L+((uint16_t)(TCNT1H<<8)));>Auch hier ist die Lesereihenfolge nicht gewährleistet. Wenn Du das noch>nicht geändert hast, dann hol das bitte nach. Nimm auch bei dieser>Operation das 16-Bit-Register.
Aber Timer ist doch 16Bit variable, oder meinst du ich sollte das
machen:
Karl wrote:
> Aber Timer ist doch 16Bit variable, oder meinst du ich sollte das> machen:>
1
>Timer=TCNT1;
2
>
Genau! Es ist auch beim Lesen die Reihenfolge zu beachten (s.o.). Und
bei der Operation mit Shift und Typkonversion hat der Compiler im
Prinzip die Freiheit, die Reihenfolge beliebig zu wählen. Es ist deshalb
nicht gewährleistet, dass die beiden Register in der richtigen
Reihenfolge gelesen werden.
>AUA. Wie kann man mit einem 16MHz!!! uC so einen riesigen Fehler bei>dermassen niedrigen Frequenzen machen. Unverständlich.
Danke dir für deine Sachlichkeit und Konstruktivität deines Betrages.
>and CKDIV8 programmed
war tatsächlich programmiert = internal Clockdevision by 8
Das erklärt den OFFSET = 8.
Allerdings sind die Messwerte deutlich ungenauer: Ich bekomme statt 1Hz
1,3Hz und statt 5Hz 8,7Hz. Dann stimmt meine Frequenz vorne und hinten
nicht... :)
>Hat der AVR nicht nen ICP interupt?
Das hat er, kann ich aber nciht nutzen, da mein signal nciht an diesen
ICP-Pin angeschlossen ist.
>Oder miß doch eine Sekunde lang wieviel Pulse kommen...
Wenn die "Sekunde" nicht stimmt, macht es kein unterschied: Zeitmessen
für bestimmte Flankenzahl oder umgekehrt..
ein simples count++ im interupt ist aber denk ich problemloser als dein
konstrukt. Außerdem kreigst du so nen Mittelwert und dein Fehler sollte
kleiner sein ebenso sparrst du dir natürlich die umrechnung
Bei so niedrigen Frequenzen ist ein Fehler von 30% aber nicht erklärbar.
Wie sieht das denn mit der Umrechnung aus? Machst Du die auch im
Programm oder hast Du bisher nur die Werte von Hand ausgerechnet?
>Wie sieht das denn mit der Umrechnung aus? Machst Du die auch im>Programm oder hast Du bisher nur die Werte von Hand ausgerechnet?
Die Umrechnung mach ich nicht im Programm. d.h. Der Timerwert wird roh
an PC Übertragen und dort nehme ich die Umrechnung vor.
@ Karl (Gast)
>>AUA. Wie kann man mit einem 16MHz!!! uC so einen riesigen Fehler bei>>dermassen niedrigen Frequenzen machen. Unverständlich.>Danke dir für deine Sachlichkeit und Konstruktivität deines Betrages.
Immer wieder gern geschehn. ;-)
Warum einfach, wenn es auch umständlich geht.
16 Mhz Takt (was hier zumindest Overkill ist).
Prescaler 1024 macht ~16 kHz.
Bei 10 Hz macht das eine Auflösung von 1600 Zählern ~0,6 Promille.
Bei 16 kHz braucht ein 16 Bit-Zähler ca. 65536/16kHz ~4 Sekunden bis zum
Overflow. Passt.
Alles was man mun tun muss ist die Differenz der Zählerstände zu
berechnen. Das wars. Im Falle eines Overflows ist die negativ, dann muss
invertiert werden. Das ist der ganze "Trick".
1
volatileuint16_tdelta_t;
2
uint16_told_time;
3
volatileuint8_tflag;
4
5
voidmain(void)
6
{
7
// timer initialisieren
8
9
TCCR1B=5;// Prescaler 1024
10
11
// interrupt initialisieren
12
13
TIMSK|=(1<<TOIE1);// Overflow Interrupt ON
14
15
sei();
16
17
while(1)
18
{
19
while(flag==0);// Warte auf Messung
20
flag=0;
21
22
// hier jetzt die Auswertung/Speicherung des Messwertes delta_t
23
}
24
}
25
26
// steigende Flanke an INT2
27
28
ISR(INT2_vect)
29
{
30
uint16_ttmp,tmp1;
31
32
tmp=TCNT1;
33
tmp1=tmp-old_time;
34
if(tmp1>32768)tmp1-=32768;
35
old_time=tmp;
36
delta_t=tmp1;
37
flag=1;
38
}
@ Läubi Mail@laeubi.de (laeubi)
>Hat der AVR nicht nen ICP interupt?
Ja.
>Ich denk das sit für Frequenzmessung genauer... Oder miß doch eine
Für DIE Frequenzen tuts auch der externe Interrupt.
>Sekunde lang wieviel Pulse kommen...
Tut er nicht, aber er macht es sehr kompliziert und falsch.
MFG
Falk
Schaut schonmal besser aus.
Ich würde in der ISR nen Flag setzen wenn die Messung abgeschlossen ist,
und dann in der Mainloop ins EEPRom schreiben und dann eien neue Messung
starten.
Ich bin der Ansicht dass man den ICP pin auf alle Faelle benutzen
sollte. Da kann dann prinzipiell nichts daneben gehen;:-)
Fakten:
Timer laeuft mit Prescaler vom 16MHZ Zeitbasis, steuert den 16-Bit Timer
mit genauem Takt im MHZ Bereich. Bei gewaehlter 1MHz Taktrate also 1us
Aufloesung der Periodenmessung des Eingangssignal.
Eingangsignal an ICPn anschliessen. (Auf minimalen Jitter achten) Die
Flankensteilheit des Sihnals muss entsprechend aufbereitet sein.
ICP Interrupt ist fuer fallende oder steigende Flanke des Eingangsignal
programmiert. (Z.B. Fallende Flanke)
Mit dem Timer overflow Interrupt zaehlen wir die Ueberlaeufe.
Wenn nun das Eingangssignal von H->L geht, wird der jeweilig
Timerzaehlstand in den capture rgeister uebernommen.
Jetzt haben wir n[start].
Dann warten wir bis der naechste Capture ankommt. Die Periodenmessung
ist
nun komplett mit n[stop].
Die Periodendauer t = n[stop] - n[start] * n[overflow] * 65536
(Wegen der 16-bit Variable und captur register braucht man sich nicht um
uebrlaufe bei der Subtraktion stoeren da die overflows ja mitbeeinzogen
werden)
Frequenz ist 1/t.
Jetzt auf die gewuenschten Masseinheiten umrechnen - Fertig.
Sorry fuer das Wiederkauen, aber ich dachte es ist vielleicht mal
nuetzlich sich das in dieser Weise vor den Augen zu halte - Also bitte
nicht schimpfen.)
Ich mache das bei meinen Programmen mit PIC und AVR immer so, und hat
bei mir immer problemlos funktioniert. Die Genauigkeit bei den niedrigen
Frequenzen ist extrem hoch. Bei 1Mhz Taktfrequenz des TIMER1 kann man
1Hz auf 0.000001 Hz genau aufloesen.
Habe das mit einer Praezisonszeitbasis auch gemessen und bestaetigen
koennen. Die Messgenaugikeit sinkt aber mit groesser werderender
Eingangsfrequenz ab. BEi 10Khz ist das Bild natuerlich nicht mehr so
schoen.
Ich bin jedenfalls der Ansicht, dass man fuer solche Zwecke unbedingt
input capture benutzen sollte. Wie gesagt ich will hier niemand auf die
Fuesse steigen. dachte aber meinen Senf dazu beitragen.
Gruss,
Gerhard
>16 Mhz Takt (was hier zumindest Overkill ist).
Sag mal warum das Overkill ist? Du weißt doch gar nicht was ich sonst
mit dem µC mache?
>Bei 16 kHz braucht ein 16 Bit-Zähler ca. 65536/16kHz ~4 Sekunden bis zum>Overflow. Passt.
Habe ich vorher gerechnet, deshalb auch 16Bit zähler
>Alles was man mun tun muss ist die Differenz der Zählerstände zu>berechnen. Das wars. Im Falle eines Overflows ist die negativ, dann muss>invertiert werden. Das ist der ganze "Trick".
Meine zu messende Frequenz ist min. 1Hz das heißt t=1s. In dieser Zeit
erreicht der Zähler bei einer frequenz von 16Mhz/1024 ~ 16kHz max ca.
16000, da er nur eine sekunde lang mißt, kommt also nicht zum overflow.
also kein trick...
>Tut er nicht, aber er macht es sehr kompliziert und falsch.
Deine Beiträge sind sehr lustig. :) Sag mal,
Warum ist es kompiziert und falsch Zeit zwischen zwei Flanken zu messen
und daraus die Frequenz zu errechnen?
4sec eine 1Hz Frequenz zu messen, dass finde ich Overkilled. Aber es ist
nicht falsch...
>Ich bin der Ansicht dass man den ICP pin auf alle Faelle benutzen>sollte. Da kann dann prinzipiell nichts daneben gehen;:-)
Das Projekt wurde eben nicht so ausgelegt, dass ich ICP nutzen kann.
Auch ich muss das als gegeben hinnehmen...
>Sorry fuer das Wiederkauen, aber ich dachte es ist vielleicht mal>nuetzlich sich das in dieser Weise vor den Augen zu halte - Also bitte>nicht schimpfen.)
Ich schipfe grundsätzlich nicht...
Also ein normaler Port Pin. Da geht es warscheinlich nicht viel
einfacher wie schon beschrieben. Da bin ich jetzt mit meinen Latein im
Augenblick auch zu Ende.
Mir kommt vor, die schlechte Genaugigkeit kommt daher dass der TIMER mit
nur 1953.125 HZ (0.512ms) getaktet wird. Die Aufloesung der Zeitmessung
ist ja nur 0.512ms. Hast Du schon probiert die Taktrate so weit zu
erhoehen bis es nicht mehr geht?
Bei dieser Taktrate ist der Unterschied Aufloesungsmaessig nur 0.00452HZ
bei 1Hz Eingangsfrequenz. Dann stimmen Deine (ungenauen) Messergebnisse.
Gerhard
@ Karl (Gast)
>>16 Mhz Takt (was hier zumindest Overkill ist).>Sag mal warum das Overkill ist? Du weißt doch gar nicht was ich sonst>mit dem µC mache?
Eben deshalb schrieb ich dass es HIER, für diese Messung, Overkill ist.
Was der uC sonst macht kann einen 16 MHz Takt notwendig machen.
>16000, da er nur eine sekunde lang mißt, kommt also nicht zum overflow.>also kein trick...
Doch, aber das erklären wir mal später.
>>Tut er nicht, aber er macht es sehr kompliziert und falsch.>>Deine Beiträge sind sehr lustig. :) Sag mal,>Warum ist es kompiziert und falsch Zeit zwischen zwei Flanken zu messen>und daraus die Frequenz zu errechnen?
Das Prinzip ist richtig, deine Umsetzung nicht.
>4sec eine 1Hz Frequenz zu messen, dass finde ich Overkilled. Aber es ist>nicht falsch...
Du hast mich nicht verstanden. Die 4 Sekunden sind eine Überlaufzeit,
keine Messzeit.
MfG
Falk
>Mir kommt vor, die schlechte Genaugigkeit kommt daher dass der TIMER mit>nur 1953.125 HZ (0.512ms) getaktet wird. Die Aufloesung der Zeitmessung>ist ja nur 0.512ms. Hast Du schon probiert die Taktrate so weit zu>erhoehen bis es nicht mehr geht?
Du meinst ich sollte die Taktrate des Zählers erhöhen? Ja das könnte ich
tun. Aber dann wird der Zähler ständig überlaufen, wenn ich niedrige
Frequenzen messe...
Mal schuen ob die Werte besser werden
solange es nicht 2 overflows gibts, braucht man sich gar nicht drum
kümmern. Auch eine Korrekturrechnung ist nicht erforderlich, rechnet man
mit unsigned int.
ISR:
{
static unsigned int Messwert_bak;
unsigned int temp;
Messwert=temp-Messwert_bak;
Messwert_bak=temp;
}
Bsp:
alter Zählerstand 0x1000, Int bei 0x2000 -> Messwert 0x1000
alter Zählerstand 0xFF00, Int bei 0x0F00 (Overflow) -> Messwert 0x1000
Karl:
So stelle ich mir das vor:
t[us] - 32 Bit UNSIGEND INTEGER
T1,T2 - 16 Bit UNSIGNED INTEGER (Unbedingt beachten)
n[overflow] - 8 oder 16 bit UNSIGNED INTEGER
t[us] = (int32) (T2 - T1) + (int32) (n[overflow] * 65536) * (int32)
Taktrate(us)
Kuemmere Dich nicht um das Sign Bit der Rechnung T2-T1. Das geht aber
nur richtig mit 16-bit Speichervariablen. Zum Beispiel wenn T1 = 55535
und T2 = 1000, dann ist der Unterschied (T2-T1) = 11001
T1 T1
T[total] = 55535 - 65536 = 10001 + 1000 = 11001 (Lauft ueber, also nicht
negativ)
Gerhard
@Falk
Es wird interessant wie ein Krimi...
>Das Prinzip ist richtig, deine Umsetzung nicht.
Ok, gehehn wir meine und deine Umsetztung systematisch durch:
im main machst du die Timer und Interrruptinitialisierung
Dann aktivierst du interrupts und wartest du bis ne steigende Flanke
kommt
1
volatileuint16_tdelta_t;
2
uint16_told_time;
3
volatileuint8_tflag;
4
5
voidmain(void)
6
{
7
// timer initialisieren
8
TCCR1B=5;// Prescaler 1024
9
10
// interrupt initialisieren
11
TIMSK|=(1<<TOIE1);// Overflow Interrupt ON
12
13
sei();
14
15
while(1)
16
{
17
while(flag==0);// Warte auf Messung
18
flag=0;
19
20
// hier jetzt die Auswertung/Speicherung des Messwertes delta_t
21
}
22
}
In ISR lädst du timerergebnis, ziehst eine unreferenzierte old_time ab
und das ergebnis prüfst du auf overflow. OK.
Nun schiebst du timerergebnis in time_old. und delta_t ist
zeitmessung.OK verstehe. Aber ne Frage: wo wird der Timer gestartet
Und verlierst du nicht die allererste Flanke wenn old_time
unreferenziert ist, beim ersten durchlauf?
1
// steigende Flanke an INT2
2
ISR(INT2_vect)
3
{
4
uint16_ttmp,tmp1;
5
6
tmp=TCNT1;
7
tmp1=tmp-old_time;
8
if(tmp1>32768)tmp1-=32768;
9
old_time=tmp;
10
delta_t=tmp1;
11
flag=1;
12
}
Was ich mache ist:
Warte auf Falnke -> Starte Timer -> Ist zweite Falnke da? -> Stoppe
Timer
Im Timer ist nun meine Zeit zwischen zwei steigenden Falnken.
Was ist konkret schlecht an meiner Umsetztung?
@ Karl (Gast)
>zeitmessung.OK verstehe. Aber ne Frage: wo wird der Timer gestartet
Im Main und läuft dann immer ohne Pause.
>Und verlierst du nicht die allererste Flanke wenn old_time>unreferenziert ist, beim ersten durchlauf?
Ja. Aber bei kontinuierlichen Messungen kein Problem.
>Was ich mache ist:>Warte auf Falnke -> Starte Timer -> Ist zweite Falnke da? -> Stoppe>Timer>Im Timer ist nun meine Zeit zwischen zwei steigenden Falnken.>Was ist konkret schlecht an meiner Umsetztung?
Schau dir dein Program an. Viel zu kompliziert. Das SREG ist für dich
als C-Programierer zu 99,999% tabu. Ist auch vollkommen unnötig. Das
Sperren und wideranschalten der Interrupts ist unnötig, ausserdem muss
dazu kein cli() gemacht werden.
Und, der letzte und grösste Fehler. Du stoppst den Zähler nicht in der
ISR sondern in der Main-Schleife. Das bringt zusätzliche, unnötige,
variable Verzögerungen, die das Messergebnis verfälschen.
Mit meiner Methode hat man fast die selbe Auflösung und Genauigkeit wie
mit der Input Capture Funktion, weil die Verzögerung von der Flanke bis
zum Auslesen des Zählers konstant ist. Die einzige Unschärfe im
Zeitverhalten ist die variable Verzögerung des Interrupts. Wenn nämlich
zufällig ein Befehl mit mehr als 1 Takt Länge ausgeführt wird, muss der
Interrupt solange warten. Das bringt aber worst case drei Takte
Unsicherheit (bei einem Befehl mit 4 Takten). Naja, und ein anderer
Interrupt darf natürlich nicht aktiv sein, sonst geht das schief, klar.
Mfg
Falk
>Schau dir dein Program an. Viel zu kompliziert. Das SREG ist für dich>als C-Programierer zu 99,999% tabu. Ist auch vollkommen unnötig.
Habe ich schon längst verbessert, hast du es übersehen?:
1
voidmain()
2
{
3
...
4
timer_value=0;
5
cli();
6
Enable_INT2();
7
sei();
8
}
9
10
ISR(INT2_vect)
11
{
12
//start counter
13
if(TCCR1B==0x00)
14
{
15
Start_Counter1();
16
}
17
18
edge_counter++;
19
20
if(edge_counter==2)
21
{
22
Stop_Counter1();
23
Disable_INT2();
24
timer_value=TCNT1;
25
26
edge_counter=0;
27
TCNT1=0;
28
}
29
}
>Und, der letzte und grösste Fehler. Du stoppst den Zähler nicht in der>ISR sondern in der Main-Schleife. Das bringt zusätzliche, unnötige,>variable Verzögerungen, die das Messergebnis verfälschen.
Stimmt nicht. Ich mach das in ISR. Wenn 2 Flanken gezählt wurden, stoppe
ich den Zähler und deaktiviere INT2. Im main aktiviere ich nur den
INterrupt für steigende Flanke.
>Naja, und ein anderer>Interrupt darf natürlich nicht aktiv sein, sonst geht das schief, klar.
Ist aber in meinem Programm der Fall. Aber die Externen Interrupts haben
allerdings den Vorrang. Haben sie trotzdem einen Einfluss?
Deine Funktion habe ich getestet und beim Debuggen sehe ich, dass
delta_t fast immer 0 ist. Dabei sollte delta_t den TimerWert zwischen
zwei flanken darstellen.. Seltsam
Karl wrote:
> Deine Funktion habe ich getestet und beim Debuggen sehe ich, dass> delta_t fast immer 0 ist. Dabei sollte delta_t den TimerWert zwischen> zwei flanken darstellen.. Seltsam
<Zitat>
1
voidmain(void)
2
{
3
// timer initialisieren
4
TCCR1B=5;// Prescaler 1024
5
6
// interrupt initialisieren
7
TIMSK|=(1<<TOIE1);// Overflow Interrupt ON
8
9
sei();
10
11
while(1)
12
{
13
while(flag==0);// Warte auf Messung
14
flag=0;
15
16
// hier jetzt die Auswertung/Speicherung des Messwertes delta_t
17
}
18
}
</Zitat>
Hast du einen Overflow Handler für Timer 1?
Solange du nicht mehr als 65536 Timer Ticks zur Messung
benötigst (auch bei freilaufendem Timer) brauchst du die
Overflows nicht zu berücksichtigen. Durch die unsigned
Rechnung gleicht sich das von alleine aus.
Daher ist auch das
if (tmp1>32768) tmp1 -= 32768;
in so einem Fall nicht notwendig.
@ Karl (Gast)
>>Naja, und ein anderer>>Interrupt darf natürlich nicht aktiv sein, sonst geht das schief, klar.>Ist aber in meinem Programm der Fall. Aber die Externen Interrupts haben>allerdings den Vorrang. Haben sie trotzdem einen Einfluss?
Aber sicher! Stell dir vor, ein anderer Interrupt wird aktiv, egal
welcher. Der Prozessor fängt an ihn zu bearbeiten. Nur einen Takt später
kommt dein Externer Interrupt. Da der AVR keine verketteten Interrupts
von Hause aus kann, muss dein Externer Interrupt warten, bis der gerade
begonnene Interrupt zu Ende ist. Das verfälscht die Messung natürlich um
EINIGES! Abhilfe schafft hier in Grenzen ein Verketten von Interupts per
Software, sprich, der erste Befehl ist ein sei() in JEDER anderen
Interruptroutine, ausser im externen Interrupt. Damit sind alle anderen
Interrupts unterbrechenbar. Doch die Zeitverzögerung ist dann immer noch
gross, je nach Compileroverhead 4....50 Takte.
>zwei flanken darstellen.. Seltsam
In der Tat.
MfG
Falk
Ist es wirklich unmoeglich, die Hardware zu modifizieren, dass der ICP
capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.
Mir kommt vor, dass es wirklich verflixt schwierig scheint, das
Zeitintervallmessen richtig zum Funktionieren zu bringen. Bei der
Capture methode verliert man auch keine Genaugigkeit, da der TIMER
Zaehlerstand von der Hardware direkt uebernommen wird.
Vielleicht is es Zeit das Skalpell aus der Schublade zu holen und an der
Platine zu operieren. Zwei Schnitte + Draht und Loetkolben: Fertig - und
das Problem gehoert der Vergangenheit an;:-)
Gerhard
> Aber die Externen Interrupts haben allerdings den Vorrang.
Es gibt bei den AVRs keine Interrupt-Prioritäten im engeren Sinne und im
Prinzip sind Interrupt Handler nicht durch andere Interrupts
unterbrechbar (außer man gibt im Handler explizit die
Interrupt-Bearbeitung frei). Die "Vorrangstufen" nach der Vektortabelle
beziehen sich lediglich auf zeitgleich auftretende Interrupts (bzw. auf
den Fall, dass bei einer globalen Freigabe mehrere lokal freigegebene
Interrupts bereits anstehen, also deren Flags gesetzt sind).
OK. Ich hab jezt die Lösung von Falk implementiert. Bis jetzt liefert
sie bessere Ergebnisse.
Damit andere Interrupts das Ergebniss nicht verfälschen schalte ich INT2
und Counter jedesmal ein und aus.
Vielleicht noch paar Worte zu meiner Anwendung. Im Grunde benutze ich
AT90USB (identisch zu MEGA128 bis auf USB). Ich sende per USB ein
Commando z.B GET_FREQUENCY (indem ich auf ein Button drücke), dann
werden die INT2 und Zähler gestartet.
1
main()
2
{
3
...
4
elseif(received_command==COMM_GET_FREQUENCY)
5
{
6
last_command=COMM_GET_FREQUENCY;
7
8
cli();
9
10
TCCR1B=5;// Prescaler 1024
11
12
TIMSK1|=(1<<TOIE1);// Overflow Interrupt ON
13
14
Enable_INT2();
15
sei();
16
}
17
}
PC wartet 1,5s lang und fragt dann die Daten ab. Die Daten werden via
USB übertragen, danach werden die INT2 und Zähler ausgeschaltet.
1
ISR(ENDPOINT_INTERRUPT)
2
{
3
...
4
if(is_interrupt_EP_INT_IN())
5
{
6
Usb_select_endpoint(EP_INT_IN);
7
Usb_ack_in_interrupt();
8
9
Usb_write_byte(MSB(delta_t));
10
Usb_write_byte(LSB(delta_t));
11
12
for(i=0;i<EP_SIZE_1-2;i++)
13
{
14
Usb_write_byte(1);
15
}
16
17
Usb_send_in();
18
//INT2 OFF
19
Disable_INT2();
20
//CNTR1 OFF
21
TCCR1B=0;
22
TIMSK1=0;
23
}
24
}
USB Interrupt und der INT2 scheinen sich nicht zu behindern. Allerding
bekomme ich bei aller ersten Messung immer 0. Wenn ich frequenz erhöhe
oder verringere muss ich ebenfalls 2x auf den Button klicken bis der
richtige Wert angezeigt wird.
>Ist es wirklich unmoeglich, die Hardware zu modifizieren, dass der ICP>capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.
no way... siehe bild
@ Karl (Gast)
>OK. Ich hab jezt die Lösung von Falk implementiert. Bis jetzt liefert>sie bessere Ergebnisse.
Schön zu hören ;-)
>Damit andere Interrupts das Ergebniss nicht verfälschen schalte ich INT2>und Counter jedesmal ein und aus.
Das bringt nix. Mein Posting nochmal lesen.
>USB Interrupt und der INT2 scheinen sich nicht zu behindern. Allerding
Du machst in einem INTERRUPT so viele USB Operatioen? Ob das ne gute
Idee ist?
>Dateianhang: Leiterplatte.PNG (251,4 KB, 13 Downloads)Bildformate!!!
>>capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.>no way... siehe bild
Wo ist das Problem?
Mfg
Falk
>Bildformate!!!
schon gut. ich habs doch extra komprimiert und als PNG hochgeladen.. ??
>Du machst in einem INTERRUPT so viele USB Operatioen? Ob das ne gute>Idee ist
Das geht in ordnung, kommt vom Atmel. Das sind alles USB-Operationen, da
USB zeitkritisch ist, muss ich unbedingt auf jeden möglichen INterrupt
reagieren.
>>capture Eingang verwendet werden koennte? Das ware sooo viel einfacher.>Wo ist das Problem?
niergends. Ich sagte ich kann die hardware nicht ändern um ICP zu
nutzen. Das ist alles.
Danke für die tolle Hilfe, ich werde nun die Frequenzmessung ausgibig
testen und falls ich Fehler entdecke und nicht weiter komme melde ich
mich.