Möchte mit einem ATMEGA8 (f = 1MHz) die Zeitdauer eines High-Signals im
Bereich 10us - 10s messen.
Dazu benutze ich den Input-Capture des Timer1 ohne Prescaler. Zusätzlich
wird bei jedem Timer-Überlauf ein Interrupt ausgelöst und die
Überlauf-Variable um 1 (bis 255) inkrementiert.
Hier der Code:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
volatileuint8_tsofttimer;
5
volatileuint32_tzeitdifferenz;
6
volatileuint32_ttimestamp;
7
8
9
typedefunion{
10
unsignedlongi24;
11
struct{
12
uint16_ticr;
13
uint8_thigh;
14
};
15
}convert24to8;
16
17
intmain(void);
18
19
ISR(TIMER1_OVF_vect)
20
{
21
++softtimer;
22
}
23
24
voidwert_verarbeiten(uint32_tzeit)
25
{
26
.....
27
}
28
29
ISR(TIMER1_CAPT_vect)
30
{
31
convert24to8cap;
32
cap.icr=ICR1;
33
cap.high=softtimer;
34
zeitdifferenz=cap.i24-timestamp;
35
timestamp=cap.i24;
36
if(TCCR1B&(1<<ICES1))
37
{
38
TCCR1B&=~(1<<ICES1);
39
}
40
else
41
{
42
TCCR1B&=~(1<<CS10);
43
TCCR1B|=(1<<ICES1);
44
}
45
}
46
47
intmain()
48
{
49
uint32_tzeit;
50
DDRB&=~(1<<DDB0);//B0 als Eingang
51
TCCR1A=0;//Counter auf "normal mode" setzen
52
TCCR1B|=(1<<ICES1)|(1<<CS10);//steigende Flanke an ICP1 und Prescaler 1:1
53
TIMSK|=(1<<TOIE1)|(1<<TICIE1);//Overflow und Capture Interrupt
54
TIFR|=(1<<ICF1)|(1<<TOV1);//Input Capture Flag und Overflow Flag rücksetzen (d.h. auf "1")
55
zeitdifferenz=0;
56
softtimer=0;
57
sei();
58
while(1)
59
{
60
//*********************************
61
1while(TCCR1B&(1<<CS10));// warte bis Timer angehalten wurde
62
2cli();
63
3zeit=zeitdifferenz;
64
4zeitdifferenz=0;
65
5wert_verarbeiten(zeit);
66
6sei();
67
7TCCR1B|=(1<<CS10);
68
//*********************************
69
}
70
return0;
71
}
Hier jetzt meine Fragen:
1) die allererste gemessene Signaldauer fällt immer mehrere hundert
Mikrosekunden zu großer aus; aller weiteren Messung stimmen jedoch.
Woran könnte das liegen?
2) hier geht es um den markierten Code-Bereich (Zeile 1 - Zeile 7). Wenn
ich die Zeilen 6 und 7 vertausche, werden alle Werte falsch gemessen.
WARUM?
DANKE schonmal!
Hi
nun ja, C ist mir zwar immer noch fremd, aber mit 1 MHz eine Zeit von
10µs zu messen ist schon sehr sportlich. 1 Takt hat schon 1 µs, also
sind zur Bearbeitung gerade mal ca. 4-5 Befehle in der Zeit möglich.
CPU-Befehle, wohlgemerkt. Ein wenig mehr Luft solltest du dir da schon
gönnen und die Taktfrequenz heraufsetzen.
Gruß oldmax
oldmax schrieb:> Hi> nun ja, C ist mir zwar immer noch fremd, aber mit 1 MHz eine Zeit von> 10µs zu messen ist schon sehr sportlich. 1 Takt hat schon 1 µs, also> sind zur Bearbeitung gerade mal ca. 4-5 Befehle in der Zeit möglich.> CPU-Befehle, wohlgemerkt. Ein wenig mehr Luft solltest du dir da schon> gönnen und die Taktfrequenz heraufsetzen.> Gruß oldmax
nur wenn man es komplett in Software machen will, aber ein Atmel hat mit
dem Input-Capture die passende Hardware für das Problem. Damit spielt
die Laufzeit vom code keine wirkliche rolle mehr.
Peter II schrieb:> nur wenn man es komplett in Software machen will, aber ein Atmel hat mit> dem Input-Capture die passende Hardware für das Problem. Damit spielt> die Laufzeit vom code keine wirkliche rolle mehr.
wenn man allerdings laufende Messungen machen will, spielt es dann schon
wieder eine Rolle, ob ein Interrupt in der 'Warteschleife' hängt, weil
der davor noch nicht fertig abgearbeitet wurde. Ist diese Verzögerung
symptomatisch, dann summiert sich dieser Verzögerung, bis dann
irgendwann mal ein kompletter Interrupt ausfallen muss, weil gerade
einer bearbeitet wird und bereits einer in der 'Wartschlange' hängt.
Falsche Ergebnisse sind dann die Folge.
Ist wie im Büro: Wenn du zur Beabreitung eines Aktes 30 Minuten
brauchst, alle 20 Minuten aber einen neuen reinkriegst, dann wird
irgendwann mal dein Schreibtisch zusammenbrechen.
Jens H. schrieb:> 1) die allererste gemessene Signaldauer fällt immer mehrere hundert> Mikrosekunden zu großer aus; aller weiteren Messung stimmen jedoch.> Woran könnte das liegen?
weil du deine erste Messperiode nicht mit einer steigenden Flanke
synchronisierst. Die beginnt zu irgend einem Zeitpunkt. Zum beispiel
auch kurz nach einer steigenden Flanke.
Jens H. schrieb:> 2) hier geht es um den markierten Code-Bereich (Zeile 1 - Zeile 7). Wenn> ich die Zeilen 6 und 7 vertausche, werden alle Werte falsch gemessen.> WARUM?
Ähnliches Problem.
Niemand garantiert dir, dass während dieser Code läuft
1
2cli();
2
3zeit=zeitdifferenz;
3
4zeitdifferenz=0;
4
5wert_verarbeiten(zeit);
es nicht eine Flanke am Capture Pin gibt. Dadurch das der cli die
Interrupts ausgeschaltet hat, wird der nicht bearbeitet. Sobald aber der
sei kommt, wird der Interrupt nachgeholt.
Der Timer läuft aber währenddessen nicht. D.h. die Zeit dazwischen ist
nicht registriert worden.
-> Timer ständig starten und stoppen ist meistens keine gute Idee.
Peter D. schrieb:> Beitrag "AVR Timer mit 32 Bit"
Also den Link finde ich zwar sehr interessant, aber es bringt mich
leider mit meinem Problem nicht so recht weiter. Der input capture fehlt
da gänzlich und das mit dem overflow habe ich ja so ähnlich auch
implementiert. Oder sehe ich den Zusammenhang einfach nicht? Danke auf
alle Fälle!
oldmax schrieb:> Hi> nun ja, C ist mir zwar immer noch fremd, aber mit 1 MHz eine Zeit von> 10µs zu messen ist schon sehr sportlich. 1 Takt hat schon 1 µs, also> sind zur Bearbeitung gerade mal ca. 4-5 Befehle in der Zeit möglich.> CPU-Befehle, wohlgemerkt. Ein wenig mehr Luft solltest du dir da schon> gönnen und die Taktfrequenz heraufsetzen.> Gruß oldmax
Ja, da hast du vermutlich schon recht oldmax. Aber mir geht es gerade
erstmal darum, das ganze überhaupt zum Laufen zu kriegen; die
Verbesserung der Genauigkeit kommt danach, z.b. durch Erhöhung der
Taktfrequenz.
Jens H. schrieb:> 1) die allererste gemessene Signaldauer fällt immer mehrere hundert> Mikrosekunden zu großer aus; aller weiteren Messung stimmen jedoch.
Auch wenn du die Sache im Simulator verfolgst?
> Woran könnte das liegen?
Das wird sich dann zeigen.
Karl H. schrieb:> Jens H. schrieb:>>> 1) die allererste gemessene Signaldauer fällt immer mehrere hundert>> Mikrosekunden zu großer aus; aller weiteren Messung stimmen jedoch.>> Woran könnte das liegen?>> weil du deine erste Messperiode nicht mit einer steigenden Flanke> synchronisierst. Die beginnt zu irgend einem Zeitpunkt. Zum beispiel> auch kurz nach einer steigenden Flanke.
Hm, das mit der Synchronisation verstehe ich jetzt leider nicht. Sorry,
ich bin blutiger Anfänger, daher bitte langsam und deutlich sprechen ;-)
Also: Das Programm startet generell mit einem LOW am Eingang; und dann
kommt irgendwann ein HIGH-Signal, das dann zwischen 10 us und 10s lang
anliegt. Während der Verarbeitung des Messergebnisses (Zeitdauer) SOLL
kein weiteres Signal verarbeitet werden, daher cli() in Zeile 2.
Wo fehlt jetzt die Synchronisation?
Tom E. schrieb:> Jens H. schrieb:>> 1) die allererste gemessene Signaldauer fällt immer mehrere hundert>> Mikrosekunden zu großer aus; aller weiteren Messung stimmen jedoch.>> Auch wenn du die Sache im Simulator verfolgst?>>> Woran könnte das liegen?>> Das wird sich dann zeigen.
Simulator habe ich nicht :-(
> es nicht eine Flanke am Capture Pin gibt. Dadurch das der cli die> Interrupts ausgeschaltet hat, wird der nicht bearbeitet. Sobald aber der> sei kommt, wird der Interrupt nachgeholt.> Der Timer läuft aber währenddessen nicht. D.h. die Zeit dazwischen ist> nicht registriert worden.
Verstehe ich folgendes richtig: Auch wenn die Interrupt-Behandlung mit
cli() deaktiviert wurde, wird ein eintreffender "Interrupt" (der ja dann
nicht wirklich einer ist) nach dem nächsten sei() verarbeitet!? Das
könnte des Rätsels Lösung sein.
Jens H. schrieb:> Also: Das Programm startet generell mit einem LOW am Eingang; und dann> kommt irgendwann ein HIGH-Signal, das dann zwischen 10 us und 10s lang> anliegt. Während der Verarbeitung des Messergebnisses (Zeitdauer) SOLL> kein weiteres Signal verarbeitet werden, daher cli() in Zeile 2.
Das mag schon sein.
Nur interessiert es die Interrupt Einheit nicht, dass Interrupts
disabled sind. Kommt eine Flanke daher, während dieser Teil
1
//*********************************
2
1while(TCCR1B&(1<<CS10));// warte bis Timer angehalten wurde
3
2cli();
4
3zeit=zeitdifferenz;
5
4zeitdifferenz=0;
6
5wert_verarbeiten(zeit);
7
6sei();
8
7TCCR1B|=(1<<CS10);
abgearbeitet wird, dann wird die Flanke registriert und sofort nach dem
sei läuft die ISR los.
-> du musst hier ebenfalls die Interrupt Flags vor dem sei löschen.
> Wo fehlt jetzt die Synchronisation?
Da hast du recht. Die erste Messung müsste funktionieren, aber die
weiteren nicht unbedingt aus dem vorher genannten Grund (Interrupt Flags
löschen).
Karl H. schrieb:> Jens H. schrieb:>> Das mag schon sein.> Nur interessiert es die Interrupt Einheit nicht, dass Interrupts> disabled sind. Kommt eine Flanke daher, während dieser Teil>
1
>//*********************************
2
>1while(TCCR1B&(1<<CS10));// warte bis Timer angehalten
3
>wurde
4
>2cli();
5
>3zeit=zeitdifferenz;
6
>4zeitdifferenz=0;
7
>5wert_verarbeiten(zeit);
8
>6sei();
9
>7TCCR1B|=(1<<CS10);
10
>
> abgearbeitet wird, dann wird die Flanke registriert und sofort nach dem> sei läuft die ISR los.>> -> du musst hier ebenfalls die Interrupt Flags vor dem sei löschen.>>> Wo fehlt jetzt die Synchronisation?>> Da hast du recht. Die erste Messung müsste funktionieren, aber die> weiteren nicht unbedingt aus dem vorher genannten Grund (Interrupt Flags> löschen).
Juhu, ich habe folgendes vor sei() eingefügt:
TIFR |= (1 << ICF1) | (1 << TOV1);
Problem 1 ist damit behoben! DANKE!!!
Das mit der "Synchronisation" (bzw. Fehlmessung beim ersten Messwert)
muss ich mir nochmal anschauen. Komisch, komisch, komisch....
Jens H. schrieb:> Möchte mit einem ATMEGA8 (f = 1MHz) die Zeitdauer eines High-Signals im> Bereich 10us - 10s messen.
Das kannst du (in den meisten Fällen) einfach vergessen.
> Dazu benutze ich den Input-Capture des Timer1 ohne Prescaler.
Das ist, was der Sache am nächsten kommt, aber es reicht schlicht nicht.
Beim Mega8 kann der ICP-Interrupt nur eine Flanke erfassen. Zur
Zeitmessung eines Pulses muß also der Flankensensor "gekippt" werden.
Außerdem muß natürlich der ICP-Wert der steigenden Flanke ausgelesen und
zwischengespeichert werden. Selbst die optimale ISR (natürlich nur in
Assembler möglich) muß hier scheitern, selbst wenn der ICP-Interrupt der
einzige im System wäre.
Warum? Deshalb:
1) Statische Interrupt-Latenz: vier Takte. Absolutes Minimum (bei
inline-ISR ab Vektor).
2) Statischer Interruptframe: acht Takte. Absolutes Minimum (bei leerer,
also völlig nutz- und funktionsloser ISR).
3) Variable Interruptlatenz: drei Takte Minimum bei Verzicht auf
konkurrierende Interrupts und jegliche cli/sei-Konstrukte.
Und selbst bei Verzicht auf Interrupts (also gnadenlosem Polling)
sieht's nicht viel besser aus. Die kürzestmögliche
Polling-Doppelschleife braucht auch sechs Takte zur Erkennung beider
Flanken. Das würde zwar immerhin auch knapp zu einer sicheren Detektion
des 10µs-Ereignisses genügen, aber auch nicht zu einer "Messung" der
Dauer. Jedenfalls nicht so, wie man üblicherweise eine "Messung"
versteht, also mit Fehlern mit schlimmstenfalls im einstelligen
Prozentbereich...
Das Takt-Zeit-Problem könnte ja durch Mega88/20Mhz Quarz um den Faktor
20 verkleinert werden. Aber C-Hater hat mit seinen Ausführungen (grumpf)
recht: mit der Stoppuhr ist "messen" was anderes. Zumindest, wenn man
auf 10μs-Pulse besteht. Denn es ist kein Problem der Genauigkeit, das
bekommt man Dank HW-Unterstützung hin. Nur nicht in dem extremen
Messbereich.
Tom E. schrieb:> Jens H. schrieb:>> Simulator habe ich nicht :-(>> Womit entwickelst du denn dein Programm? Hast du kein Atmel Studio?
Ne, habe einen stinknormalen Texteditor, einen Compiler und avrdude und
das ganze auf Linux.
c-hater schrieb:> 1) Statische Interrupt-Latenz: vier Takte. Absolutes Minimum (bei> inline-ISR ab Vektor).> 2) Statischer Interruptframe: acht Takte. Absolutes Minimum (bei leerer,> also völlig nutz- und funktionsloser ISR).> 3) Variable Interruptlatenz: drei Takte Minimum bei Verzicht auf> konkurrierende Interrupts und jegliche cli/sei-Konstrukte.>> Und selbst bei Verzicht auf Interrupts (also gnadenlosem Polling)> sieht's nicht viel besser aus. Die kürzestmögliche> Polling-Doppelschleife braucht auch sechs Takte zur Erkennung beider> Flanken. Das würde zwar immerhin auch knapp zu einer sicheren Detektion> des 10µs-Ereignisses genügen, aber auch nicht zu einer "Messung" der> Dauer. Jedenfalls nicht so, wie man üblicherweise eine "Messung"> versteht, also mit Fehlern mit schlimmstenfalls im einstelligen> Prozentbereich...
Das hört sich spannend an! Dazu aber folgende Frage: Verschiebt sich
durch die Verarbeitungszeit eines Interrupts sowohl der Beginn als auch
das Ende der Messung um mindestens 3-4 Takte nach hinten? Dann wäre ja
die Messung der Zeitdauer zwar im Vergleich zum Signal etwas verschoben,
aber trotzdem korrekt!? Oder führt das dazu, dass die Messung mindestens
6-8 Takte zu groß ausfällt?
Und - ähem- vielleicht magst du ja die Punkte 1 - 3 etwas genauer
erklären? Mir ist der Unterschied nämlich nicht so ganz klar. Was heisst
z.B. "Absolutes Minimum (bei inline-ISR ab Vektor)".
Wie gesagt, bin blutiger Anfänger im Bereich µc's....
Jens H. schrieb:> Verschiebt sich> durch die Verarbeitungszeit eines Interrupts sowohl der Beginn als auch> das Ende der Messung um mindestens 3-4 Takte nach hinten? Dann wäre ja> die Messung der Zeitdauer zwar im Vergleich zum Signal etwas verschoben,> aber trotzdem korrekt!? Oder führt das dazu, dass die Messung mindestens> 6-8 Takte zu groß ausfällt?
Die statische Interruptlatenz ist immer da, also ein konstanter Offset
zwischen Realität und Messung, wäre also mittels Versuch herauszufinden.
Dein Problem ist eher die variable Latenz, denn es kann sein, das die zu
erkennende Flanke den MC 'auf dem falschen Fuß' erwischt, z.B. mitten in
einem Befehl, der 2 Takte dauert.
Dieser Befehl wird immer zuerst abgearbeitet, bevor der IRQ erkannt und
abgearbeitet wird. Einige sehr wenige Befehle brauchen sogar 3 Zyklen,
wie LPM oder (variable Zyklenzahl) SPM, die kann man allerdings meistens
vermeiden.
Es entsteht also ein 'Jitter', der abhängig davon ist, an was für einem
Befehl der MC gerade knabbert, wenn die Flanke auftritt.
Hallo Jens,
fräs willst du wirklich Messen?
Pulsdauern inn angegebenen Bereich?
Oder doch Frequenzen?
Bei letzteren gibt es 2 Möglichkeiten:
Pulse Zählen, das macht man wenn viele davon innerhalb einer
Anzeige-Refresh-Zeit vorkommen, bei dir das 10μs (100kHz) Ende. Wenn die
100kH mit 2 Nachkommastellen "gezählt" werden sollen (gerade Neulinge
sind sich sicher, daß sie diese 8 Stellen brauchen), dann gibts nur alle
1 2/3 Minuten eine neue Anzeige.
Zeit zwischen Flanken ermitteln, das get bei 10s (0.1Hz) besser. Nach
max einer Periode hat man den Wert, nur braucht man bei hohen Frequenzen
eine fein auflösende Stoppuhr. Ein AVR mit 1MHz Takte hat bei deinen
10μs Pulse aber 10% Ungenauigkeit.
Man wird also beides machen und zwar jeweils das für den Bereich, für
den es besser paßt.
Zur Frage "Messfehler durch variable Interruptlatenz: InputCompare hat
dieses Problem nicht. Nur muß man, will man steigende und fallende
Flanken messen, zwischendurch (in der IC ISR) die Richtung umschalten.
Wenn man dazu z.B. bis zu 20μs braucht (weil irgendwo Int's gesperrt
werden müssen), dann kann man zuverlässig keine 10μs erfassen. Der Wert
selbst ist aber im Rahmen der HW-Genauigkeit richtig und nicht
cli()-abhängig.
Hi
Jens H. schrieb:> Möchte mit einem ATMEGA8 (f = 1MHz) die Zeitdauer eines High-Signals im> Bereich 10us - 10s messen.
Damit erübrigt sich die Frage nach Frequenzmessung. Um einen Puls zu
erfassen, werden beide Flanken des Signales gebraucht. Die Hardware
hilft etwas, dennoch sind 10 µs mit 1 MHz Takt aus bereits erwähnten
Gründen nicht zu schaffen. Warum nimmst du nicht erst mal den Teiler
raus. Dann hast du 8 MHz, auch ohne externen Takt. Das gibt etwas Luft
und bringt dich dem Ziel etwas näher.
Gruß oldmax
oldmax schrieb:>> Damit erübrigt sich die Frage nach Frequenzmessung. Um einen Puls zu> erfassen, werden beide Flanken des Signales gebraucht. Die Hardware> hilft etwas, dennoch sind 10 µs mit 1 MHz Takt aus bereits erwähnten> Gründen nicht zu schaffen. Warum nimmst du nicht erst mal den Teiler> raus. Dann hast du 8 MHz, auch ohne externen Takt. Das gibt etwas Luft> und bringt dich dem Ziel etwas näher.> Gruß oldmax
Ja genau oldmax, ich möchte die Dauer eines HIGH-Signals bestimmen!
Der Prescaler steht übrigens auf 1:1, d.h. der Timer zählt mit einer
Frequenz von 1 MHz. Ich könnte dem ATMEGA natürlich einen 8MHz-Quarz
spendieren und dann wäre ich natürlich auf der sicheren Seite.
Danke, pescador.
Hi
Na, dann will ich nochmal....
Schau mal auf Seite 30 vom Datenblatt. Für 8 MHz brauchst du noch keinen
externen Quarz, aber es wird trotzdem eine knappe Sache. Allein die
Bandbreite von 10µs bis 10s dürfte spannend werden. Wenn du da nicht im
Vorfeld die Signaldauer zuordnen kannst und im ganzen Bereich mit deiner
Messwerterfassung bleiben willst, wird es auf jeden Fall knapp. Dein
Zähler kann so ca. 65ms erfassen, danach läuft er über. Bei 1 MHz, bei
einer höheren Frequenz ist die Zeit entsprechend kleiner.
Einfach mal 1/f rechnen und somit die Taktzeit erhalten. Dann erkennst
du schon die Probleme. Und ja, wenn du mit den erfassten Werten auch was
anstellen willst, braucht es auch sicherlich ein paar wenige Befehle in
der ISR. Da kommst du nicht drumrum.
Gruß oldmax
oldmax schrieb:>...> Schau mal auf Seite 30 vom Datenblatt. Für 8 MHz brauchst du noch keinen> externen Quarz....
Verdammt oldmax, da hast du wohl recht - Danke für den Hinweis!!!
>...> Bandbreite von 10µs bis 10s dürfte spannend werden. Wenn du da nicht im> Vorfeld die Signaldauer zuordnen kannst und im ganzen Bereich mit deiner> Messwerterfassung bleiben willst, wird es auf jeden Fall knapp. Dein> Zähler kann so ca. 65ms erfassen, danach läuft er über.
Klar, er läuft über, aber den Überlauf speicher ich in sofftimer, einer
uint8_t Variablen; d.h. ich komme damit bei f=1MHz doch auf 256 x 65ms =
ca. 16 s!?
Im übrigen habe ich gerade noch festgestellt, dass eine Signaldauer von
<= 50 μs gar nicht gemessen wird. WARUM das denn??? 100 μs werden jedoch
ziemlich genau berechnet. Muss mal noch rausfinden, wo die Grenze genau
liegt, habe leider keinen Funktionsgenerator.