Frage:
Lesen eines 16-Bit-Timers auf einer 8-Bit-MCU
ajs410
2010-08-04 04:23:31 UTC
view on stackexchange narkive permalink

Da eine 8-Bit-MCU nicht den gesamten 16-Bit-Timer in einem Zyklus lesen kann, entsteht eine Race-Bedingung, bei der das Low-Word zwischen den Lesevorgängen wechseln kann. Hat die Community eine bevorzugte Methode, um diese Rennbedingungen zu vermeiden? Ich denke derzeit darüber nach, den Timer während des Lesens anzuhalten, aber ich würde gerne wissen, ob es eine elegantere Lösung gibt.

Zu Ihrer Information, dies ist für einen PIC16F690. P. >

Verwandte: http://stackoverflow.com/questions/5162673/how-to-read-two-32bit-counters-as-a-64bit-integer-without-race-condition
Dies ist ein Problem, um das sich alle Mikrocontroller kümmern, AFAIK. Wenn ein Byte gelesen wird, wird das andere zwischengespeichert, und das Lesen des anderen Bytes wird aus dem Zwischenspeicher gelesen. Problem gelöst.
Vier antworten:
Windell Oskay
2010-08-04 06:31:40 UTC
view on stackexchange narkive permalink

Überprüfen Sie Ihr Datenblatt! Es wird ausführlich für Ihren speziellen Chip darüber sprechen.

Ich bin nicht sicher, ob dies für alle 16-Bit-Zähler- / Timer-Register auf allen PICs gilt (Vielleicht kann jemand anderes darauf antworten!) Aber zumindest für den PIC 18F, den ich verwende, spricht das Datenblatt speziell darüber, wie damit umgegangen wird.

Wenn Sie das Low-Byte lesen, puffert es das High-Byte in ein temporäres Register für das High-Byte, sodass Sie beim nächsten Lesen des High-Bytes eine vollständige Momentaufnahme des Timer-Werts erhalten.

Der gleiche grundlegende Prozess wird bei AVR (und natürlich bei Arduino) und in den meisten anderen Fällen verwendet, in denen Sie Zwei-Byte-Register auf 8-Bit-MCUs gleichzeitig lesen oder schreiben müssen.

Ich erinnere mich auch daran, dies in AVR-Dokumenten gelesen zu haben
Natürlich habe ich das Datenblatt überprüft, aber es ist eigentlich nicht so blutig, wie Sie vielleicht denken. Nichts im Text für timer1 (Kapitel 6) zeigt an, dass das High-Byte gepuffert wird. Unter dem asynchronen Zähler (6.5.1) befindet sich ein Unterabschnitt mit der Aufschrift "Für Schreibvorgänge wird empfohlen, dass der Benutzer den Timer einfach stoppt und die gewünschten Werte schreibt". Zum Lesen wird nichts erwähnt. Deshalb bin ich in die Community gekommen
Es macht es jetzt viel einfacher, da Sie den tatsächlich verwendeten Chip veröffentlicht haben. ;) Und ... ich würde es ziemlich blutig nennen: Es heißt ausdrücklich, dass zwischen den Lesevorgängen ein Überlauf auftreten kann. Ihre Lösung, den Timer anzuhalten, ist zwar unelegant, aber je nach Anwendung möglicherweise * sicher *.
akohlsmith
2010-08-04 11:28:15 UTC
view on stackexchange narkive permalink

Windell hat Recht, wenn Sie über PICs sprechen, übernimmt das Datenblatt (und die Hardware) dies bereits für Sie.

Wenn Sie keinen PIC verwenden, ist die allgemeine Methode, die ich verwende dies:

  Byte hi, lo; Wort timer_value; do {hi = TIMER_HI; lo = TIMER_LO;} while (hi! = TIMER_HI); timer_value = (hi << 8) | lo;  

Dies bedeutet, das obere Byte gefolgt vom unteren Byte zu lesen und so lange fortzufahren, bis sich das Hi-Byte nicht mehr ändert. Dies behandelt leicht den Fall, in dem das niedrige Byte des 16-Bit-Werts zwischen den Lesevorgängen überläuft. Es wird natürlich davon ausgegangen, dass das mehrmalige Lesen des TIMER_HI-Registers keine Nebenwirkungen hat. Wenn Ihr spezieller Mikroprozessor dies nicht zulässt, ist es an der Zeit, ihn wegzuwerfen und einen zu verwenden, der nicht ganz so geisteskrank ist. :-)

