Forum: Mikrocontroller und Digitale Elektronik ATmega8 Pulweitenmessung


von Anna-zaira E. (nanalisa)


Angehängte Dateien:

Lesenswert?

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:
1
timer_diff = (NrOverflows << 16) + timer_fall - timer_rise;
scheint ihn derartig zu irritieren, dass er timer_diff gar nicht 
berechnet und auf null lässt.

Was mache ich denn falsch?

Vielen Dank schon mal!

von Johannes M. (johnny-m)


Lesenswert?

Anna-zaira Engeln wrote:
>
1
timer_diff = (NrOverflows << 16) + timer_fall - timer_rise;
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
 double  pulsewidth    = 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.

von Peter D. (peda)


Lesenswert?

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

von Anna-zaira E. (nanalisa)


Angehängte Dateien:

Lesenswert?

@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:
1
TCCR1B = (1<<CS10);   // no prescale -> CPU frequency

- Aktivierung der relevanten Interrupts:
1
TIMSK  = (1<<TICIE1) | (1<<TOIE1); // activate input capture + overflow interrupts

- Enablen von interrupt handling in Zeile 91:
1
sei();// global interrupt enable

- 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!

von Anna-zaira E. (nanalisa)


Lesenswert?

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!?

von STK500-Besitzer (Gast)


Lesenswert?

>- 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.

von Johannes M. (johnny-m)


Lesenswert?

> 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.

von Anna-zaira E. (nanalisa)


Lesenswert?

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!

von Anna-zaira E. (nanalisa)


Lesenswert?

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

von Skua C. (skua)


Lesenswert?

Meist werden in den interrupts Flags gesetzt die dann im Main 
ausgewertet und zurück gesetzt werden.

von STK500-Besitzer (Gast)


Lesenswert?

>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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.