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


von Sven F. (finkman)


Angehängte Dateien:

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Die Auswertereihenfolge bei dieser Operation:
1
  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:
1
        uint16_t temp = TCNT1;

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

von Stefan B. (stefan) Benutzerseite


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?

von Sven F. (finkman)


Lesenswert?

Die Lösung des Rätzels:
> Benutze stattdessen die 16-Bit-Pseudoregisternamen:
>
>
1
>         uint16_t temp = TCNT1;
2
>
>
> 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

von Johannes M. (johnny-m)


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.

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.