www.mikrocontroller.net

Forum: Compiler & IDEs 16-bit-Timer - wer kann helfen?


Autor: Egon Müller (kpc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich versuche, die Dauer von high-Perioden an einem ATmega644-Port 
mittels eines 16-bit-Timers auszuzählen.

Hier der kurze Code:

//###### 16-bit-Zähler für Flügelrad
unsigned long highabfrage(void)
{
    unsigned long high = 0;
    DDRD &= 127;                         // PORTD7 wird Eingang

  while ( !(PIND & 1<<PD7));             // warte auf high

  TIFR1 = 1;                        // nach overflow manuell rücksetzen
  TCCR1B |=  (1<<CS12) | (1<<CS10); // Timerstart mit 1/1024

  while ((PIND & 1<<PD7) && !(TIFR1 & 1<<TOV1)); // warte auf low
                                                 // oder T-overflow
   TCCR1B = 0;                                   // Timer stop
   high = TCNT1L + (TCNT1H<<8);                  // auslesen

    return (high);
}

Die zu erfassenden high-Perioden sind z.Zt. 87 ms lang, die 
Timerfrequenz ist 16 MHz / 1024 = ca 15 kHz, also 67 mysec/Takt. Demnach 
sollte der Timer typischerweise bis 1300 kommen, bevor Port D7 low wird. 
Bei Sensorschaden bleibt der Port D7 high und die Schleife endet durch 
Timeroverflow. Ein Interrupt wird nicht ausgelöst, deshalb die manuelle 
Rückstellung von TIFR. Ein Interrupt während der Timerauslesens findet 
auch nicht statt. Da ich nicht sicher war, ob der Zähler mit 0 startet, 
habe ich ihn auch mal manuell auf Null gestellt, hat aber auch nicht 
geholfen.

Die ausgebenen Zahlen schwanken wild, von den relativ konstanten 
high-Perioden keine Spur.

Was mache ich hier falsch?
Gibt es irgendwo, in einem verborgenen Winkel, den ich nicht gefunden 
habe, Beispielcode?

Gruß
Egon

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Da ich nicht sicher war, ob der Zähler mit 0 startet,
>habe ich ihn auch mal manuell auf Null gestellt, hat aber auch nicht
>geholfen.

Der Zähler startet nach einem Reset mit Null, danach aber zählt er immer 
von dem Zählerstand weiter, den er gerade hat. Insofern musst du das 
Null-Stellen am Anfang der Funktion wieder einbauen.
  high = TCNT1L + (TCNT1H<<8);   

geht auch einfacher mit
  high = TCNT1;   

Damit kannst du sicher sein, daß TCNT1L vor TCNT1H gelesen wird. Bei 
deiner handgestrickten Version darf der Compiler das umstellen (auch 
wenn er es sehr wahrscheinlich nicht tut).

Du bist dir sicher, daß deine Signale nicht prellen?

Oliver

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch was:

Für den 16-Bit-Zähler reicht ein unsigned int. Da braucht es kein long. 
Empfehlenswert ist die Verwendung der Typen aus stdint.h, in deinem Fall 
uint16_t. Damit gibt es weniger Verwirrung, wie groß nun welcher 
Datentyp ist.

Oliver

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast du meinen Post im anderen Thread überhaupt gelesen?

Beitrag "Re: watchdog ohne Totalreset?"

Autor: Egon Müller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Ernst,
ärgerlicherweise erst zu spät.
Normalerweise falle ich begierig über neue  Antworten her.
Schade, dann hätte ich die Sache mit dem Rücksetzen des TIFR1 nicht aus 
dem Manual klauben müssen.
Nichts gefunden habe ich über den Startwert des Zählers. Bist Du sicher, 
daß der Zähler vor dem Starten erst auf Null gesetzt werden muß? 
Immerhin könnte das meine Zufallszahlen erklären, aber anderseits müßte 
dann doch wenigstens hin und wider für high 65536 zu lesen sein, wenn er 
immer weiterzählt und dadurch Überlauf und Schleifenabbruch erreicht.
Was bekommt man eigentlich, wenn man auf einen Overflow reagiert und 
sofort den Zähler anhält und ausliest? Etwa Null, weil er dann schon 
resettet ist?

Auf das Auslesen von TCNT1 nach der 8-bit-Methode war ich verfallen, 
weil es mit 16-bit nicht funktioniert hatte. Aber da hatte ich mich bloß 
bei der Variablenbenennung vertan, da ich nicht nur L und H, sondern 
auch noch die "1" weggelassen hatte.


@hi Oliver
auf dem Oszi sind sehr schöne Rechteckkurven zu sehen, allerdings kriege 
ich ein gewisses Zittern nicht heraus - aber das dürfte dem ATmega egal 
sein.

Erst mal vielen Dank für Eure Hilfe,
ich melde mich nach dem Ausprobieren wieder


Viele Grüße
Egon

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Egon Müller schrieb:

> Nichts gefunden habe ich über den Startwert des Zählers. Bist Du sicher,
> daß der Zähler vor dem Starten erst auf Null gesetzt werden muß?

Ja. (abgesehen vom allerersten Start natürlich)

> Immerhin könnte das meine Zufallszahlen erklären, aber anderseits müßte
> dann doch wenigstens hin und wider für high 65536 zu lesen sein, wenn er
> immer weiterzählt und dadurch Überlauf und Schleifenabbruch erreicht.
> Was bekommt man eigentlich, wenn man auf einen Overflow reagiert und
> sofort den Zähler anhält und ausliest? Etwa Null, weil er dann schon
> resettet ist?

Auch nach einem Überlauf läuft der Timer munter weiter. Bei einem 
Prescaler von 1024 hast du ja aber genug Zeit, auf das Overflow-Flag zu 
reagieren. Du solltest dann also tatsächlich 0 auslesen.

Apropos Prescaler: 1024? Im anderen Thread hattest du noch Sorge um 
deine Auflösung wegen einer Hand voll zusätzlicher Cycles in der 
manuellen Zählschleife und jetzt setzt du die Auflösung auf 1/1024?

Autor: Egon Müller (kpc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst schrieb:
?
>
> Auch nach einem Überlauf läuft der Timer munter weiter. Bei einem
> Prescaler von 1024 hast du ja aber genug Zeit, auf das Overflow-Flag zu
> reagieren. Du solltest dann also tatsächlich 0 auslesen.

Eigentlich wäre es mir lieber gewesen, ich würde den vollen Zählerwert 
von 65536 auslesen können, aber das geht wohl nicht. Schade
>
> Apropos Prescaler: 1024? Im anderen Thread hattest du noch Sorge um
> deine Auflösung wegen einer Hand voll zusätzlicher Cycles in der
> manuellen Zählschleife und jetzt setzt du die Auflösung auf 1/1024?

Hatte ich zwischendurch zum Testen so niedrig gesetzt, weil das 
Zählergebenis so aussah, als würden da irgendwelche Variablen ihren 
Wertebereich verlassen. Aber wenn jetzt der Zähler als Übeltäter 
identifiziert ist....

Viele Grüße
Egon

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nur so nebenbei:
Der höchste Wert bei 16 Bit ist 65535.

Autor: Egon Müller (kpc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst schrieb:
> Nur so nebenbei:
> Der höchste Wert bei 16 Bit ist 65535.

Bei 16 bit kann ich mir nur "65000-ungrad" merken.
Aber Du hast schon recht. Schade, daß man an diesen Wert bei 
Timeroverflow nicht herankommt - hätte sich im übrigen Programm gut 
gemacht.

Übrgigens: Ich werde mich doch noch mit dem Watchdog vertraut machen 
müssen - wegen eines anderen Sensors. Aber ich finde einfach keine 
Tutorials, z.B., wie man mit ISR umgeht usw. Wenigsten ist im Manual zu 
lesen, daß der Watchdog des ATmega644Bei nicht nur einen Reset, sondern 
auch einen Interrupt auslösen kann. Die Dinger werden immer besser.

Viele Grüße
Egon

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Einerseits gibt es für solche Sachen die InputCapture-Einheit, 
andererseits kann man sowas wirklich einfach selber basteln:

Timer Initialisieren und laufen lassen.

In Schleife auf l-Pegel warten // da man ja mitten im Impuls zu messen 
anfangen könnte

In Schleife auf H-Pegel warten // positive Flanke erkennen...
Timerwert in erster Variablen sichern
In Schleife auf L-Pegel warten
Timerwert in zweiter Variablen sichern

Differnz zweite Variable - erste Variable errechnen

Rückgabe der Differenz

Autor: Egon Müller (kpc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
STK500-Besitzer schrieb:
> Einerseits gibt es für solche Sachen die InputCapture-Einheit,

Ja, benutze ich auch in einer anderen Schaltung und es funktioniert 
bestens.
Nachteil: es gibt nur einen Pin für input capture, man braucht einen 
Logikbaustein der die eingehenden Signale nacheinander auf diesen Pin 
legt, wobei man von dem Logikbaustein verlangen muß, daß sich seine 
Ausgänge zusammnenschalten lassen. Erfreulicherweise habe ich dann noch 
bei einen Ditributor diesen Baustein gefunden.

> andererseits kann man sowas wirklich einfach selber basteln:
>
> Timer Initialisieren und laufen lassen.
>
> In Schleife auf l-Pegel warten // da man ja mitten im Impuls zu messen
> anfangen könnte
>
> In Schleife auf H-Pegel warten // positive Flanke erkennen...
> Timerwert in erster Variablen sichern
> In Schleife auf L-Pegel warten
> Timerwert in zweiter Variablen sichern
>
> Differnz zweite Variable - erste Variable errechnen
>
> Rückgabe der Differenz

Früher hatte ich eine Eigenbaulösung, wo ich während der High-Periode 
ganz einfach eine Variable incrementiert hatte - was auch ging. Hier im 
Forum hat man mir empfohlen, einen Timer zu benutzten. Das tue ich 
jetzt; jedesmal, bevor ich ihn starte, setze ihn auf Null. Dann kann ich 
als Nebeneffekt das Zählen bei Sensorschaden (=Dauerhigh) abbrechen, 
sozusagen als Watchdogersatz für jemanden, der noch keine rechten 
Gebrauchsanweisungen für Interrupthandling gefunden hat.


Viele Grüße
Egon

Autor: Egon Müller (kpc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Egon Müller schrieb:

> sozusagen als Watchdogersatz für jemanden, der noch keine rechten
> Gebrauchsanweisungen für Interrupthandling gefunden hat.
>
>
> Viele Grüße
> Egon

Erledigt - meine alten Tutorialausdrucke waren schon sehr veraltet.
In den aktualisierten Tutorials steht alles, was mir gefehlt hatte!

Egon

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Egon Müller schrieb:
> STK500-Besitzer schrieb:
>> Einerseits gibt es für solche Sachen die InputCapture-Einheit,
>
> Ja, benutze ich auch in einer anderen Schaltung und es funktioniert
> bestens.
> Nachteil: es gibt nur einen Pin für input capture, man braucht einen
> Logikbaustein der die eingehenden Signale nacheinander auf diesen Pin
> legt, wobei man von dem Logikbaustein verlangen muß, daß sich seine
> Ausgänge zusammnenschalten lassen. Erfreulicherweise habe ich dann noch
> bei einen Ditributor diesen Baustein gefunden.

Falls du den ADC nicht verwendest zu dem Zeitpunkt kannst du einfach den 
ADC deaktivieren und über dessen Input-MUX eines von 6 oder 8 Signalen 
zum AC durchschleifen. Der Ausgang des AC wird schliesslich zum InCapt 
verdrahtet.

Es braucht da keine externe Hardware wie Analog-Multiplexer à la 4051 
oder so.

Johann

Autor: Egon Müller (kpc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> Falls du den ADC nicht verwendest zu dem Zeitpunkt kannst du einfach den
> ADC deaktivieren und über dessen Input-MUX eines von 6 oder 8 Signalen
> zum AC durchschleifen. Der Ausgang des AC wird schliesslich zum InCapt
> verdrahtet.
>
Interessant, werde mal über den Input-MUX nachlesen, vielen Dank!

Gruß
Egon

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.