Hi,
ich versuche gerade die Zeit zu messen, die ein Signal im Zustand "high"
ist. Das Signal liegt am Input Capture Pin 1 (ICP1) an.
Es handelt sich hierbei um einen Ultraschall Entfernungsmesser, aber das
sollte nichts zur Sache tun.
Im Wesentlichen läuft Timer1 im CTC Modus und löst alle 50 ms einen
Interrupt aus. Darin wird der Trigger Pin für das Ultraschall Modul für
10 uS auf High gesetzt sowie der Input Capture Trigger so eingestellt,
dass er auf eine Rising Edge reagiert (Bit ICES1 gesetzt). Wie im
Datenblatt angegeben lösche ich außerdem das Input Capture Flag (ICF1).
Nun gibt es die eigentliche ISR für den Input Capture. Hier wird
zunächst geprüft, ob es sich um eine Rising Edge handelt. Sofern dies
der Fall ist speichere ich den aktuellen Zählerstand in einer Variable,
und setze den Input Capture Trigger auf "Falling Edge" (und lösche das
Input Capture Flag wieder entsprechend).
Im Fall, dass die ISR bei einer Falling Edge aufgerufen wird,
subtrahiere ich den aktuellen Zählerstand vom vorher festgehaltenen und
habe damit die Zeit, die das Signal "high" war.
In der Theorie ganz einfach. In der Praxis habe ich aber stets ein
"paar" Takte Versetzung, da das Einlesen des Zählerstandes (16 Bit
Wert), sowie der Vergleich innerhalb der ISR, ob es sich nun um eine
Rising bzw. Falling Edge handelt ja auch nicht "umsonst" ist.
Insbesondere die Fallunterscheidung gefällt mir nicht so recht. Gibt es
hier eine bessere Alternative? Ich arbeite zum Ersten Mal mit dem Input
Capture, insofern ist es durchaus möglich, dass ich etwas Grundlegendes
übersehe?
Ich hänge hier mal noch die beiden ISRs in ihrer aktuellen Form an. In
der Hauptschleife passiert nichts wirklich Interessantes. Dort wird nur
bei Bedarf etwas auf einem LCD ausgegeben.
Ich hoffe, ich konnte den Ablauf halbwegs klar machen und freue mich auf
eure Vorschläge!
1
staticuint16_techoHighLevelTime=0;
2
ISR(TIMER1_COMPA_vect){
3
4
/*
5
* Reset echoHighLevelTime in case there was an overflow
6
*/
7
echoHighLevelTime=0;
8
9
/*
10
* Send Trigger pulse (according to datasheet needs to be 10 uS at least)
11
*/
12
PORT(TRIGGER)|=_BV(BIT(TRIGGER));
13
_delay_us(10);
14
PORT(TRIGGER)&=~(_BV(BIT(TRIGGER)));
15
16
/*
17
* Set Input Capture trigger to rising edge
18
*/
19
TCCR1B|=_BV(ICES1);
20
21
/*
22
* Clear Input capture flag
23
*/
24
TIFR1|=_BV(ICF1);
25
26
}
27
28
ISR(TIMER1_CAPT_vect){
29
30
staticuint16_thighRiseStartCounterValue;
31
32
/*
33
* Check whether ISR was triggered by a rising edge
34
*/
35
if(TCCR1B&_BV(ICES1)){
36
37
/*
38
* Save the current counter value
39
*/
40
highRiseStartCounterValue=TCNT1;
41
42
/*
43
* Set Input Capture trigger to falling edge
44
*/
45
TCCR1B&=~(_BV(ICES1));
46
47
/*
48
* Clear Input capture flag
49
*/
50
TIFR1|=_BV(ICF1);
51
52
/*
53
* ISR was triggered by a falling edge
54
*/
55
}else{
56
57
/*
58
* Substract the counter value from the start in order to get
> /*> * Save the current counter value> */> highRiseStartCounterValue = TCNT1;
Der Charme des Input Capture Interrupts besteht darin, dass der Timer
selber den Zählerstand exakt beim Eintreten des Capture Ereignisses in
einem eigenen Register sichert.
-> In der ISR holst du dir diesen Zählerstand aus diesem Register und
nicht aus TCNT1. Denn dann ist es ziemlich egal (im Rahmen), wieviele
Takte vom Capture Event vergehen, bis du dir den Wert abholst. Im
Register ICR1 steht der Zählerstand, zu dem der Input Capture ausgelöst
hat, egal wie weit der Zähler in der Zwischenzeit weiter gezählt hat.
highRiseStartCounterValue = ICR1;
...
echoHighLevelTime = ICR1 - highRiseStartCounterValue;
tatsächlich ist es genau dieses Feature, welches den Input Capture
Interrupt interessant macht. Sonst könnte man ja auch gleich einfach nur
einen externen Interrupt nehmen und bräuchte den Input Capture gar
nicht.
Karl Heinz Buchegger schrieb:> In der ISR holst du dir diesen Zählerstand aus diesem Register und> nicht aus TCNT1.
Klar, da habe ich mich extreme trottelig angestellt. Vielen Dank für die
Aufklärung ;).
Um die "Weiche" innerhalb der ISR komme ich aber nicht herum, oder? Auch
wenn es das Ergebnis nicht verfälscht ...
Cube schrieb:> Um die "Weiche" innerhalb der ISR komme ich aber nicht herum, oder?
Nachdem du auf beide Flanken, steigend und fallend, reagieren willst:
nein. Da führt kein Weg vorbei. Irgendwie musst du das unterscheiden.
Kostet doch fast nichts. Die 2 Assembler Instruktionen tun nicht weh.
Karl Heinz Buchegger schrieb:> Da führt kein Weg vorbei. Irgendwie musst du das unterscheiden.
Ok, vielen Dank. Und ist es denn wenigstens üblich das so zu machen wie
ich, sprich innerhalb der ISR auf das Bit "ICES1" hin zu prüfen, oder
hat sich da etwas anderes bewährt?
Cube schrieb:> Karl Heinz Buchegger schrieb:>> Da führt kein Weg vorbei. Irgendwie musst du das unterscheiden.>> Ok, vielen Dank. Und ist es denn wenigstens üblich das so zu machen wie> ich, sprich innerhalb der ISR auf das Bit "ICES1" hin zu prüfen, oder> hat sich da etwas anderes bewährt?
Ich würde das als die beste Alternative ansehen.
Welche Möglichkeiten gibt es
* am Pin nachsehen ob der Pegel 0 oder 1 ist.
WEnn 0, dann muss es eine fallende Flanke gewesen sein
Wenn 1, dann muss es eine steigende Flanke gewesen sein
Problem: Vom Zeitpunkt der Flanke aus gerechnet, bis dann
endlich der Pin abgefragt wird, vergeht Zeit. Zeit, in der
der Pin seinen Zustand schon wieder geändert haben könnte.
-> ist also nicht 100% zuverlässig
* in einer Variablen mitzählen, der wievielte Aufruf das war
(oder, was aufs gleiche rauskommt: eine Variable abwechselnd
0 bzw. 1 setzen und anhand der entscheiden)
Problem: Wird auch nur 1 Flanke verpasst (aus welchem Grund auch
immer), kommt alles durcheinander. Anstatt der Länge des Pulses
misst man die Länge der Pause.
* Beim INput Capture nachfragen, worauf eigentlich reagiert wurde.
Also deine Methode
Vorteil: Man hat die Gewissheit, dass die 'Auskunft' auf jeden
Fall korrekt ist. Die ISR wurde aufgerufen, WEIL eine steigende
Flanke detektiert wurde. Befrage ich die Cature Unit, wie sie
gerade eingestellt ist, dann lautet die Auskunft: steigende Flanke
Wäre da eine fallende Flanke gewesen, hätte die Capture Unit mit
der Einstellung nicht reagiert.
Kommt also der Interrupt und sagt mir die Capture Unit, das sie
auf steigend konfiguriert ist, dann war da auch eine steigende
Flanke. 100% sicher.
Eine eigene Frage?
Dieser Thread ist über 14 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.