Diese Methode geht AUCH davon aus, dass sich Ihr Timer nicht so schnell ändert, dass Sie das Risiko eingehen, die niedrigen 8 Bits innerhalb eines oder zweier Prozessorabrufzyklen zu überlaufen. Wenn Sie einen Timer so schnell (oder einen Mikroprozessor so langsam) ausführen, ist es Zeit, Ihre Implementierung zu überdenken.

Mit dieser Technik können Sie auch einen Timer lesen, der aus einem Hardware-Timer und einem im Überlauf-Interrupt inkrementierten Software-Zähler besteht.
Danke, ich mag diese Technik besser als das Stottern des Timers während eines Lesevorgangs.
Schließlich fand eine Microchip App Hinweis, "PIC Micro Mid-Range MCU-Familie". Es scheint alt zu sein, scheint aber die 16F-Serie detaillierter zu beschreiben als das Datenblatt. Die zum Lesen des freilaufenden 16-Bit-Timers vorgeschlagene Technik passt recht gut zu Ihrem Vorschlag, daher markiere ich sie als Antwort.
Im Allgemeinen wäre meine Antwort "Mach was der Compiler macht" gewesen - schreibe etwas in C und sieh und sieh, wie die Assembly aussieht.
O_Engenheiro Die Schleife wird zweimal ausgeführt, wenn ein Überlauf auftritt, und einmal unter allen anderen Umständen. Wenn Sie mehr als das schleifen, ist entweder Ihre Timer-Uhr viel zu schnell oder Ihre CPU viel zu langsam.
@Andrew Kohlsmith, ich habe meinen Kommentar gelöscht. Entschuldigung, ich habe den Code falsch analysiert. Vielen Dank.
@Andrew Kohlsmith: Wenn sich der Timer im synchronen Modus befindet, ist Ihr Ansatz in Ordnung. Wenn der Timer im asynchronen Modus läuft und nicht zu schnell ist (z. B. läuft er von einem 32-kHz-Uhrenkristall ab), ist es besser, erneut zu schalten, wenn sich das LSB des Timers geändert hat. Andernfalls besteht die Gefahr, dass sich der Timer genau während des Lesens ändert und ein ungültiger Messwert angezeigt wird.
@supercat Ich bin nicht sicher, ob ich es verstehe. Wenn der Timer asynchron zum CPU-Takt ist, wird höchstens 1 Timer abgehakt. Das bedeutet nicht, dass der Messwert ungültig ist. Wenn Sie jedoch meinen, dass das Bitmuster im Register ungültig ist, handelt es sich um ein sehr schlechtes CPU- / Peripheriedesign. Peripherieregister sollten vom Hersteller ordnungsgemäß synchronisiert werden.
@Andrew Kohlsmith: Ein Zähler, der von einer CPU, deren Takt asynchron zu ihr ist, zuverlässig gelesen werden kann, benötigt ein gutes Stück Silizium. Im Wesentlichen muss man entweder in Graycode zählen oder die Anzahl in Graycode konvertieren, diese dann synchronisieren und dann den Graycode-Wert wieder in "normale" Binärdatei konvertieren. In Fällen, in denen der CPU-Takt - * beim Ausführen * - mindestens dreimal schneller als das zu zählende Signal ist, ist es möglicherweise einfacher, den Zähler synchron zu takten, wenn die CPU wach ist, und asynchron, wenn sie schläft ...
@Andrew Kohlsmith: ... aber das Umschalten zwischen synchroner und asynchroner Taktung, ohne eine Zählung zu gewinnen oder zu verlieren - selbst in Randfällen - ist schwierig. Wenn die Software mit dem wiederholten Lesen des Timers auskommt, bis sie mehrmals dieselbe Antwort erhält, kann einfachere Hardware verwendet werden, als dies sonst erforderlich wäre. Beachten Sie, dass dieser Ansatz nur dann wirklich nicht funktioniert, wenn die Zählrate im Vergleich zur CPU-Taktrate ziemlich hoch ist, was darauf hindeuten würde, dass der Graycode-Ansatz der richtige ist.
@Andrew Kohlsmith: Beachten Sie, dass es keinen wirklichen Nachteil gibt, wenn der Zähler zurückkehrt, es sei denn, es ist eine Funktion zum synchronen 16-Bit-Lesen vorgesehen oder die Zählrate ist so schnell, dass die CPU vor niedrigen Änderungen nicht in der Lage ist, niedrig-hoch-niedrig zu lesen Beschädigte Werte, wenn sie während der Änderung gelesen werden (dies ist sicherlich weniger nachteilig als ein gelegentlicher Gewinn oder Verlust, der durch das Umschalten zwischen synchronem und asynchronem Modus verursacht wird).
@supercat interessant. Ich habe mich im Laufe der Jahre mit vielen Mikrocontrollern beschäftigt und lerne VHDL, und ich bin noch nie darauf gestoßen. Ich habe mich auch mit meinem VHDL "Tutor" unterhalten und er hat auch noch nie ein solches Problem in einem Mikrocontroller gesehen. Wo hast du das schon gesehen? Ich wette, das war ein echter Bär zum Debuggen!
@Andrew Kohlsmith: Das Debuggen von asynchronem Material ist nicht allzu schwierig, wenn man davon ausgeht, dass sich Signale zu den schlechtesten Zeiten nicht ändern können. Tatsächlich bevorzuge ich in vielen Fällen völlig unsynchronisierte Timer gegenüber solchen, die versuchen, die mit der Asynchronität verbundenen Probleme zu vermeiden, da ich mich mehrmals mit Hardwarefehlern befassen musste, die mit solchen Angelegenheiten zusammenhängen. Ein undokumentierter Ärger bei vielen PIC-Timern ist beispielsweise, dass, wenn der Timer einen anderen Wert als FFFF enthält und mit FFFF geschrieben ist, der Überlauf-Interrupt ...
... wird bei der nächsten Zählung nicht passieren, sondern nach 65.537 Zählungen. Ich vermute, dass das Design von dem Wunsch herrührt, zu vermeiden, dass ein falscher Interrupt ausgelöst wird, wenn z. Der Zähler hatte 128 gehalten und wurde mit 127 geschrieben, gerade als eine Zählung einging, aber ich hätte lieber einfach in der Dokumentation angegeben, dass jedes Schreiben in den Timer im asynchronen Modus das Interrupt-Flag fälschlicherweise setzen könnte, so dass ein sicherer Betrieb erforderlich wäre Deaktivieren des Interrupts, Schreiben des Timers, Löschen des Flags und erneutes Aktivieren des Interrupts.
@Andrew Kohlsmith: Am liebsten hätte ich ein System mit einem asynchronen 48-Bit-Zähler, der aus eigener Stromversorgung betrieben wird, und einem Vergleichsregister auf den unteren 32 Bit, das auch dann funktionieren könnte, wenn der Haupttakt des Prozessors gestoppt wäre. Es wäre nicht nötig, in den 48-Bit-Zähler zu schreiben, und es würde mir nichts ausmachen, Vergleichsinterrupts zu deaktivieren, wenn in das Vergleichsregister geschrieben wird. Ein solches Timersystem würde es einem RTOS ermöglichen, Aufgaben mit einer Genauigkeit von 17us oder 33us zu planen und den Hauptoszillator jederzeit auszuschalten, wenn er nicht benötigt wird.
@Andrew Kohlsmith: Der EFM Gekko ist fast perfekt; Meine einzige Beschwerde ist, dass der Timer nur 24 Bit beträgt und die Zeit nicht halten kann, während die CPU ausgeschaltet ist. Die ST Micro 32F-Serie scheint etwas besser zu sein, mit einer Echtzeituhr in Form eines 32-Bit-Linearzählers und eines programmierbaren Vorskalars. Meine einzige Beschwerde mit diesen 32 Bit ist etwas zu kurz (bei einer Zählrate von 256 Hz würde das Ding das Datum effektiv vergessen, wenn es für ein Jahr ausgeschaltet würde; bei einer schnelleren Zählrate würde es früher überlaufen).
@Andrew Kohlsmith: Leider hat ST die Dinge mit der 32L-Serie etwas ruiniert. Der RTC-Chip hat einen einstellbaren Prescalar, muss aber leider in einem YMDHMS-Format ausgelesen werden, das eine Inkrementierungsrate von 1 Sekunde annimmt, und seine Alarmregister nehmen das HMS-Format an. Wenn die Zählrate 256 Hz beträgt, würden die Alarmregister Einheiten von 1/256 Sekunden, 15/64 Sekunden und 14 1/16 Sekunden darstellen (wobei letztere alle 337,5 Sekunden umbrechen). Weitaus weniger praktisch für die Verwendung mit RTOS-Scheduling.
Kevin Vermeer
2010-08-04 21:29:15 UTC
view on stackexchange narkive permalink

