Hallo Spezis!
Am ICP1 meines ATmega8 liegt ein pwm-Signal. Ich messe parallel mit
einem Oszi die Pulsweite mit 12.42ms, die Frequenz ist 10Hz (~100ms).
Mit dem angehängten Programm möchte ich nun diese Pulsweite messen [und
im Endeffekt einen Abstand messen, es handelt sich nämlich um einen
Ultraschallsensor, aber das tut jetzt hier nichts zur Sache].
Im aktuellen Zustand sollen 8 LEDS, die an den D-Port angeschlossen sind
leuchten, wenn er etwas gemessen hat. Seltsamerweise funktioniert das so
nicht.
Nach vielen verschiedenen Versuchen habe ich inzwischen aber
herausgefunden, dass er sowohl timer_fall als auch timer_rise bestimmt,
sie sind nicht null.
Aber irgendetwas in Zeile 105:
Das geht sowieso schief. Wenn Du einen solchen Wert wie NrOverflows um
16 Stellen nach links schiebst, kommt in jedem Fall Null raus. Die Werte
auf der rechten Seite der Zuweisung sind allesamt <= unsigned int, also
werden alle Berechnungen auch in 16 Bit durchgeführt. Wenn die
Schieberei was sinnvolles ergeben soll, dann muss NrOverflows vor dem
Schieben nach unsigned long gecastet werden!
Übrigens:
Wenn das {8B} am Anfang des Kommentars bei z.B.
1
doublepulsewidth=0.0;// {8B} [s] time between rising and falling edge
bedeuten soll, dass das 8 Bit sind, dann liegst Du falsch. Der AVR-GCC
unterstützt in seiner aktuellen Inkarnation keine
64-Bit-Gleitkommaverarbeitung (es sei denn, mir ist da was entgangen).
Dementsprechend ist sizeof(double) == sizeof(float) == 4. Verlasse Dich
dementsprechend auch nicht auf die trügerische "Genauigkeit" der
Gleitkommageschichten. Man kann u.U. genauere Ergebnisse erzielen, wenn
man mit Festkommaarithmetik arbeitet.
Du mußt Dich schon entscheiden, entweder ICP pollen oder
Interrupthandler.
Beides zusammen geht schief, da klaut der eine dem anderen das Ereignis
unterm Hintern weg.
Und zum Erweitern von Timern, das hier beachten:
Beitrag "AVR Timer mit 32 Bit"
sonst gibts Übertragsprobleme.
Peter
@johnny-m:
> Das geht sowieso schief. Wenn Du einen solchen Wert wie NrOverflows um> 16 Stellen nach links schiebst, kommt in jedem Fall Null raus. Die Werte> auf der rechten Seite der Zuweisung sind allesamt <= unsigned int, also> werden alle Berechnungen auch in 16 Bit durchgeführt. Wenn die> Schieberei was sinnvolles ergeben soll, dann muss NrOverflows vor dem> Schieben nach unsigned long gecastet werden!
Vielen Dank für Deine Hinweise. Ich habe meinen Code dementsprechend
angepasst, was aber leider keine Veränderung gebracht hat. :-/
DER NEUE CODE IST IM ANHANG.
Peter Dannegger wrote:
> Du mußt Dich schon entscheiden, entweder ICP pollen oder> Interrupthandler.
Was bedeutet das? Was habe ich denn falsch gemacht / verstanden?
Was genau bedeutet, bzw. ist der Unterschied zwischen "ICP pollen" und
"Interrupthandler"?
Was ich dachte, was ich tue ist folgendes, bitte korrigiere mich:
- Einstellungen der relevanten Register in Zeile 86:
- Mitteilung: achte auf steigende Flanken in Zeile 94:
1
TCCR1B=(1<<ICES1);// rising edge detection
- Solange warten, bis das Input Capture Flag Timer/Counter 1 gesetzt
ist, Zeile 95:
1
while(TIFR&(0<<ICF1)){;}// nothing to do, just wait for detection
Hier sollte er so lange verweilen, bis in der ISR in Zeilen 71-74...
1
ISR(TIMER1_CAPT_vect){
2
timer_buff=ICR1;// store time in buffer
3
NrOverflows=0;// reset overflow counter
4
}
... das ICF1 gesetzt wurde und dann den gespeicherten Timer-Wert
abspeichern und weitermachen.
Das scheint so weit ja auch zu klappen, wie gesagt:
> Nach vielen verschiedenen Versuchen habe ich inzwischen aber> herausgefunden, dass er sowohl timer_fall als auch timer_rise bestimmt,> sie sind nicht null.
Von daher verstehe ich gerade leider nicht, was Du damit meinst, ...
sorry!
Hallo nochmal,
ich habe gerade noch etwas ausprobiert, ich scheine in der Rechnung
etwas echt falsch zu machen!
1
timer_diff1=timer_fall-timer_rise;
2
timer_diff2=timer_fall+timer_rise;
3
timer_diff3=timer_rise-timer_fall;
4
5
if(timer_diff1>0){
6
PORTD+=0xC0;
7
}else{
8
PORTD=x00;
9
}
10
11
if(timer_diff2>0){
12
PORTD=0x30;
13
}else{
14
PORTD=0x00;
15
}
16
17
if(timer_diff3>0){
18
PORTD=0x0C;
19
}else{
20
PORTD=0x00;}
21
}
Für den Fall, dass timer_diff(1..3) berechnet werden, also ein Ergebnis
größer als null beinhalten, sollen jeweils zwei LEDs zum Leuchten
gebracht werden.
Es gehen nur die 0x30 LEDs an, es scheint also nur die Addition zu
klappen. Die Subtraktion - weder in die eine noch in die andere Richtung
führen zu einem Ergebnis - warum!?
>- Solange warten, bis das Input Capture Flag Timer/Counter 1 gesetzt>ist, Zeile 95:while(TIFR&(0<<ICF1)){;} // nothing to do, just wait for >detection
Das nennt sich Pollen
>Hier sollte er so lange verweilen, bis in der ISR in Zeilen 71-74...>ISR(TIMER1_CAPT_vect){> timer_buff = ICR1; // store time in buffer> NrOverflows = 0; // reset overflow counter>}
Hier greifst du auf das gleiche Ereignis noch mal zu.
Das ist das, was Peter meinte: Du behandelst das Gleiche Ereignis in
deinem Programm auf zwei verschiedene Weisen zur gleichen Zeit (mehr
oder weniger).
Das Umschalten der Flankenrichtung kannst du mit in die ISR packen.
In der ISR mußt du dann auch je nach Flankenrichtung entscheiden, was du
tun willst. Oder Du gibst gar nicht erst den Interrupt frei, sparst dir
die ISR und mußt nur für "timer_buff" "ICP" einsetzen.
> while(TIFR&(0<<ICF1)){;}
Auch mal überlegt, dass die Schleifenbedingung nie wahr werden kann?
Irgendwas mit 0 verUNDet ist immer false. Und eine 0 kannst Du so oft
durch die Gegend schieben, wie Du willst, es bleibt eine Null.
STK500-Besitzer wrote:
> Hier greifst du auf das gleiche Ereignis noch mal zu.> Das ist das, was Peter meinte: Du behandelst das Gleiche Ereignis in> deinem Programm auf zwei verschiedene Weisen zur gleichen Zeit (mehr> oder weniger).
Mir ist die ganze Funktionsweise von Interrupts offensichtlich noch
nicht klar. Anscheinend laufen die komplett parallel. Aber woher erfährt
dann die main(), welche Flanke gerade dran war und was sie jetzt machen
soll!?
> Das Umschalten der Flankenrichtung kannst du mit in die ISR packen.
Hm. Die Umschaltung in die ISR!? Also so ne art Switch der immer hin-
und herschaltet!?
> In der ISR mußt du dann auch je nach Flankenrichtung entscheiden, was du> tun willst.
Das heißt ich würde dann in der ISR auch je nach Flanke die jeweiligen
Variablen (also rising und falling) belegen... kopfkratz ... und WAS
fängt dann die main() WIE damit an? Woher weiß sie, dass sie gerade die
zusammengehörigen Variablen gespeichert hat und nicht einen halben
Schritt weiter ist!?
> Oder Du gibst gar nicht erst den Interrupt frei, sparst dir> die ISR und mußt nur für "timer_buff" "ICP" einsetzen.
Ich denke mal Du meinst nicht ICP sondern ICR1?
Welchen Interrupt gebe ich nicht frei? Gar keinen? Auf welche Zeile
beziehst Du Dich damit? Und die ISR soll ich komplett löschen? Aber wie
löst denn der Interrupt aus und beschreibt den ICR1?
Mann, ich versteh da echt noch einiges nicht, irgendwas blockiert in
meinem Hirn bei dieser Interrupt-Denkweise.
Ich bitte um Schläge auf den Hinterkopf!
DANKE!
Johannes M. wrote:
>> while(TIFR&(0<<ICF1)){;}> Auch mal überlegt, dass die Schleifenbedingung nie wahr werden kann?> Irgendwas mit 0 verUNDet ist immer false. Und eine 0 kannst Du so oft> durch die Gegend schieben, wie Du willst, es bleibt eine Null.
Ja, danke für den Hinweis, ist korrigiert, aber hilft mir immer noch
nicht weiter. seufz
>Meist werden in den interrupts Flags gesetzt die dann im Main>ausgewertet und zurück gesetzt werden.
Schwammiger geht es vermutlich nicht...
Die Interrupt-Flags werden immer gesetzt, wenn ein entsprechendes
Ereignis eintritt. Entweder hat man in einem Interrupt-Mask-Register den
Sprung in eine ISR freiggeben oder nicht. Wenn man diesen Sprung
freigegeben hat, dann braucht man auch eine ISR, sonst führt das zu
ungewünschten Programmsprüngen.
Wenn eine ISR aufgerufen wird, wird i.d.R. das Interrupt-Flag
zurückgesetzt - benutzt man keine ISR, dann muß man das Flag manuell
zurücksetzen.
Guck dir mal die Interrupt-Behandlung im Datenblatt an. Da ist das recht
gut erklärt.