mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATmega8 Pulweitenmessung


Autor: Anna-zaira Engeln (nanalisa)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
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!

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Anna-zaira Engeln wrote:
>
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.
 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Anna-zaira Engeln (nanalisa)
Datum:
Angehängte Dateien:

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

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

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

- Mitteilung: achte auf steigende Flanken in Zeile 94:
TCCR1B = (1<<ICES1); // rising edge detection

- 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

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

Autor: Anna-zaira Engeln (nanalisa)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo nochmal,

ich habe gerade noch etwas ausprobiert, ich scheine in der Rechnung 
etwas echt falsch zu machen!
timer_diff1 = timer_fall - timer_rise;
timer_diff2 = timer_fall + timer_rise;
timer_diff3 = timer_rise - timer_fall;
             
if(timer_diff1>0) {
                   PORTD += 0xC0;
                  }else{
                        PORTD =x00;
                       }            
            
if(timer_diff2>0){
                  PORTD = 0x30;
                 }else{
                       PORTD = 0x00;
                      }            
            
if(timer_diff3>0){   
                  PORTD = 0x0C;
                 }else{
                       PORTD = 0x00;}
                      }
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!?

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Anna-zaira Engeln (nanalisa)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Anna-zaira Engeln (nanalisa)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Skua C:\> (skua)
Datum:

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

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.