Der Text, auf den Sie in Ihrem Kommentar (Abschnitt 6.5.1) verwiesen haben, bezieht sich auf den asynchronen Timer-Betrieb, bei dem der Timer von einer Quelle außerhalb des Mikrocontrollers getaktet wird.

Der Haftungsausschluss schlägt vor, den Timer zu starten und zu stoppen, da Andrews allgemeine Methode (wirklich jede Methode) nicht funktioniert, da möglicherweise viele Ticks (mehr als 256, sogar) der externen Uhr und damit des Timers vorhanden sind in einem einzigen Zyklus des Prozessors. Ich gehe davon aus, dass Sie mit diesem Randfall nicht arbeiten.

Wenn Sie stattdessen den internen Oszillator als Referenz verwenden, ist alles in Ordnung. Selbst mit einem Prescaler von 1 haben Sie bis zu 255 Zyklen, nachdem Sie das High-Byte gelesen haben, um das Low-Byte zu lesen. Dies sollte ein 2-Zyklus-Vorgang sein. Sie benötigen weiterhin die do / while-Schleife, falls sich Ihr High-Byte zwischen diesen Zeiten ändert (Was ist der Unterschied zwischen 'hi' und 'TIMER_HI', wenn der Wert von 'lo' 0 ist?). Mit einem Vorteiler von 2 oder mehr benötigen Sie diesen Test nicht mehr. Nur mit Vorskalierern unter 1 wird es zu einem Problem, und dies ist ohne die externe Uhr nicht möglich.

