Guten Abend,
ich will mittels Input Capture, eine eingehende Frequenz abtasten, und
messen wie lange sie auf 'HIGH' liegt.
Die Frequenz erzeugte ich mit einem Funktionsgenerator und überprüfe sie
mit einem Oszilloskop.
Theoretich sollte ich als Ergebnis '80' haben (entspricht nach
Umrechnunng 10ms)
allerdings bekomme ich solche Werte:
122
99
111
188
168
157
243
184
141
191
160
209
170
193
216
170
131
214
223
211
188
203
227
176
217
225
___
Ich denke das ich da etwas gewaltig falch mache in meinem Programm Code,
denn vom programmieren hab ich noch nicht alzuviel Ahnung.
btw: ich benutze einen ATmega168, programmiere mit einer AVRISP mkII und
nem int. RC Osc. mit 8MHz
TIFR0|=(1<<ICF1);// hier her und dafür aus der ISR raus
27
// ***********************************
28
29
sei();
30
}
31
}
das sorgt dafür, dass nach dem sei nicht sofort noch irgendwelche
pending Interrupt Requests feuern. Aber im Grunde ist der ganze Aufbau
mit Berechnung, Ausgabe und delay in einem cli()/sei() Block schon Käse.
Das ist TIFR1, (mein Fehler habs auch in der ISR korrigiert)
> das sorgt dafür, dass nach dem sei nicht sofort noch irgendwelche> pending Interrupt Requests feuern. Aber im Grunde ist der ganze Aufbau> mit Berechnung, Ausgabe und delay in einem cli()/sei() Block schon Käse.
Käse? Ich hab nicht viel Ahnung, aber als ich das ganze geschrieben
hatte, dachte ich mir, cli(); sperrt die Interrupts, also zählt auch der
Zähler nicht weiter. Dann in ruhe die Werte auswerten, starten und
warten bis wieder eine Messung erfolgt ist.
Und das Delay muss nicht sein, ich kann es jederzeit rausnehmen, es
ändert jedoch nichts an den kommenden Werten.
Die Werte sehen wie folgt aus, nachdem ich deinen Vorschlag intigriert
habe:
113
184
87
107
116
94
28
166
96
102
82
122
89
78
92
149
Bis jetzt wird das Ergebnis auf 8 Bit abgeschnitten, als unsigned Char.
Da kann man sich das Zählen der Überläufe erstmal sparen. So einfach wie
hier gedacht geht das mit den overflows ohnehin nicht. Bis 2^16
Zählschritten geht es auch erstmal ohne die extra overflows.
Mit dem cli() sperrt man nur den Aufruf der ISR - das Flag wird weiter
gesetzt.
Der Befehl
TIFR0 |= ( 1 << ICF1 );
ist hinten und vorne Murks:
wie schon geschreiben ist es das Register TIFR1. Beim AVR braucht man
die meisten Interrupt flags auch nicht von Hand in der ISR löschen, das
macht die Hardware schon beim Aufruf der ISR. Zum Löschen muss man nur
eine 1 in das zu löschende Flag schreiben - der gezeigte Befehl schreibt
überall eine 1 rein, wo schon eine 1 stand, und löscht damit alle
anstehenden Interrupts. Das ist also eine sehr missverständliche
Schreibweise.
Wenn überhaupt dann ein
TIFR1 = ( 1 << ICF1 );
im Hauptprogramm, da wo Karl Heinz Buchegger es vorgeschlagen hat. Wobei
es da nicht so schlimm ist alle Interrupt flags zu löschen.
Ulrich schrieb:>[...]
Das ist eine gute Erklärung, so habe ich mir das inzwischen auch
gedacht.
Wobei: Ich übernehme ja den Start, und den Endwert zur rechten Zeit
(Quasi beim betreten der Capt. ISR). Jetz habe ich die IRS etwas
abgeändert:
1
ISR(TIMER1_CAPT_vect)
2
{
3
4
if(ErsteFlanke)
5
{
6
StartTime=ICR1;
7
ErsteFlanke=FALSE;
8
TCCR1B&=~(1<<ICES1);
9
}
10
else
11
{
12
EndTime=ICR1;
13
ErsteFlanke=TRUE;
14
fertig=TRUE;
15
TCCR1B|=(1<<ICES1);
16
cli();//HIER
17
}
18
19
TIFR1|=(1<<ICF1);
20
}
Wie man hier sieht speere ich den Interrupts hiermit nach jeder
vollständigen Messung, d.h. ich muss sie im Hauptprogramm erst wieder
starten, bevor weiter Interrupts erfolgen können, und meine
abgespeicherten Werte, StartTime und EndTime, ändern sich nicht mehr,
da nun 100%tig nicht mehr in die entsprechende ISR gesprungen werden
kann, da ich mit Differenzen rechne ist es theoretich egal wenn der
Zähler weiterzählt, da wir aber nichts risskieren wollen:
1
TCNT1H=0x00;//Zähl register für Timer1
2
TCNT1L=0x00;
Dies setze ich im Hauptprogramm vor das sei();
> Wenn überhaupt dann ein> TIFR1 = ( 1 << ICF1 );> im Hauptprogramm, da wo Karl Heinz Buchegger es vorgeschlagen hat. Wobei> es da nicht so schlimm ist alle Interrupt flags zu löschen.
Das hatte ich dann nach dem Post von Karl Heinz Buchegger (kbuchegg),
schon gemacht, nachdem ich alles was ich gerade erklärt habe angewandt
habe
Werte:
7
241
185
223
252
227
4
16
20
237
4
252
218
2
74
46
241
248
10
21
203
57
Für mich leider immernoch keine ordentlichen Wert :/
Ulrich schrieb:> Mit dem cli() sperrt man nur den Aufruf der ISR - das Flag wird weiter> gesetzt.
Genau das ist meiner Ansicht nach das Hauptproblem.
Johannes
Das ist deswegen Murks, weil du die Interrupts sperren kannst solange du
willst, die Interrupt auslösenden Ereignisse treten ja weiter auf und
werden vom µC auch registriert, der dann eben später (nach Freigabe der
Interrupts) darauf reagiert.
Eine Analogie:
Was du machst ist: Du willst die Laufzeit von Skiläufern stoppen,
schaust aber die halbe Zeit nicht hin, ob gerade ein Läufer startet
(INterrupts gesperrt). Wenn du dann hinschaust (Interrupts freigibst)
kommst du dann drauf, dass ein Läufer gestartet ist (das entsprechende
Interrupt Flag ist bereits gesetzt) und du drückst noch schnell auf den
Startknopf deiner Stoppuhr (die ISR wird aufgerufen).
Dass diese Zeiten nix aussagen, dürfte klar sein.
Daher: entweder du lässt diesen Läufer fahren und stoppst dann erst
wieder beim nächsten, dessen Start du sicher zweifelsfrei gesehen hast
(Das Interrupt Flag löschen) oder aber wegschauen ist mehr oder weniger
verboten (Interrupts dürfen nicht so lange gesperrt bleiben) oder eine
Kombination aus beidem.
>> Der Befehl> TIFR0 |= ( 1 << ICF1 );>> ist hinten und vorne Murks:
Ich hab ehrlich gesagt nicht kontrolliert, welches Flag er da wie löscht
und habs einfach aus der ISR von ihm übernommen. Aber scheinbar muss man
wirklich jede Kleinigkeit mit dem Datenblatt in der Hand überprüfen.
Danke für den Catch.
So, im Anhang nocheinmal die Datei (die etwas 'ausgebesserte')
Ich habe zum einen eure Vorschläge Berücksichtigt, keine cli/sei mehr,
kein Delay mehr.
Zu der sache mit TIFR0, entschuldige ich mich nochmal, aber da ich
selber mit dem Datenblatt nochmal drüber gegangen bin, ist es mir ja
aufgefallen (siehe Post weiter oben.)
Die Sache mit Zählregister zurücksetzen haeb ich nun auch rausgenommen,
da ich ja eh keine Interruptspeere mehr habe.
Nun da ich keine Interrupts mehr Sperre,
Edit:
Neue Werte:
249
13
213
187
154
195
168
170
72
149
117
179
194
169
182
187
184
186
170
188
163
203
165
167
197
200
189
196
228
180
__
Sehen nun schoneinmal etwas konstanter aus aber immernoch nicht richtig.
Edit 2: und etwas später:
152
147
86
112
154
162
176
183
134
140
104
127
116
69
119
Johannes L. schrieb:> So, im Anhang nocheinmal die Datei (die etwas 'ausgebesserte')> Ich habe zum einen eure Vorschläge Berücksichtigt, keine cli/sei mehr,
Überhaupt keine ist ein Problem.
Du musst die Interrupts hier
unsigned char DIFFERENZ;
DIFFERENZ=dif(StartTime, EndTime);//EndTime - StartTime
int len ;
DIFFERENZ = (overflow*65536) + DIFFERENZ;
sperren, damit du sicher gehen kannst dass EndTime und StartTime aus
derselben Messung stammen und nicht zwischendurch ein ISR AUfruf dir die
Werte unter dem Hintern verändert (Später gleich mehr, warum man es
trotzdem nicht braucht)
Ausserdem solltest du dein flag fertig auch innerhalb der ISR
weiterbenutzen. Mittels fertig meldet die ISR an die Hauptschleife, dass
sie Werte hat. Und umgekehrt kann auch die Hauptschleife auf diesem Weg
wieder an die ISR signalisieren: OK, ich hab mir die Werte geholt, du
kannst wieder neu messen
if( fertig ) {
Werte holen
Umrechnen
Ausgeben
Alles klar soweit, fertig auf 0 setzen
}
ISR( .... )
{
if( fertig ) // die Hauptschleife hat noch kein OK für eine
return; // neue Messung gegeben
if( steigende Flanke )
...
else if( fallende Flanke ) {
...
fertig = 1; // eine neue Messung ist fertig
}
}
In dem Fall darfst du dann sogar ohne Interrupt Sperre auf die Messwerte
zugreifen, weil sichergestellt ist, dass sie von der ISR nicht verändert
werden. Warum? Weil die Hauptschleife noch keine Freigabe dafür gegeben
hat.
Und zum Feststellen welche Flanke: Die bessere Lösung ist es, einfach am
Pin nachzusehen, ob der 1 oder 0 ist, anstatt sich auf deine Zählung zu
verlassen. Dir braucht nur 1 Flanke als nicht erkannt flöten zu gehen
und du ordnest die Dinge falsch zu. PLötzlich misst du nicht mehr den
Puls, sondern die Pause zwischen den Pulsen.
Schön langsam nähern wir uns dem Verständnis des Problems und seiner
Lösung :-)
ZU deinem Programm:
Du darfst nicht alles umdrehen. Die Helfer hier versuchen aus deinem
vorhandenen Programm etwas zu machen. Die Hilfestellung ist daran
angelehnt wie dein Programm aussieht. Wenn du es umdrehst, dann greift
die Hilfestellung nicht mehr.
overflow=0;
fertig=0;
//TIFR1 |= ( 1 << ICF1 );//Muss nicht mehr, weill kein cli/sei mehr
}
No. Das wäre immer noch notwendig. Dafür ist es in der ISR sinnlos. Denn
da löscht sowieso die Hardware das Flag.
Ach und noch was.
Ulrich hat es ja schon angesprochen.
Rechne dir aus, ob bei deinen Zeiten eine Timer-Differenz größer als
65535 rauskommt. Wenn ja, stell den Vorteiler höher.
Und dann wirf die Overflow-Behandlung erstmal raus. Die ist nämlich
falsch.
Karl Heinz Buchegger schrieb:> unsigned char DIFFERENZ;> DIFFERENZ=dif(StartTime, EndTime);//EndTime - StartTime> int len ;> DIFFERENZ = (overflow*65536) + DIFFERENZ;>> sperren, damit du sicher gehen kannst dass EndTime und StartTime aus> derselben Messung stammen und nicht zwischendurch ein ISR AUfruf dir die> Werte unter dem Hintern verändert.
Deswegen wollte ich anfangs ja die Interrupts beim verlassen der Inpt.
ISR sperren, denn auch wenn ich die Interrupts direckt vor diesem
Abschnitt speere, kann es ja sein das ich direckt nach dem übernehmen
der Starttime (eines anderen Wertes) in die Auswertung kann.
> if( fertig ) {>> Werte holen>> Umrechnen>> Ausgeben>> Alles klar soweit, fertig auf 0 setzen> }>>>> ISR( .... )> {> if( fertig ) // die Hauptschleife hat noch kein OK für eine> return; // neue Messung gegeben>> if( steigende Flanke )>> ...>> else if( fallende Flanke ) {> ...>> fertig = 1; // eine neue Messung ist fertig> }> }>>> In dem Fall darfst du dann sogar ohne Interrupt Sperre auf die Messwerte> zugreifen, weil sichergestellt ist, dass sie von der ISR nicht verändert> werden. Warum? Weil die Hauptschleife noch keine Freigabe dafür gegeben> hat.>> Schön langsam nähern wir uns dem Verständnis des Problems und seiner> Lösung :-)
Ah, das hört sich interessant an,
habe nund das
1
if(fertig)
2
return;
in die Capt. ISR eingefügt, und mir die Werte erneut angeschaut die nun
rauskammen.
126
102
104
120
58
61
107
98
143
135
112
41
39
45
35
63
46
35
75
68
20
116
____________
Das TIFR1 |= (1<<ICF1); //Sitzt noch /wieder im Hauptrogramm
>Und zum Feststellen welche Flanke: Die bessere Lösung ist es, einfach am>Pin nachzusehen, ob der 1 oder 0 ist, anstatt sich auf deine Zählung zu>verlassen. Dir braucht nur 1 Flanke als nicht erkannt flöten zu gehen>und du ordnest die Dinge falsch zu. PLötzlich misst du nicht mehr den>Puls, sondern die Pause zwischen den Pulsen.
1
if(PINC&(1<<PINC2))//ist PINB PINB0 = 1?//wenn ich mich jetz nicht vertu,
2
{
3
//????
4
}
5
elseif(!(PINC&(1<<PINC2)))
6
{
7
//???
8
}
Das verstehe ich nicht genau, ich soll in der ISR(?) überprüfen ob der
entsprechende PIN, schon auf Low/High ist.
da verstehe ich den entsprechenden Sinn nicht, denn ich springe ja nur
in die ISR, wenn der PIN den entsprechenden WERT hat, das erste mal
springe ich in die Capt. ISR, sobald der Pin High ist, speicher den Wert
und gehe da wieder raus, das zweite mal springe ich in die Capt. ISR
sobald der Pin auf LOW liegt. Das es das zweite mal ist, springe ich
dann else hinein, speicher den Endwert, und ab da kann dan nichts mehr
in Start/endwert geschrieben werden, denn durch das if(fertig){return;}.
Kommt er erst wieder in der ISR weiter, sobald ich 'fertig' wieder auf 0
setze.
Wieso soll ich nun irgendwo überprüfen ob der Pin auf High/low ist?
*leicht verwirrt.*
Johannes L. schrieb:> Das verstehe ich nicht genau, ich soll in der ISR(?) überprüfen ob der> entsprechende PIN, schon auf Low/High ist.
Da hab ich mich vertan.
Sorry für die Konfusion. Mein Fehler. Deine Variable ersteFlanke steht
ja sowieso immer richtig, weil sie von der ISR selber gestellt wird.
Ich hab das falsch gelesen.
Aus mehr oder minder Testzwecken habe ich die Frequenz auf 500mHz
gestellt, was einer Highzeit von 1.00S entspricht, meine Werte sehen
immernoch nur nach Müll aus
152
81
204
150
110
173
4
und ich meine, bei einer Sekunde Zeit, zwischen Messung 1 und Messung
zwei, sollte er sich ja wohl nicht zwischen den verschiedenen Perioden
vertuen.
>Deine Variable ersteFlanke steht>ja sowieso immer richtig, weil sie von der ISR selber gestellt wird.>Ich hab das falsch gelesen.
Uff, war ganz schön verwirrt xD
unsigned char DIFFERENZ;
DIFFERENZ=dif(StartTime, EndTime);//EndTime - StartTime
int len ;
DIFFERENZ = (overflow*65536) + DIFFERENZ;
unsigned char für die DIFFERENZ. Und dann noch im Fall des Falles 65536
für einen Overflow dazu?
int DIFFERENZ = EndTime - StartTime;
und die Overflows lass erst mal weg. So einfach wie das da gemacht ist,
geht das nicht.
Das du ständig unsinnige Werte bekommst, liegt auch daran, dass du die
Differenz auf 8 Bit begrenzt, das Ergebnis da aber nie rein passt. Denn
schon deine Annahme über die Ausgangslage ist falsch:
Johannes L. schrieb:> Theoretich sollte ich als Ergebnis '80' haben (entspricht nach> Umrechnunng 10ms)
Nein, 80 wären 10 µs (Micro, nicht Milli).
Johannes L. schrieb:> Aus mehr oder minder Testzwecken habe ich die Frequenz auf 500mHz> gestellt, was einer Highzeit von 1.00S entspricht, meine Werte sehen> immernoch nur nach Müll aus
Und das ist nun vollends Unsinn.
Karl Heinz Buchegger schrieb:> unsigned char DIFFERENZ;>> DIFFERENZ=dif(StartTime, EndTime);//EndTime - StartTime> int len ;> DIFFERENZ = (overflow*65536) + DIFFERENZ;>>> unsigned char für die DIFFERENZ. Und dann noch im Fall des Falles 65536> für einen Overflow dazu?>> int DIFFERENZ = EndTime - StartTime;>> und die Overflows lass erst mal weg. So einfach wie das da gemacht ist,> geht das nicht.
1
intDIFFERENZ;
2
DIFFERENZ=dif(StartTime,EndTime);
3
DIFFERENZ=DIFFERENZ*125000/1033;//Beginn der Umrechnung.
Es Funktioniert, ich bin mir noch nicht ganz genau sicher, warum es
gerade deswegen so große ungenauigkeiten gab O.o aber die neuen
Messwerte:
(Hab das Programm ein wenig abgeändert, siehe Anhang, nun wird schon
schön umgerechnet, Korrektur faktor... etc)
Werte:
9922ns
9922ns
9922ns
9922ns
9922ns
10us
10us
10us und das bei ziemlich exakt 100us, find ich gut! (Frequenz 50kHz)
bei 500kHz wirds wieder brei aber egal! bei 5kHz bekomme ich Minuswerte:
-30879ns
-30879ns
-31000ns
-30879ns
-31000ns
-31121ns
__
Sieht doch glatt nach nem Overflow aus und das verlangt ja wohl nach nem
Presc.
Danke dir :*
So und hier noch das Funktionierende Programm, (Presc. is da abern och
nicht mit einberechnet. Ich denke da ansowas wie.
if DIFFERENZ<=0 (Minuswerte)
Dann Presc. einstellen.
und dann mal schaun wie ich es mache das der Presc. auch wieder
abschaltbar ist, aber ich denk mir da bestimmt noch was aus :-)
So ganz richtig funktionieren kann das immer noch nicht. Die variablen
DIFFERENZ ist nur als Int deklariert. Da gibt es mit Zahlen wie 125000
garantiert einen überlauf. Und der Vergleich mit 1000000 ist auch nicht
sinnvoll.
Am Ende der ISR ist auch immer noch der unnötige und falsche Versuch das
Interrupt Flag zu löschen. Da hier nur ein Interrupt genutzt wird
passiert nichts - aber wenn man sich das später noch mal ansieht und
ggf. Code kopiert, kann man so herrlich versteckte Fehler kopieren.