- Warum Timer, wenn wir Delay () haben?
- PIC-Mikrocontroller-Timer:
- Programmieren und Arbeiten Erklärung:
- Schaltplan und Proteus-Simulation:
Dies ist das fünfte Tutorial in unserer PIC-Tutorial-Reihe, mit dem Sie Timer in PIC16F877A lernen und verwenden können. In unseren vorherigen Tutorials hatten wir mit der Einführung in PIC und MPLABX IDE begonnen. Dann haben wir unser erstes PIC-Programm zum Blinken der LED mithilfe von PIC geschrieben und anschließend mithilfe der Verzögerungsfunktion im PIC-Mikrocontroller eine LED-Blinksequenz erstellt. Lassen Sie uns nun dieselbe LED-Blinksequenz verwenden, die wir in der vorherigen Tutorial-Hardware verwendet haben, und damit lernen wir, wie Timer in unserer PIC-MCU verwendet werden. Wir haben gerade eine weitere Schaltfläche in der LED-Platine für dieses Tutorial hinzugefügt. Lesen Sie das Tutorial, um mehr zu erfahren.
Timer sind eines der wichtigsten Arbeitspferde für einen eingebetteten Programmierer. Jede Anwendung, die wir entwerfen, beinhaltet irgendwie eine Timing-Anwendung, wie das Ein- oder Ausschalten nach einem bestimmten Zeitintervall. Okay, aber warum brauchen wir Timer, wenn wir bereits Verzögerungsmakros (__delay_ms ()) haben, die dasselbe tun !!
Warum Timer, wenn wir Delay () haben?
Ein Verzögerungsmakro wird als "Dump" -Verzögerung bezeichnet. Denn während der Ausführung der Verzögerungsfunktion setzt die MCU den Speicherauszug, indem sie nur eine Verzögerung erzeugt. Während dieses Vorgangs kann die MCU ihre ADC-Werte nicht abhören oder nichts aus ihren Registern lesen. Daher ist es nicht ratsam, Verzögerungsfunktionen zu verwenden, außer für Anwendungen wie das Blinken der LED, bei denen die Zeitverzögerung nicht genau oder lang sein muss.
Die Verzögerungsmakros weisen auch die folgenden Mängel auf:
- Der Verzögerungswert muss für Verzögerungsmakros eine Konstante sein. Sie kann während der Programmausführung nicht geändert werden. Daher bleibt es vom Programmierer definiert.
- Die Verzögerung ist im Vergleich zur Verwendung von Timern nicht genau.
- Größere Werte für Verzögerungen können nicht mit Makros erstellt werden. Eine Verzögerung von einer halben Stunde kann beispielsweise nicht mit Verzögerungsmakros erstellt werden. Die maximale Verzögerung, die verwendet werden kann, basiert auf dem verwendeten Kristalloszillator.
PIC-Mikrocontroller-Timer:
Physikalisch ist der Timer ein Register, dessen Wert kontinuierlich auf 255 ansteigt und dann von vorne beginnt: 0, 1, 2, 3, 4… 255…. 0, 1, 2, 3……usw.
Die PIC16F877A PIC-MCU verfügt über drei Timer-Module. Sie sind Namen wie Timer0, Timer1 und Timer2. Der Timer 0 und der Timer 2 sind 8-Bit-Timer und der Timer 1 ist ein 16-Bit-Timer. In diesem Tutorial verwenden wir den Timer 0 für unsere Anwendung. Sobald wir den Timer 0 verstanden haben, wird es einfach sein, auch an Timer 1 und Timer 2 zu arbeiten.
Der Timer / Zähler des Timer0-Moduls verfügt über folgende Funktionen:
- 8-Bit-Timer / Zähler
- Lesbar und beschreibbar
- Programmierbarer 8-Bit-Prescaler
- Interne oder externe Uhr auswählen
- Unterbrechung beim Überlauf von FFh bis 00h
- Flankenauswahl für externe Uhr
Um einen Timer zu verwenden, sollten wir einige der ausgefallenen Begriffe wie 8-Bit / 16-Bit-Timer, Prescaler, Timer-Interrupts und Focs verstehen. Lassen Sie uns nun sehen, was jeder wirklich bedeutet. Wie bereits erwähnt, gibt es in unserer PIC-MCU sowohl 8-Bit- als auch 16-Bit-Timer. Der Hauptunterschied besteht darin, dass der 16-Bit-Timer eine viel bessere Auflösung als der 8-Bit-Timer hat.
Prescaler ist ein Name für den Teil eines Mikrocontrollers, der den Oszillatortakt teilt, bevor er eine Logik erreicht, die den Timerstatus erhöht. Der Bereich der Prescaler-ID liegt zwischen 1 und 256, und der Wert des Prescaler kann über das OPTION-Register eingestellt werden (das gleiche, das wir für Pull-up-Widerstände verwendet haben). Wenn der Wert von Vorteiler zum Beispiel ist 64, dann für jeden 64 - ten Timer - Impuls wird um 1 erhöht werden.
Wenn der Timer inkrementiert wird und seinen Maximalwert von 255 erreicht, löst er einen Interrupt aus und initialisiert sich wieder auf 0 zurück. Dieser Interrupt wird als Timer-Interrupt bezeichnet. Dieser Interrupt informiert die MCU darüber, dass diese bestimmte Zeit abgelaufen ist.
Der Fosc steht für Frequenz des Oszillators, es ist die Frequenz des verwendeten Kristalls. Die für das Timer-Register benötigte Zeit hängt vom Wert des Prescaler und vom Wert des Fosc ab.
Programmieren und Arbeiten Erklärung:
In diesem Tutorial werden zwei Tasten als zwei Eingänge und 8 LED als 8 Ausgänge eingestellt. Mit der ersten Taste wird die Zeitverzögerung eingestellt (500 ms für jeden Druck) und mit der zweiten Taste wird das Blinken der Timer-Sequenz gestartet. Wenn beispielsweise die erste Taste dreimal gedrückt wird (500 * 3 = 1500 ms), wird die Verzögerung auf 1,5 Sekunden eingestellt, und wenn die zweite Taste gedrückt wird, wird jede LED mit der vordefinierten Zeitverzögerung ein- und ausgeschaltet. Überprüfen Sie das Demonstrationsvideo am Ende dieses Tutorials.
Lassen Sie uns nun unter Berücksichtigung dieser Grundlagen unser Programm betrachten, das am Ende im Abschnitt Code angegeben ist.
Es ist in Ordnung, wenn Sie das Programm nicht bekommen haben, aber wenn Sie es getan haben !! Geben Sie sich ein Cookie und sichern Sie das Programm, um Ihre Ausgabe zu genießen. Für andere werde ich das Programm in sinnvolle Teile aufteilen und Ihnen erklären, was in jedem Block passiert.
Da die ersten paar Zeilen des Codes immer die Konfigurationseinstellungen und Header-Dateien sind, werde ich dies nicht erklären, da ich dies bereits in meinen vorherigen Tutorials getan habe.
Als nächstes überspringen wir alle Zeilen und springen direkt in die void-Hauptfunktion, in der wir die PORT-Konfiguration für den Timer0 haben.
void main () {/ ***** Portkonfiguration für Timer ****** / OPTION_REG = 0b00000101; // Timer0 mit externer Frequenz und 64 als Prescalar // Aktiviert auch PULL-UPs TMR0 = 100; // Lade den Zeitwert für 0.0019968s; delayValue kann nur zwischen 0 und 256 liegen. TMR0IE = 1; // Timer-Interrupt-Bit im PIE1-Register aktivieren GIE = 1; // Global Interrupt aktivieren PEIE = 1; // Peripheral Interrupt aktivieren / *********** ______ *********** /
Um dies zu verstehen, müssen wir uns das OPTION-Register in unserem PIC-Datenblatt ansehen.
Wie im vorherigen Tutorial erläutert, wird das Bit 7 verwendet, um einen schwachen Pull-up-Widerstand für den PORTB zu aktivieren. In der obigen Abbildung wird das Bit 3 auf 0 gesetzt, um die MCU anzuweisen, dass der folgende eingestellte Vorteiler für den Timer und nicht für den WatchDogTimer (WDT) verwendet werden soll. Der Timer-Modus wird durch Löschen von Bit 5 T0CS ausgewählt
(OPTION_REG <5>)
Jetzt wird mit den Bits2-0 der Prescaler-Wert für den Timer eingestellt. Wie in der obigen Tabelle gezeigt, müssen die Bits auf 101 gesetzt werden, um einen Vorteilerwert von 64 einzustellen.
Schauen wir uns als nächstes die mit Timer0 verknüpften Register an
Der Timer beginnt nach dem Einstellen zu inkrementieren und läuft nach Erreichen eines Wertes von 256 über. Um den Timer-Interrupt während dieses Punktes zu aktivieren, muss das Register TMR0IE auf High gesetzt werden. Da Timer 0 selbst ein Peripheriegerät ist, müssen wir den Peripherie-Interrupt aktivieren, indem wir PEIE = 1 setzen. Schließlich müssen wir den globalen Interrupt aktivieren, damit die MCU während eines Vorgangs über den Interrupt benachrichtigt wird. Dies erfolgt durch Setzen von GIE = 1.
Verzögerung = ((256-REG_val) * (Prescal * 4)) / Fosc
Die obige Formel wird verwendet, um den Wert der Verzögerung zu berechnen.
Wo
REG_val = 100;
Prescal = 64
Fosc = 20000000
Dies bei Berechnung ergibt, Verzögerung = 0,0019968s
Der nächste Satz von Zeilen besteht darin, die E / A-Ports festzulegen.
/ ***** Portkonfiguration für E / A ****** / TRISB0 = 1; // Weisen Sie die MCU an, dass der PORTB-Pin 0 als Eingang für Taste 1 verwendet wird. TRISB1 = 1; // Weisen Sie die MCU an, dass der PORTB-Pin 1 als Eingang für Taste 1 verwendet wird. TRISD = 0x00; // Weisen Sie die MCU an, dass alle Pins an PORT D ausgegeben werden. PORTD = 0x00; // Initialisiere alle Pins auf 0 / *********** ______ *********** /
Dies ist das gleiche wie in unserem vorherigen Tutorial, da wir dieselbe Hardware verwenden. Nur dass wir eine weitere Schaltfläche als Eingabe hinzugefügt haben. Dies erfolgt durch die Zeile TRISB1 = 1.
Als nächstes haben wir von innen nach außen unendlich while- Schleife zwei Codeblöcke. Einer wird verwendet, um den Timer-Eingang vom Benutzer zu erhalten, und der andere, um die Verzögerungssequenz über die LEDs auszuführen. Ich habe sie mit Kommentaren für jede Zeile erklärt.
während (1) {count = 0; // Timer nicht ausführen, während er sich in der Hauptschleife befindet // ******* Die Nummernverzögerung vom Benutzer abrufen **** ////// if (RB0 == 0 && flag == 0) // Wann Eingabe gegeben {get_scnds + = 1; // get_scnds = get_scnds + http: // Inkrementiere Variable flag = 1; } if (RB0 == 1) // Um ein kontinuierliches Inkrementierungsflag zu verhindern = 0; / *********** ______ *********** /
Eine Variable namens get_scnds wird jedes Mal inkrementiert, wenn der Benutzer die Taste 1 drückt. Eine Flag- Variable (softwaredefiniert) wird verwendet, um den Inkrementierungsprozess zu halten, bis der Benutzer seinen Finger von der Taste entfernt.
// ******* Sequenz mit Verzögerung ausführen **** ////// while (RB1 == 0) {PORTD = 0b00000001 <
Der nächste Block wird aktiviert, wenn die zweite Taste gedrückt wird. Da der Benutzer die erforderliche Zeitverzögerung bereits über die Schaltfläche 1 definiert hat und diese in der Variablen get_scnds gespeichert wurde. Wir verwenden eine Variable namens hscnd, diese Variable wird von der ISR (Interrupt Service Routine) gesteuert.
Die Interrupt-Serviceroutine ist ein Interrupt, der jedes Mal aufgerufen wird, wenn der Timer0 überläuft. Lassen Sie uns im nächsten Block sehen, wie es vom ISR gesteuert wird. Wenn Sie die Zeitverzögerung bei jedem Tastendruck um eine halbe Sekunde (0,5 s) erhöhen möchten, müssen Sie die Variable hscnd für jede halbe Sekunde erhöhen . Da wir unsere Timer über Fluss für alle 0.0019968s (~ 2 ms) programmiert haben, so halbe Sekunde zu zählen Zahl sollte variabel 250, weil 250 * 2 ms = 0,5 Sekunden sein. Wenn also count 250 (250 * 2ms = 0,5 Sekunden) erreicht, bedeutet dies, dass es eine halbe Sekunde ist, also erhöhen wir hscnd um 1 und initialisieren count auf Null.
void Interrupt timer_isr () {if (TMR0IF == 1) // Timer-Flag wurde aufgrund eines Timer-Überlaufs ausgelöst {TMR0 = 100; // Lade den Timer Wert TMR0IF = 0; // Timer Interrupt Flag Count löschen ++; } if (count == 250) {hscnd + = 1; // hscnd wird für jede halbe Sekunde erhöht = 0; }}
Also verwenden wir diesen Wert und vergleichen ihn mit unserem hscnd und verschieben unsere LED basierend auf der benutzerdefinierten Zeit. Es ist auch dem letzten Tutorial sehr ähnlich.
Das ist es, wir haben unser Programm verstanden und arbeiten.
Schaltplan und Proteus-Simulation:
Wie üblich können wir zuerst die Ausgabe mit Proteus überprüfen. Ich habe hier die schematischen Dateien des Proteus verlinkt.
Fügen Sie unserer vorherigen LED-Platine eine Schaltfläche hinzu, und unsere Hardware ist einsatzbereit. Es sollte ungefähr so aussehen:
Laden Sie nach Abschluss der Verbindung den Code hoch und überprüfen Sie die Ausgabe. Wenn Sie ein Problem haben, verwenden Sie bitte den Kommentarbereich. Überprüfen Sie auch das Video unten, um den gesamten Prozess zu verstehen.