Ja, ich weiß, dass es ein asynchroner Timer war, aber es ist das Datenblatt, das dem Problem am nächsten kommt. Obwohl es jetzt sinnvoller ist, warum sie vorschlagen würden, die Uhr anzuhalten, anstatt Andrews Lösung zu erwähnen (obwohl sie nicht einmal * das * im Datenblatt erwähnen, sondern einen separaten App-Hinweis).
Daniel Grillo
2010-08-04 23:36:04 UTC
view on stackexchange narkive permalink

Im PIC Mid-Range MCU-Referenzhandbuch zum Lesen und Schreiben von Timer1 im asynchronen Zählermodus finden Sie ein Beispiel.

Beispiel: Lesen eines 16-Bit-Free- Timer ausführen

 ; Alle Interrupts sind deaktiviertMOVF TMR1H, W; Lesen Sie High-ByteMOVWF TMPH; MOVF TMR1L, W; Niedriges Byte lesenMOVWF TMPL; MOVF TMR1H, W; High byte lesenSUBWF TMPH, W; Sub 1. Lesen mit 2. LesenBTFSC STATUS, Z; Ist Ergebnis = 0GOTO WEITER; Gutes 16-Bit-Lesen ;; TMR1L wurde möglicherweise zwischen dem Lesen der High- und Low-Bytes verschoben.; Wenn Sie jetzt die hohen und niedrigen Bytes lesen, wird ein guter Wert angezeigt .; MOVF TMR1H, W; Lesen Sie High-ByteMOVWF TMPH; MOVF TMR1L, W; Lesen Sie low byteMOVWF TMPL ;; Aktivieren Sie den Interrupt erneut (falls erforderlich). WEITER; Fahren Sie mit Ihrem Code  
fort
Ja, das ist das Handbuch, das bestätigt, dass Andrews Vorlage der beste Kompromiss war. Obwohl ich sein Beispiel so modifiziert habe, dass es eher der Versammlung ähnelt; d.h. anstelle von do-while habe ich nur ein if verwendet.


Diese Fragen und Antworten wurden automatisch aus der englischen Sprache übersetzt.Der ursprüngliche Inhalt ist auf stackexchange verfügbar. Wir danken ihm für die cc by-sa 2.0-Lizenz, unter der er vertrieben wird.
Loading...