mikrocontroller.net

Forum: Compiler & IDEs 16Bit Timer, Externe Clock, Frequenzmessung


Autor: Sven Fink (finkman)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!
Folgendes ist mein Aufbau:
Ich habe einen RGB Sensor, welcher die Helligkeit über ein 
Frequenzgenerator ausgibt.
Bei maximaler Helligkeit liefert eine 600kHz Recheckspannung.
Meine Idee war, mit dieser Rechteckspannung den 16bit Counter des 
Atmega8, welcher mit 16Mhz läuft, anzusteuern. Somit liegt das Rechteck 
des Signals am PD5 Eingang.

Mit dem Timer0 bestimme ich mein Messinterval und habe diesen auf den 
1024 Prescale Takt gelegt.
Wenn Time0 einer Overflow-interrupt auslöst, halte ich den 16Bit Counter 
an TCCR1B = 0; und entnehme den Zählerstand.
Danach setze ich den Zähler auf Null zurück und lasse alles wieder von 
vorne beginnen.

(Im angehängten Code, wird noch der RGB Filter des Sensors geändert, was 
allerdings ja nichts ausmachen dürfte.)

Mein Problem:
Ich erhalte nur werte zwischen 0 und 255, das HiByte des Counter bleibt 
aus mir unerkenntlichen Gründen auf 0.
Nimmt man nun ein Lampe und beleuchtet den Sensor so wandert der 
Messwert bis zu 255 hoch und beginnt dann erneut bei 0. (Overflow)

Zudem find ich es interessant, dass wenn ich am Ende meiner 
Interruptroutine den Zählerstand nicht auf Null zurücksetzte, sehrwohl 
werte über 255 erhalte.

Ich bin sehr froh, wenn mir jemand die Lösung für dieses Rätzel liefern 
könnte.
Zudem nehm ich auch gerne alternative Lösungsvorschläge an.

Mit freundlichen Grüßen und Danke im Vorraus

Sven Fink

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Auswertereihenfolge bei dieser Operation:
  uint16_t temp = (TCNT1H<<8) | TCNT1L;

ist undefiniert.  Es ist dem Compiler frei gestellt, ob er zuerst
TCTN1H oder TCNT1L dabei liest.

Benutze stattdessen die 16-Bit-Pseudoregisternamen:
        uint16_t temp = TCNT1;

Dafür generiert der Compiler sowohl beim Lesen als auch beim Schreiben
die korrekte Reihenfolge der Zugriffe.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In deinem Code sehe ich keine saubere Trennung zwischen Messung und 
Ausgabe.

Derzeit hast du Messungen am laufen (gestartet in init_io()), die durch 
eine Ausgabe (cli/sei-Block in while(1)) teilweise (Timer0 OVF ja, 
Counter1 nein) unterbrochen werden und dann wieder zufällig 
weitergeführt werden (long_delay(300)).

Ich würde das sauberer trennen oder wenigstens das Sperren der 
Interrupts auf das kurzest mögliche Maß reduzieren:

  {
    uint16_t temp;
    cli();
    temp = RGBC[i];
    sei();
    uart_printl(temp);
  }

statt

    uart_printl(RGBC[i]);

Wenn ich das Zeitverhalten nachrechne (F_CPU 16 MHz, Prescaler Timer0 = 
1024 und Startwert 0), komme ich auf einen Timer0-Overflow alle 
0,016384s (256*1024/16000000).

Bei 600 kHz (max. Helligkeit) an dem 16-Bit Counter1 an Pin T1 wären 
somit 9830,4 (600000*0,016384) steigende Flanken zu erwarten.

Bist du im Bereich der max. Helligkeit oder arbeitest du im Bereich 
niedriger Frequenzen (<15,5 kHz bei 255 Flanken). Hast du die 
Möglichkeit die Frequenz anderwärtig zu messen bzw. kannst du dein 
Programm mit einer definierten Frequenzquelle testen?

Autor: Sven Fink (finkman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Lösung des Rätzels:
> Benutze stattdessen die 16-Bit-Pseudoregisternamen:
>
>
>         uint16_t temp = TCNT1;
> 
>
> Dafür generiert der Compiler sowohl beim Lesen als auch beim Schreiben
> die korrekte Reihenfolge der Zugriffe.

Für mich zwar immernoch unlogisch, da der oder operator kommutativ ist, 
aber hauptsache es geht! Danke

Zudem hab ich auch die anderen Hinweiße berücksichtigt: Meine cli/sei 
Zeiten verkürzt und bei der Ausgabe auch den Counter angehalten.

Nochmals Danke

Gruß Sven Fink

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

Bewertung
0 lesenswert
nicht lesenswert
Sven Fink wrote:
> Für mich zwar immernoch unlogisch, da der oder operator kommutativ ist,
Eben deswegen kann das ja auch schief gehen! Beim Zugriff auf diese 
Doppelregister (das gilt für die TCNT- und OCRx-Register der 
16-Bit-Timer) ist die Zugriffsreihenfolge essentiell wichtig. Beim Lesen 
muss immer zuerst das Low-Byte gelesen werden, beim Schreiben muss 
zuerst das High-Byte geschrieben werden. Gleiches gilt (wenn auch 
hardwaremäßig etwas Anderes dahinter steckt) für das Datenregister des 
ADC (natürlich nur für das Lesen).

Näheres im Datenblatt ("Accessing 16 Bit Registers") und in den 
Tutorials.

Bei der Hochsprachen-Programmierung kann einem der Compiler die Sache 
mit der Reihenfolge abnehmen, wenn man die richtigen Bezeichner 
verwendet. Bei der Verwendung des 16-Bit-Pseudo-Registers TCNT1 weiß der 
Compiler sofort, was er zu tun hat. Bei den Einzelzugriffen kann er das 
nicht riechen.

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.