heim - Internet-Setup
Dynamische Sieben-Segment-Anzeige auf Avr. Kurze theoretische Informationen

Bei batteriebetriebenen Geräten gilt die Verwendung von LCD-Anzeigen im Allgemeinen als vorzuziehen gegenüber Leuchtdiodenanzeigen (LED), da letztere einen hohen Stromverbrauch haben. Dieses Postulat erscheint mir aus folgenden Gründen überhaupt nicht naheliegend: 1) In modernen LCD-Anzeigen gibt es eine Hintergrundbeleuchtung, die bis zu 100 mA verbraucht; 2) sie sind relativ zerbrechlich und haben Angst vor direkter Sonneneinstrahlung; 3) Moderne LED-Anzeigen (insbesondere superRED und ultraRED) haben auch bei einem Strom von 1 mA eine ausreichende Helligkeit und bei zeitnaher Helligkeitsanpassung je nach Lichtverhältnissen beträgt der durchschnittliche Stromverbrauch einer 4-stelligen Anzeige nicht mehr als 30 mA sogar im Freien, was weniger ist als der Verbrauch der LCD-Hintergrundbeleuchtung.

Trotz der Fülle an dynamischen Anzeigeschaltkreisen im Internet habe ich auf dem PIC16 keinen Schaltkreis mit Software-Helligkeitssteuerung gesehen. Dieser Artikel zeigt meine bescheidene Sicht auf die Umsetzung einer solchen Aufgabe. Es richtet sich vor allem an Funkamateure, die ihre ersten Schritte von sich wiederholenden Designs bis hin zu selbstprogrammierenden Mikrocontrollern unternehmen.

In dem Artikel wird erläutert, wie eine LED-Matrix mit einem PIC-Mikrocontroller der Mittelklasse mithilfe von Interrupts der Timer TMR0 und TMR2 gesteuert wird. Der Timer TMR2 wird zur PWM-Steuerung des durchschnittlichen Stroms durch die aktivierten Segmente verwendet. Der Arbeitsorganisationsalgorithmus ist wie folgt:

1. Initialisierung. Wir konfigurieren die Mikrocontroller-Ports gemäß dem Anzeige-Anschlussplan. Für die Timer 1 und 2 ist der interne Taktmodus mit einem Vorteiler von 16 aktiviert. Peripherie-Interrupts sind aktiviert.

2. Wir erstellen eine Zeichengeneratortabelle, um Zahlen und einige (meist lateinische) Buchstaben und Symbole auf dem Indikator anzuzeigen.

3. Wir reservieren zwei Vier-Bit-Variablen. In einem geben wir den sequentiellen digitalen Code (für Zahlen - nur eine Zahl) des für die Ausgabe erforderlichen Zeichens gemäß der Tabelle aus Abschnitt 2 ein. Die umgerechneten Werte aus der Tabelle werden zur dauerhaften Anzeige auf dem Indikator in eine andere Variable übertragen.

4. Im Interrupt von TMR0 werden die Zeichenbits der Reihe nach gemäß der Tabelle angezeigt. Vor einem Ziffernwechsel erlischt die Anzeige. Jeder Interrupt zeigt eine Ziffer an. Danach wird der TMR2-Timer zurückgesetzt, das Interrupt-Flag von TMR2 wird zurückgesetzt und Interrupts von ihm werden aktiviert.

5. Im TMR2-Interrupt erlischt die Anzeige und der TMR2-Interrupt ist deaktiviert.

6. Im Hauptprogramm wird die Unterbrechungsperiode von TMR2 und damit die Zeit, in der der Indikator eingeschaltet ist, durch Eingabe von Zahlen von 7 bis 255 in Dezimalrechnung in das PR2-Register unter Verwendung der Formel X(n+1)=2* angepasst. X(n)+1. Daraus ergeben sich sechs Helligkeitsabstufungen mit einem 2-fachen Unterschied zwischen ihnen. Bei PR2=255 ist die Dauer maximal (4ms von 4ms), bei PR2=7 beträgt die Dauer ca. 0,25ms.

Um dieses Steuerprinzip zu demonstrieren, finden Sie unten eine Schaltung auf einem kostengünstigen PIC16F628A und ein Testprogramm in Assemblersprache, das das Wort „Test“ auf dem Indikator anzeigt. Wenn Sie die Taste drücken, wird die Helligkeit auf der Anzeige angezeigt (üblicherweise Zahlen von 0 bis 5). Bei weiteren Betätigungen ändert sich die Helligkeit kreisförmig und ist sofort auf der Anzeige sichtbar. Ich möchte Anfänger sofort warnen: Wenn Sie eine Schaltung auf einem Simulator wie Proteus modellieren, können Sie aufgrund der Funktionen dieses Programms (Proteus) die Änderung der Helligkeit nicht sehen. Für Tests und Experimente muss ein Prototyp der Schaltung in Hardware zusammengebaut werden. Um jedoch die tatsächliche Organisation der dynamischen Anzeige (mit Ausnahme von Helligkeitsänderungen) zu überwachen, ist ein Proteus-Modell beigefügt.

Der Stromkreisverbrauch beträgt bei minimaler Helligkeit weniger als 4 mA, bei maximal etwa 80 mA.

Das Archiv enthält ein Testprogramm im MPASM-Assembler.

Um die Schaltung zu vereinfachen und die „Beine“ für verschiedene Aktionen freizugeben, wird eine Konfiguration mit internem Generator und internem Reset verwendet. Gleichzeitig können diejenigen, die einen selbstgebauten Programmierer verwenden, ohne das MCLR-Signal vor Upp senden zu können, Probleme mit der anschließenden Überprüfung, dem Lesen und Löschen haben. Für diejenigen, die sich ihrem Programmierer nicht sicher sind, und wenn eine hohe Stabilität des Oszillators erforderlich ist, können Sie einen 4-MHz-Quarz nach dem Standardschema mit der Auswahl in der Konfiguration „OSC_XT“ installieren. Wenn der Endstromkreis Unterbrechungen vom INT0-Pin (RB0) erfordert, kann das Komma über den RA4-Pin gesteuert werden; bei einem Indikator mit OA wird der Indikator trotz der Tatsache, dass er offen ist, direkt an diesen Pin angeschlossen. Der freigewordene Pin RB0 kann bestimmungsgemäß verwendet werden. Im Programm wird im Interrupt von TMR0 in diesem Fall der Code nach „movwf PORTB“ eingefügt:

Andlw b"00000001" bsf PORTA,4 unterdrücken das Komma btfsc STATUS,Z berücksichtigen, dass in W der Kehrwert ist. bcf PORTA,4 wenn 0. Bit = 0, helles Komma

Kleine Erläuterungen zum Programm:

Die Ausgabenummer wird entsprechend der Ziffer in den Variablen OUT_ - OUT+3 abgelegt und daraus im Unterprogramm out__ nach der Konvertierung in OUT_LED abgelegt. Natürlich könnte man auch auf die Variable OUT_ verzichten und überall für die Ausgabe schreiben:

Movlw X-Aufruf Table_s movwf OUT_LED

In seiner ursprünglichen Form ist jedoch alles viel einfacher und klarer (ich habe es in OUT_ platziert und vergessen), und auch bei mehreren Ausgaben von verschiedenen Stellen im Programm werden Codeeinsparungen erzielt (4 Wörter pro 1 Ausgabe) – ich denke, das ist eine Guter Ausgleich für die zusätzlichen 4 Byte RAM.

Das Gleiche gilt für die Ausgabe eines Kommas über die Variable „comma_“.

In der Tabellenunterroutine Table_s wurden Maßnahmen ergriffen, um korrekt zu funktionieren, wenn sie an einer beliebigen Stelle im Programmspeicher platziert wird, ohne Einschränkungen hinsichtlich der Schnittmenge von 256-Byte-Blöcken.

Die Variable pause_ im TMR0-Interrupt wird verwendet, um die Zeitintervalle auf 4 ms einzustellen.

Der Rest ergibt sich meiner Meinung nach aus dem Algorithmus und den Kommentaren.

P.S. Für 2 oder 3 Ziffern im Programm müssen Sie minimale Änderungen vornehmen, die meiner Meinung nach auch für Anfänger möglich sind. Um eine Anzeige mit einer Anzahl von Ziffern von 5 bis 8 zu steuern, ist es notwendig, entweder einen Controller mit einer großen Anzahl von Pins zu verwenden oder zur Steuerung der Ziffern einen 3 x 8-Decoder zu verwenden.

Im ersten Fall sind auch die Änderungen im Programm minimal (Verwendung eines anderen Ports anstelle von Port A usw.). Wenn ein Decoder verwendet wird, ändert sich das Programm bezüglich des TMR0-Interrupts erheblich.

Liste der Radioelemente

Bezeichnung Typ Konfession Menge NotizGeschäftMein Notizblock
U1 MK PIC 8-Bit

PIC16F628A

1 Zum Notizblock
H1 Indikator4x7 FIQ-3641A1 Zum Notizblock
Q1-Q4 Bipolartransistor

KT361E

4 Zum Notizblock
C3 Kondensator22 nF1 Zum Notizblock
R1-R7, R14 Widerstand

150 Ohm

8 Zum Notizblock
R8 Widerstand

Als Fortsetzung der Lektion schauen wir uns die dynamische Anzeige an. Wenn Sie die statische Anzeige sorgfältig studiert haben, wissen Sie, dass es sich bei einer Segmentanzeige um eine Reihe von LEDs handelt. Um den Indikator anzuschließen, benötigen Sie 7 Mikrocontroller-Pins. Aber plötzlich mussten wir mehrere Indikatoren verwenden, zum Beispiel 2, 3, 4 ...

Dann brauchen wir 14, 21, 28 Beine und es sind nicht genug Beine... Hier kommt uns die dynamische Anzeige zu Hilfe. Das Hauptziel der dynamischen Anzeige besteht darin, die Anzahl der verwendeten Mikrocontroller-Pins zu reduzieren. Bitte beachten Sie, dass im Diagramm 9 und nicht 14 Beine beteiligt sind. Die Steuerbeine sind alle parallel geschaltet.

Im Allgemeinen funktioniert dieses Design wie folgt: Zuerst wird die Konfiguration der ersten Nummer auf den gemeinsamen Bus ausgegeben und wir schalten PB1 ein. Die erste Anzeige leuchtet mit der gewünschten Nummer auf. Dann schalten wir es aus, geben die Konfiguration der zweiten Nummer an den Datenbus aus, leuchten die zweite Anzeige auf und schalten sie aus.

Ausführlicher. Im ersten Moment ist alles ausgeschaltet PORTB=0x00; PORTD=0xFF; da der Stromkreis über eine gemeinsame „+“-Anode verfügt. Als nächstes wird die Konfiguration der ersten Zahl, zum Beispiel „0“, an PORTD gesendet. Aus der statischen Anzeige erinnern wir uns:

Fall 0: ( PORTD= 0xC0 ; break ; )

Fall 0: ( PORTD=0xC0; break; )

Beachten Sie jedoch, dass „+“ mit PORTB.1 verbunden ist, d. h. Um ein Segment anzuzünden, müssen Sie das Bein PORTB.1=1 einschalten;

Im zweiten Moment schalten wir alles wieder aus, senden die Konfiguration der zweiten Nummer und schalten diesmal den zweiten Indikator ein. Als nächstes wiederholen wir.

Bei hohen Frequenzen kann das menschliche Auge diese Schalter nicht erkennen und die Anzeige scheint ständig zu leuchten. Es wird empfohlen, keine Frequenzen zu verwenden, die ein Vielfaches von 50 Hz betragen. In meinem Testprojekt habe ich 120Hz verwendet. Der Timer ist auf eine Frequenz von 1 MHz eingestellt. Der Code wird im Timer1-Interrupt verarbeitet. Der Interrupt wird 240 Mal pro Sekunde aufgerufen, da es zwei Indikatoren gibt, also schieben wir 1000.000/240=4166 oder 0x1046 in das Vergleichsregister. Es war nicht möglich, Proteus mit einem dynamischen Indikator zu koppeln, aber auf der Hardware funktionierte es sofort.

Beachten Sie!!! Beim Anschluss eines Indikators wird empfohlen, an jedes Segment einen strombegrenzenden Widerstand anzuschließen und nicht an einen gemeinsamen. Außerdem empfehle ich, das gemeinsame Anzeigekabel über einen Transistor anzuschließen, da Sie sich sonst das Bein verbrennen können.

Für einen Stromkreis mit gemeinsamer Anode

Für einen gemeinsamen Kathodenkreis

Als Test-Firmware habe ich einen Timer aus einem früheren Projekt verwendet.

Video des Firmware-Vorgangs

Indikatoren befinden sich normalerweise an geeigneten Stellen, um die darauf angezeigten Informationen anzuzeigen. Der Rest der digitalen Schaltung kann auf anderen Leiterplatten untergebracht werden. Mit zunehmender Anzahl der Indikatoren erhöht sich auch die Anzahl der Leiter zwischen der Anzeigeplatine und der Digitalplatine. Dies führt zu gewissen Unannehmlichkeiten bei der Entwicklung des Designs und des Betriebs der Ausrüstung. Der gleiche Grund führt zu einer Erhöhung der Kosten.

Die Anzahl der Anschlussleiter kann reduziert werden, indem die Anzeigegeräte im Impulsbetrieb betrieben werden. Das menschliche Auge ist träge, und wenn man die Indikatoren dazu zwingt, Informationen einzeln und mit ausreichend hoher Geschwindigkeit anzuzeigen, hat der Mensch den Eindruck, dass alle Indikatoren ihre Informationen kontinuierlich anzeigen. Dadurch ist es möglich, angezeigte Informationen abwechselnd über dieselben Leiter zu übertragen. Normalerweise reicht eine Bildwiederholfrequenz von 50 Hz aus, besser ist es jedoch, diese Frequenz auf 100 Hz zu erhöhen.

Schauen wir uns das Blockdiagramm der in Abbildung 1 gezeigten Sieben-Segment-LED-Anzeigen an. Diese Schaltung kann eine dynamische Anzeige der ausgegebenen digitalen Informationen liefern.


Abbildung 1. Blockdiagramm der dynamischen Anzeige

Im Diagramm in Abbildung 1 werden vier digitale Ziffern angezeigt. Jedes Bit wird kurzzeitig mit einem eigenen Eingang des Schalters verbunden. Der Generator wird verwendet, um die Geschwindigkeit der Aktualisierung von Informationen zu Indikatoren festzulegen. Der Binärzähler generiert nacheinander vier Zustände der Schaltung und versorgt die Sieben-Segment-Anzeigen über die Tasten abwechselnd mit Strom.

Wenn der Schalter daher einen binären Dezimalcode von Eingang A an die Eingänge des Sieben-Segment-Decoders liefert, wird dieser Code auf der HL1-Anzeige angezeigt. Wenn der Schalter den Binär-Dezimal-Code von Eingang B an die Eingänge des Sieben-Segment-Decoders liefert, wird dieser Code auf der HL2-Anzeige usw. in einem Zyklus angezeigt.

Die Informationsaktualisierungsrate im betrachteten Schema ist viermal geringer als die Frequenz des Generators. Das heißt, um eine Blinkfrequenz des Indikators von 100 Hz zu erhalten, ist eine Generatorfrequenz von 400 Hz erforderlich.

Wie oft haben wir dadurch die Anzahl der Anschlussleiter reduziert? Es hängt davon ab, wo wir den Querschnitt des Stromkreises zeichnen. Wenn wir nur Indikatoren auf der Anzeigetafel belassen, sind für deren Betrieb 7 Informationssignale für Segmente und vier Schaltsignale erforderlich. Insgesamt gibt es 11 Dirigenten. In einer statischen Anzeigeschaltung würden wir 7×4=28 Leiter benötigen. Wie Sie sehen, liegen die Gewinne auf der Hand. Bei der Implementierung einer 8-Bit-Anzeigeeinheit ist der Gewinn sogar noch größer.

Ein noch größerer Gewinn ergibt sich, wenn der Querschnitt des Stromkreises entlang der Eingänge der Indikatoren verläuft. In diesem Fall benötigt die vierstellige Anzeigeeinheit nur sechs Signalleiter und zwei Stromkreisleiter. Ein solcher Querschnittspunkt der dynamischen Anzeigeschaltung wird jedoch nur sehr selten verwendet.

Berechnen wir nun den Strom, der durch jedes LED-Segment fließt, wenn es aufleuchtet. Dazu verwenden wir eine Ersatzschaltung des Stromflusses durch eines der Anzeigesegmente. Dieses Diagramm ist in Abbildung 2 dargestellt.


Wie bereits erwähnt, benötigt eine LED für den normalen Betrieb einen Strom von 3 bis 10 mA. Stellen wir den minimalen LED-Strom auf 3 mA ein. Im Impulsbetrieb nimmt die Helligkeit des Indikators jedoch N-mal ab, wobei der Koeffizient N gleich dem Arbeitszyklus der diesem Indikator zugeführten Stromimpulse ist.

Wenn wir die gleiche Helligkeit des Leuchtens beibehalten wollen, müssen wir die Stärke des durch das Segment fließenden Impulsstroms um das N-fache erhöhen. Für einen achtstelligen Indikator ist der Koeffizient N gleich acht. Wählen wir zunächst einen statischen Strom durch die LED von 3 mA. Um die gleiche Helligkeit der LED in der achtstelligen Anzeige aufrechtzuerhalten, ist dann ein gepulster Strom erforderlich:

Ich seg din = Ich seg stat× N= 3mA×8 = 24mA.

Nur einige Serien digitaler Mikroschaltungen können einen solchen Strom kaum liefern. Für die meisten Mikroschaltungsserien sind Verstärker auf Transistorschaltern erforderlich.

Bestimmen wir nun den Strom, der durch den Schalter fließt, der die einzelnen Bits der 8-Bit-Anzeigeeinheit mit Strom versorgt. Wie aus dem Diagramm in Abbildung 2 ersichtlich ist, kann Strom von jedem Segment des Indikators durch den Schlüssel fließen. Wenn die Zahl 8 angezeigt wird, müssen alle sieben Segmente der Anzeige leuchten, was bedeutet, dass der in diesem Moment durch die Taste fließende Impulsstrom wie folgt bestimmt werden kann:

I cl = I segding× N-Segment= 24mA×7 = 168mA.

Wie gefällt dir diese Strömung?! In Amateurfunkschaltungen stoße ich oft auf Lösungen, bei denen der Schaltstrom direkt vom Ausgang des Decoders entnommen wird, der keinen Strom von mehr als 20 mA erzeugen kann, und ich stelle mir die Frage: Wo soll ich nach einem solchen Indikator suchen? In völliger Dunkelheit? Das Ergebnis ist ein „Nachtsichtgerät“, also ein Gerät, dessen Messwerte nur bei völliger Dunkelheit sichtbar sind.

Schauen wir uns nun das schematische Diagramm der resultierenden Anzeigeeinheit an. Es ist in Abbildung 3 dargestellt.



Abbildung 3. Schematische Darstellung der dynamischen Anzeigeeinheit

Nachdem wir nun die dynamische Anzeigeschaltung erhalten haben, können wir ihre Vor- und Nachteile besprechen. Der unbestrittene Vorteil der dynamischen Anzeige ist die geringe Anzahl von Anschlussdrähten, die sie in manchen Fällen unverzichtbar macht, beispielsweise beim Arbeiten mit Matrixanzeigen.

Der Nachteil ist das Vorhandensein großer Impulsströme, und da jeder Leiter eine Antenne ist, dient die dynamische Anzeige als starke Störquelle. Eine weitere Störquelle ist die Stromversorgung.

Bitte beachten Sie, dass die Vorderflanken der Schaltimpulse sehr kurz sind, sodass ihre harmonischen Komponenten den Hochfrequenzbereich bis hin zu Ultrakurzwellen abdecken.

Die Verwendung der dynamischen Anzeige ermöglicht es also, die Anzahl der Verbindungskabel zwischen dem digitalen Gerät und der Anzeige zu minimieren, stellt jedoch gleichzeitig eine starke Störquelle dar, sodass ihre Verwendung in Funkempfangsgeräten unerwünscht ist.

Wenn aus irgendeinem Grund, beispielsweise aufgrund der Notwendigkeit, Matrixindikatoren zu verwenden, eine dynamische Anzeige erforderlich ist, müssen alle Maßnahmen zur Unterdrückung von Störungen ergriffen werden.

Maßnahmen zur Unterdrückung von Störungen durch dynamische Anzeige umfassen die Abschirmung des Geräts, der Verbindungskabel und der Platinen. Verwendung einer Mindestlänge der Verbindungskabel und Verwendung von Stromversorgungsfiltern. Bei der Abschirmung eines Blocks kann es erforderlich sein, die Anzeigen selbst abzuschirmen. In diesem Fall wird üblicherweise ein Metallgewebe verwendet. Dieses Raster kann gleichzeitig den Kontrast der angezeigten Zeichen erhöhen.

Literatur:

Zusammen mit dem Artikel „Dynamische Anzeige“ lesen Sie:

Indikatoren sollen einer Person verschiedene Arten von Informationen anzeigen. Die einfachste Art von Informationen ist...
http://site/digital/Indic.php

Gasentladungsanzeiger werden sowohl zur Anzeige von Bitinformationen als auch zur Anzeige von Dezimalinformationen verwendet. Bei der Konstruktion von Dezimalindikatoren ist die Kathode...
http://site/digital/GazIndic/

Heutzutage werden LEDs fast überall zur Anzeige binärer Informationen eingesetzt. Das ist wegen...
http://site/digital/LED.php

Funktionsprinzipien von Flüssigkristallindikatoren... Funktionsweise von Flüssigkristallindikatoren... Entstehung eines Farbbildes...
http://site/digital/LCD.php

Die dynamische Anzeige ist eines der Probleme, mit denen unerfahrene Mikrocontroller-Programmierer konfrontiert sind. Es wurde schon viel darüber gesagt, aber ich habe beschlossen, das Bekannte mit Bildern und Beispielen von Quellcode in C zu untermauern, um die Beherrschung dieser Anzeigemethode zu erleichtern.

1 Grundlagen oder Einführung

Definieren wir zunächst die Terminologie, die wir im gesamten Artikel verwenden werden.

Wenn Sie eine Anzeige steuern müssen, die aus einer einzelnen Sieben-Segment-Vertrautheit besteht, stellt dies keine Probleme dar – man kann sie sich als 8 unabhängige LEDs vorstellen. Wenn Sie mehr Informationen als ein einzelnes Zeichen anzeigen müssen, beginnen Probleme: 2 bekannte Stellen bilden 16 LEDs, drei - 24 usw., das heißt, für eine dreistellige Anzeige sind möglicherweise einfach nicht genügend Mikrocontroller-Pins vorhanden, nicht zu erwähnen 6- oder mehrstellige Anzeigen und insbesondere Matrixanzeigen.

Der Einfachheit halber vereinbaren wir, dass alle unsere Indikatoren eine gemeinsame Kathode haben. Die Lösung des Problems ist ganz einfach: Verbinden Sie die Anschlüsse der Segmente aller Indikatoren miteinander. Wenn Sie nun Informationen an den ersten bekannten Ort ausgeben möchten, sollten Sie die erforderlichen Pegel auf die Segmentleitungen anwenden und den gemeinsamen Ausgang des ersten Indikators mit der gemeinsamen Leitung des Stromkreises verbinden. Natürlich müssen an den gemeinsamen Kathoden aller anderen Indikatoren hohe Werte vorhanden sein. Offensichtlich leuchten die erforderlichen Segmente der ersten Anzeige auf. Zur Ausgabe an die zweite, dritte usw. Indikatoren sollten das Gleiche tun, d. h. Durch Anlegen einer logischen Null an eine der gemeinsamen Kathoden wählen wir die aktuell angezeigte Ziffer aus und der Zustand der Segmentlinien bestimmt das sichtbare Symbol.

Damit die gesamte Anzeige als dauerhaft leuchtend wahrgenommen wird, sollten die Ziffern schnell umgeschaltet werden – mehr als 25 Mal pro Sekunde. Wie Sie sehen, ändern sich die Pegel aller Ausgänge (die übrigens deutlich kleiner geworden sind als beim üblichen Ansatz) kontinuierlich, d. h. haben keine statischen, sondern dynamische Ebenen, daher der Name der Anzeigemethode – dynamisch.

Dynamisches Anzeigebild

2 Arten der Hardware-Implementierung

2.1 Flache Matrizen

Wenn wir von Sieben-Segment-Indikatoren abstrahieren, kann unsere Anzeige als eine Matrix einzelner LEDs dargestellt werden, deren Anoden zu Zeilen der Matrix und deren Kathoden zu Spalten zusammengefasst sind. Eigentlich ist es genau so.

Indem wir die Zeilen und Spalten unserer Matrix mit den erforderlichen Pegeln versorgen, können wir natürlich jedes elementare LED-Segment (auch Pixel genannt – dies ist ein traditionellerer Begriff in Bezug auf Matrixanzeigen) zum Leuchten bringen. Abhängig davon, wie genau wir die Ebenen in Zeilen und Spalten ändern, können wir verschiedene Arten der dynamischen Anzeige erhalten:

  • nach Zeilen;
  • nach Spalten;
  • Segment für Segment (pro Pixel);
  • auf gemischte Weise.

Wir haben uns die Spaltenoption im vorherigen Kapitel angesehen. Die Zeilenoption unterscheidet sich davon nur dadurch, dass die Zeilen und Spalten unserer Matrix vertauscht sind. Die Segment-für-Segment-Methode bedeutet, dass zu jedem Zeitpunkt nur eine Zeile und eine Spalte den zum Leuchten der LED erforderlichen Pegel enthält. Das heißt, zu einem bestimmten Zeitpunkt kann nur eine LED der gesamten Matrix aufleuchten (im Gegensatz zu den vorherigen Optionen, bei denen die gesamte Zeile oder gesamte Spalte gleichzeitig aufleuchten kann). Diese Methode erinnert an das Scannen eines Fernsehers, bei dem der Strahl um den gesamten Bildschirm läuft und den Leuchtstoff an den richtigen Stellen beleuchtet. Die gemischte Option besteht, wie der Name schon sagt, darin, dass in mehreren Zeilen und Spalten gleichzeitig „aktive“ Ebenen vorhanden sein können.

Die ersten beiden Optionen sind sehr einfach zu implementieren und werden daher häufig verwendet. Die dritte Option wird seltener verwendet, weil erfordert höhere Informationsaktualisierungsraten in Zeilen und Spalten, und der durchschnittliche Strom durch das Segment (d. h. die Segmenthelligkeit) ist in diesem Fall deutlich niedriger als in anderen. Die letzte gemischte Methode ist die am wenigsten verbreitete, obwohl sie eine Reihe positiver Eigenschaften aufweist. Diese Methode setzt zunächst voraus, dass in den Zeilen- und Spaltenkreisen stabile Stromquellen verwendet werden, da sonst die Helligkeit der Leuchtsegmente zwangsläufig von deren Gesamtzahl abhängt. Und die Berechnung von Signalkombinationen in Zeilen und Spalten ist nicht ganz einfach.

2.2 Mehrdimensionale Matrizen

Die von uns betrachteten Beispiele gehen von der Implementierung einer monochromen Anzeige aus, d.h. bestehend aus einfarbigen LEDs. Was tun, wenn Sie eine mehrfarbige Anzeige beispielsweise mit RGB-LEDs erhalten möchten? Es gibt zwei mögliche Lösungen.

Die erste besteht darin, einfach die Anzahl der Zeilen (oder Spalten) unserer Matrix zu erhöhen und jede RGB-LED als drei unabhängige einzelne LEDs zu behandeln. Der große Nachteil dieses Ansatzes ist die Verdreifachung der Anzahl der Zeilen (oder Spalten). Was das in der Praxis bedeutet, zeigt ein einfaches Beispiel: Mit zwei 8-Bit-Mikrocontroller-Mikrocontrollern können wir eine monochrome 8x8-Segmentmatrix oder eine 4x4-Farbmatrix erhalten. Stimmen Sie zu, dass es im zweiten Fall praktisch unmöglich ist, etwas Verständliches darzustellen ...

Der zweite Weg besteht darin, von einer „flachen“ Segmentmatrix zu einer „mehrdimensionalen“ Matrix überzugehen. Wenn das Signal jeder Leitung durch einen 1x3-Multiplexer geleitet wird, können wir uns das Anzeigesystem der RGB-LEDs als 3 unabhängige Matrizen der ursprünglichen Dimension vorstellen: Jede Matrix besteht aus LEDs derselben Farbe, und wir wählen die gewünschte Matrix mithilfe eines Multiplexers aus Steuersignale. Das Bild erklärt, was gesagt wurde.

Natürlich ist im Falle einer mehrdimensionalen Matrix auch eine zusätzliche Anzahl von Steuerleitungen erforderlich, diese ist jedoch nicht so groß: Auf denselben beiden Controller-Ports können wir ein 7x7-Farbdisplay erhalten!!!

2.3 Möglichkeiten, die Dimension von Matrizen zu reduzieren

Wenn die Anzahl der Mikrocontroller-Pins sehr begrenzt ist, müssen wir nach Möglichkeiten suchen, die Anzahl der Zeilen und Spalten unserer Matrix zu reduzieren. Natürlich geschehen keine Wunder, und in diesem Fall müssen wir dafür bezahlen, dass wir zusätzlich zum Mikrocontroller zusätzliche Mikroschaltungen verwenden. Wie Sie vielleicht schon erraten haben, können Sie hier die zuvor besprochene Methode der „mehrdimensionalen“ Matrizen verwenden – schließlich wird uns niemand verbieten, anstelle von RGB-LEDs einfach die dreifache Anzahl einfarbiger LEDs zu verwenden? Die Hauptsache ist, sie angemessen anzuordnen...

Wir können also die Dimension der Matrix reduzieren, indem wir Folgendes verwenden:

  • Decoder oder Multiplexer;
  • Schieberegister.

Multiplexer haben wir schon früher kennengelernt, Decoder Wie Sie sich vorstellen können, unterscheidet es sich nicht grundlegend von einem Multiplexer. Es sollte nur hinzugefügt werden, dass es durch die Verwendung von Decodern/Multiplexern sowohl für Zeilen als auch für Spalten möglich ist, die Matrixdimension entlang beider Dimensionen gleichzeitig zu reduzieren, aber in diesem Fall kann es notwendig sein, nur eine segmentweise dynamische Anzeige zu verwenden. mit all seinen Nachteilen.

Schieberegister können viel besser helfen als Entschlüsseler. Betrachten Sie das Diagramm in der Abbildung unten.

Es ist leicht zu erkennen, dass eine beliebige Anzahl von Zeilen und Spalten nur eine Erhöhung der Anzahl der Register erfordert und die Anzahl der beteiligten Steuerleitungen des Mikrocontrollers gleich bleibt! Ein kleiner Nachteil dieses Ansatzes besteht darin, dass mit zunehmender Anzahl der Register in der Kette die Geschwindigkeit der sequentiellen Ausgabe von Informationen in diese erhöht werden muss, was nicht immer einfach zu erreichen ist. Zum Beispiel gängige Mikrocontroller der Familie AVR ermöglichen es Ihnen praktisch nicht, die serielle Ausgabegeschwindigkeit von 10 Megabit/Sek. zu überschreiten. Wenn Sie andererseits andere Controller verwenden, die Signale schneller ausgeben können, können Probleme anderer Art auftreten: die Ausbreitung eines hochfrequenten Taktsignals entlang einer langen Leitung (und bei einer großen Anzahl von Registern wird es zwangsläufig ein einziges sein). ) tritt völlig anders auf als ein niederfrequentes, daher sind besondere Maßnahmen beim Layout einer Leiterplatte und andere Dinge erforderlich, auf die wir in diesem Artikel nicht eingehen.

3 Methoden zur Softwareimplementierung

Wir werden nicht auf die Software-Implementierung aller genannten dynamischen Anzeigeoptionen eingehen – dies würde den Artikel unangemessen aufblähen. Wir beschränken uns auf nur drei der beliebtesten Beispiele: eine flache Matrix mit direkter Steuerung von Zeilen und Spalten, dasselbe mit der Verwendung eines Decoders und schließlich eine Variante mit Schieberegistern. In allen Fällen wird besonderes Augenmerk auf alle Nuancen der Softwareimplementierung gelegt, d. h. dem C-Code werden nur dann Erläuterungen beigefügt, wenn dies mit der Absicht des Autors und überhaupt nicht mit Ihrem Ausbildungsstand übereinstimmt . Damit weise ich darauf hin, dass Sie die Grundlagen von C ohne mich kennen sollten.

Für alle Beispiele stimmen wir zu, dass unsere Anzeige auf Sieben-Segment-Anzeigen mit einer gemeinsamen Kathode basiert.

3.1 Der einfachste Weg

Offensichtlich wäre es im Programm am bequemsten, über ein bestimmtes Array zu verfügen, dessen Inhalt eindeutig bestimmen würde, welche Segmente in welchen bekannten Bereichen der Anzeige beleuchtet sind – eine Art Analogon zum Bildschirm-RAM.

Lassen Sie uns die Definition der folgenden Konstanten einführen:

#define SCR_SZ 6 /* Anzahl der Anzeigebekanntmachungen */ #define ROWS PORTB /* „Zeilen“-Port anzeigen, d. h. Segmentverwaltung */ #define COLS PORTD /* „Spalten“-Verwaltungsport, d. h. gemeinsame Kathoden */

Lassen Sie uns nun das Bildschirmarray deklarieren:

Unsigned char SCR;

Zunächst gehen wir davon aus, dass jedes Element des Arrays der Vertrautheit der Anzeige entspricht und jedes Bit dieses Elements einem bestimmten Segment des Indikators entspricht. Welches Bit welchem ​​Segment entspricht – in diesem Fall spielt es keine Rolle, genauso wie es egal ist, wie diese Bits in den Bytes unseres Arrays gesetzt sind, wir gehen zunächst einfach davon aus, dass sie bereits vorhanden sind. Der Einfachheit halber gehen wir auch davon aus, dass die gemeinsamen Kathoden mit den Port-Pins verbunden sind COLS nacheinander: Das niedrigstwertige Bit ist der Indikator ganz rechts, dann das zweite, dann das dritte usw.

Wie lässt sich dieses Array auf dem Display „anzeigen“? Schreiben wir den folgenden Code:

< SCR_SZ; pos++){ ROWS = SCR; COLS = ~(1 << pos); }

Wird es die erforderliche Funktion erfüllen? Ja. Aber es ist nicht gut.

Bitte beachten Sie zunächst, dass wir keinen Einfluss darauf haben, wie schnell die Inhalte von Zeilen und Spalten aktualisiert werden. Beachten Sie zweitens, dass bis zum Zeitpunkt des Druckens das neue Array-Element gedruckt wird REIHEN auf den Linien COLS Die alte Bedeutung ist immer noch da! Wohin führt es? Darüber hinaus zeigt der Vertrautheitsbereich für den Bruchteil einer Sekunde Ausschnitte des benachbarten Vertrautheitsbereichs an, d. h. Einige Segmente werden falsch beleuchtet.

Sie können diesen Effekt vermeiden, indem Sie Folgendes tun: bevor Sie den Inhalt aktualisieren REIHEN, lösche immer den vertrauten Ort aus, der der vorherige war. Um sich nicht die Mühe zu machen, die bisherige Vertrautheit festzustellen, können Sie alles auf einmal löschen. Unser Code hat also die folgende Form:

Unsigned char pos; while(1) for(pos = 0; pos< SCR_SZ; pos++){ COLS = 0xFF; ROWS = SCR; COLS = ~(1 << pos); delay(); }

Wir haben die Austastung der gesamten Anzeige vor der Aktualisierung des Zustands der Segmentlinien hinzugefügt (indem wir die gemeinsamen Kathoden hoch schicken, schalten wir den Indikator aus, unabhängig davon, was an den Anoden vorhanden ist) und haben eine Verzögerung am Ende des Zyklus eingeführt. Jetzt funktioniert die Anzeige viel besser. Aber haben wir ein gutes Programm geschrieben? Leider nein.

Tatsache ist, dass der endlose Anzeigezyklus während Es lässt uns einfach nichts anderes tun. Was für ein Programm werden wir haben, das nur etwas auf dem Indikator anzeigen kann?! Natürlich ist nicht alles 100 % schlecht, denn mit Interrupts kann man etwas Sinnvolles machen... und nicht mit Verzögerungen Verzögerung() Sie können einige Aktionen ausführen ... Aber das alles ist sehr, sehr schief: Es ist nicht ratsam, in Interrupt-Handlern etwas Komplexes und Umständliches auszuführen; Wenn Sie jedoch anstelle einer Verzögerung etwas Komplexes und Umständliches tun, ist es schwierig, die gleiche Rechenzeit sicherzustellen, da sonst die bekannten Orte für unterschiedliche Zeiträume leuchten, was optisch wie ihre eigenen aussieht Leuchten oder Flackern unterschiedlicher Helligkeit.

Im Allgemeinen kann diese Option nur in Ausnahmefällen, nur als Lehrbeispiel oder in dem Fall (aber wiederum nur in Ausnahmefällen!) zugelassen werden, wenn das zu lösende Hauptproblem sehr einfach ist (dies könnte z. B. der Fall sein). , das Problem der Messung mit ADC Spannung messen und auf dem Display anzeigen).

Was sollte man tun? Die Antwort ist wie immer einfach: Alle Prozesse, die unbemerkt von der Lösung des Hauptproblems durchgeführt werden müssen (und natürlich ist ein solcher Prozess ein Hinweis darauf), sollten mithilfe von Timer-Interrupts ausgeführt werden.
Die Unterbrechungen erfolgen in genau definierten Abständen und sorgen so für eine gleichmäßige Ausleuchtung der bekannten Schilder. Die Hintergrundanzeige ermöglicht es uns, einfach zum richtigen Zeitpunkt in der Hauptschleife etwas in das Array zu schreiben SCR- und es erscheint sofort auf dem Display! Und alle Änderungen am Code laufen darauf hinaus, dass wir anstelle von Schleifen eine Interrupt-Handler-Funktion verwenden:

ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; COLS = 0xFF; ROWS = SCR; COLS = ~(1<< pos); if(++pos == SCR_SZ) pos = 0; }

Ein paar Kommentare.

Variable Pos, die die Nummer des aktuellen beleuchteten Schildes angibt, machen wir daraus eine lokale statische Variable, damit sie ihren Wert von Interrupt zu Interrupt behält. Am Ende der Funktion erhöhen wir selbstständig (schließlich haben wir keine Schleife mehr) die Nummer der bekannten Stelle, bis wir das Limit erreichen – in diesem Fall gehen wir wieder an den Anfang.

Im Hauptprogramm müssen wir nur die Ports und den Timer initialisieren (in diesem Fall - Timer 0), sodass es in den von uns benötigten Abständen überläuft und Unterbrechungen zulässt. Danach müssen Sie nicht mehr über die Indikation nachdenken – es wird ruhig und friedlich von alleine funktionieren. Doch wie ermittelt man das gewünschte Timer-Überlaufintervall? Sehr einfach. Das menschliche Auge nimmt Flackern mit einer Frequenz von mehr als 25 Hz als kontinuierliches Leuchten wahr. Wir haben 6 Indikatoren, jeder von ihnen sollte mit dieser Frequenz flackern, was bedeutet, dass die Informationen auf dem Display mit einer Frequenz von 25 x 6 = 150 Hz oder mehr aktualisiert werden sollten. Berechnen wir nun den Wert des Timer-Vorteilers: Teilen Sie die Taktfrequenz des MK durch 256 ( Timer 0 jeder hat AVR acht Bit, was bedeutet, dass es nach dem Zählen bis 256 überläuft) – dies ist der gewünschte Wert des Timer-Vorteilers. Natürlich ist es unwahrscheinlich, dass das Ergebnis mit einem der Standardwerte des Vorteilers übereinstimmt – das ist kein Problem, Sie können den nächstkleineren geeigneten Wert nehmen. Die Anzeige arbeitet mit einer höheren Frequenz, die Qualität wird dadurch jedoch nicht beeinträchtigt! Ein Nebeneffekt wird eine große Belastung des MK-Kerns für die Anzeige sein. Sollte dies das Hauptprogramm stark beeinträchtigen, müssen Sie die Anzeige auf einen anderen Timer, beispielsweise einen 16-Bit, umstellen Timer 1, oder geben Sie einen Zähler für übersprungene Timer-Überläufe ein:

#define SKIP 15 /* Anzahl der zu überspringenden Timer-Interrupts */ ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; static unsigned char skip = SKIP; if (--skip) return; skip = SKIP; COLS = 0xFF; ROWS = SCR ; COLS = ~(1<< pos); if(++pos == SCR_SZ) pos = 0; }

In diesen vereinfachten Beispielen gehen wir davon aus, dass es sich um den Hafen handelt COLS Außer den gemeinsamen Kathoden der Indikatoren ist nichts anderes angeschlossen. Im wirklichen Leben kommt ein solches Glück jedoch nicht oft vor, und höchstwahrscheinlich hängt etwas anderes mit den verbleibenden Leitungen dieses Hafens zusammen. Daher sollte bei der Organisation einer dynamischen Anzeige stets darauf geachtet werden, dass der Zustand aller Portleitungen, die nicht direkt an der Anzeige beteiligt sind, unverändert bleibt. Das geht ganz einfach: Anstatt einfach einen neuen Wert auf den Port zu schreiben, sollten Sie unnötige Bits maskieren:

COLS |= 0x3F; // also löschen wir alle bekannten Orte COLS &= ~(1<

Beide Operatoren ändern den Wert der höchstwertigen Bits des Ports nicht COLS.

3.2 Methode mit Decoder

Der Decoder kann für beide Konvertierungen verwendet werden VERHEXEN oder BCD Code in Sieben-Segment-Symbole umwandeln oder eine der Matrixspalten auswählen. Beide Optionen unterscheiden sich von der zuvor besprochenen einfachsten Methode nur darin, wie die Ausgabe an die Ports organisiert wird REIHEN und/oder COLS, an den die Decoder-Eingänge angeschlossen werden.
Eine Option zur Verwendung eines Decoders zum Erhalten eines Sieben-Segment-Symbols:

ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; COLS |= 0x3F; ROWS = (ROWS & 0xF0) | (SCR & 0x0F); COLS &= ~(1<< pos); if(++pos == SCR_SZ) pos = 0; }

Wie Sie sehen, sind die Änderungen minimal – vor der Anzeige REIHEN Zeichencode aus dem Array SCR, werden die höchstwertigen Bits maskiert, danach werden die niedrigstwertigen Bits entsprechend dem Zeichencode gesetzt. Das heißt, wir gehen davon aus, dass der Decoder mit den 4 niedrigstwertigen Bits des Ports verbunden ist REIHEN.

Ich hoffe, es hat keinen Sinn, ein Beispiel für die Dekodierung von Spalten zu nennen – alles ist bereits klar.

3.3 Registrierungsmethode

Obwohl sich die dynamische Anzeige mithilfe von Schieberegistern nicht grundsätzlich von den zuvor diskutierten Methoden unterscheidet, gibt es mehrere Möglichkeiten für ihre Implementierung. Wir betrachten die einfachste Variante – die Ausgabe von Bits rein per Software. Und bei der Umsetzung anderer (mit USI/USART/SPI/TWI) können Sie es selbst ausprobieren.

Für die Variante der zuvor gewählten Darstellung von 6- und 7-Segment-bekannten Orten verwenden wir 2 Schieberegister des Typs 74HC595. Dieses Register wird durch drei Signale gesteuert: serielle Dateneingangstaktimpulse CLK, die Daten selbst DATEN und ein Impuls zur gleichzeitigen parallelen Ausgabe der in das Register geschriebenen Informationen SATZ. Lassen Sie uns die entsprechenden Makros deklarieren (der Einfachheit halber senden wir alle Signale an einen Port):

#define CLK _BV(PB0) #define DATA _BV(PB1) #define SET _BV(PB2) #define REG_PORT PORTB

Um in das Register zu schreiben, ist es praktisch, eine separate Funktion zu schreiben:

Statische Lückenverschiebung(unsigned char d)( unsigned char i; for (i=0; i< 8; i++){ // устанавливаем нужный уровень DATA if(d & 1) REG_PORT |= DATA; else REG_PORT &= ~DATA; REG_PORT |= CLK; // даем импульс CLK REG_PORT &= ~CLK; d >>= 1; } }

Es wird dringend empfohlen, diese Funktion statisch zu machen, weil Es wird im Interrupt-Handler verwendet. Der Compiler wird höchstwahrscheinlich statische Funktionen im Formular erstellen im Einklang-Einfügungen in den Interrupt-Handler, d.h. Es erfolgt keine unnötige Stapelnutzung, was für nicht statische Funktionen nicht garantiert ist.

Jetzt sieht unser Interrupt-Handler so aus:

ISR(TIMER0_OVF_vect)( static unsigned char pos = 0;shift(SCR);shift(~(1<< pos)); REG_PORT |= SET; // выдаем импульс SET REG_PORT &= ~SET; if(++pos == SCR_SZ) pos = 0; }

Da die in die Register geschriebenen Daten gleichzeitig an ihren Ausgängen erscheinen, ist es nicht erforderlich, zuerst die Anzeigen zu löschen. Es sollte beachtet werden, dass die sequentielle Softwareausgabe ein ziemlich langer Prozess ist, insbesondere bei Matrizen mit großen Abmessungen, daher sollte sie so weit wie möglich auf Geschwindigkeit optimiert werden. Dies lässt sich am besten mit der seriellen Ausgabehardware der MCU bewerkstelligen.

4 Für diejenigen, die nie genug haben

Sie haben sich also mit den Grundlagen der Implementierung einer dynamischen Anzeige vertraut gemacht. Aber wie üblich nehmen die Fragen nicht ab, sondern zu. Da ich einige davon vorwegnehme, werde ich versuchen, sofort die notwendigen Antworten zu geben.

4.1 Anoden, Kathoden – was soll man wählen?

Alles, was wir zuvor betrachtet haben, bezog sich auf Indikatoren mit gemeinsamen Kathoden. Was ist, wenn Sie es mit herkömmlichen Anoden verwenden möchten? Im Allgemeinen bleibt alles beim Alten, außer dass vor der Ausgabe eine Invertierung der Daten erforderlich ist. Das Löschen der Vertrautheit erfolgt durch die Ausgabe von Nullen COLS, Zündung - bzw. Einheiten und Segmente in REIHEN werden mit Nullen statt Einsen eingefügt. Der Interrupt-Handler würde also etwa so aussehen:

ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; COLS &= 0xC0; ROWS = ~SCR; COLS |= (1<< pos); if(++pos == SCR_SZ) pos = 0; }

Es ist einfach. Es sei denn natürlich, Sie versuchen, einen universellen Code zu schreiben, der sowohl für gemeinsame Anoden als auch für gemeinsame Kathoden geeignet ist. Dafür gibt es zwei Möglichkeiten: entweder die Verwendung von Anweisungen zur bedingten Kompilierung oder die Verwendung einer Konvertierungsfunktion. Ich werde die erste Option demonstrieren und Ihnen vorschlagen, selbst über die zweite nachzudenken.

#define COMMON_ANODE 1 ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; #if COMMON_ANODE != 1 COLS &= 0xC0; ROWS = ~SCR; COLS |= (1<< pos); #else COLS |= 0x3F; ROWS = SCR; COLS &= ~(1 << pos); #endif if(++pos == SCR_SZ) pos = 0; }

Obwohl dies etwas umständlich ist, können Sie es nach einmaligem Schreiben praktisch ohne Änderungen in allen Projekten verwenden.

4.2 Flimmern

In vielen Fällen dient das Display nicht nur zur Anzeige von Informationen aus dem Inneren des Geräts, sondern auch zur Anzeige von Benutzereingaben. Und in diesem Fall ist es notwendig, das Unveränderliche auf der Darstellung irgendwie vom Veränderbaren trennen zu können. Am einfachsten geht das, indem man den entsprechenden bekannten Ort (oder mehrere bekannte Orte) zum Flackern bringt.

Es ist sehr einfach zu machen. Lassen Sie uns eine globale Variable einführen, bei der jedes Einheitsbit ein blinkendes Symbol bezeichnet:

unsigned char blink = 0;

Lassen Sie uns nun den Interrupt-Handler leicht modifizieren:

ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; static unsigned char Eintrag = 0; COLS |= 0x3F; if(!(blink & (1<

Wie Sie sehen, wurde nur eine statische Variable hinzugefügt – der Zähler der Eingaben in den Interrupt-Handler Eintrag und einen Bedingungstestoperator. Die Logik ist einfach: Die Ausgabe der nächsten Vertrautheit erfolgt nur, wenn entweder im entsprechenden Bit blinken Null oder das höchstwertige Bit des Zählers Eintrag ist gleich 1. Wenn, angenommen, blinken enthält nur Nullen, dann ist diese Bedingung immer erfüllt – alle bekannten Orte werden angezeigt. Wenn blinken Wenn in einem seiner Bits eine 1 enthalten ist, wird das entsprechende Vorzeichen nur dann beleuchtet, wenn das höchstwertige Bit des Zählers gleich 1 ist. Da der Zähler bei jedem Aufruf des Interrupt-Handlers inkrementiert wird, leuchtet das entsprechende Vorzeichen Flackern mit einer Frequenz, die 128-mal kleiner als die Interrupt-Frequenz ist.

4.3 Anpassen der Helligkeit von Segmenten

Über die Helligkeitsanpassung habe ich in einem separaten Artikel geschrieben, der so genannt wurde.

4.4 Beliebige Verteilung der Pins

Es wurde zuvor gesagt, dass das Glück, einen ganzen MK-Port für die Anzeige zu reservieren, ziemlich selten ist. Aber es ist noch seltener, eine praktische Leiterbahn einer Leiterplatte zu erhalten, wenn ein Port ausschließlich für Zeilen und der andere Port für Spalten der Anzeigematrix verwendet wird. Viel häufiger wird eine optimale Ablaufverfolgung nur erreicht, wenn Zeilen und Spalten zwischen zwei oder sogar mehr Mikrocontroller-Ports gemischt werden. Sie müssen nicht auf die Schönheit der Leiterplatte verzichten, wenn Sie während der Anzeige eine Software-Neuanordnung der Bits vornehmen.

Schauen wir uns ein abstraktes Beispiel an. Die beste Verfolgung soll mit der folgenden Signalverteilung entlang der Leitungen der MK-Ports gewährleistet werden:

Segment A

Segment B

Segment H

Segment C

Segment D

Segment G

Segment E

Segment F

Wie Sie sehen können, werden die Matrixzeilen zwischen den drei Ports gemischt und alle nicht verwendeten Zeilen dieser Ports sollten während des Anzeigevorgangs natürlich ihren Pegel nicht ändern.

Für diesen Fall ist es am besten, mit der Entwicklung einer dynamischen Anzeigefunktion zu beginnen, indem Segmente auf Symbolbits verteilt werden. Bisher glaubten wir, dass das im Array der Fall ist SCR Wir speichern Bitmasken von Zeichen, d.h. Die Einsen im Byte geben leuchtende Segmente an. Wir haben nicht darüber nachgedacht, welches Bit welchem ​​Segment entspricht. Jetzt ist es also an der Zeit, darüber nachzudenken.

Es ist zweckmäßig, den Zweck der Hafenlinien in Form von drei Platten zu malen:

1

A

0

4

H

3

2

B

F

E

5

G

D

C

Wir müssen alle Segmente in einem Byte zusammenfassen. Dies muss im Schichtbetrieb erfolgen, daher sollten Sie versuchen, diese so zu verteilen, dass möglichst wenige Schichten stattfinden. Lass uns reden.

Wenn das Segment bits FEGDC Lassen Sie das Symbol so, dass sie hineinfallen PORTD ohne Verschiebungen, dann das Segment H kann auch im 6. Bit des Symbols verbleiben und muss vor der Ausgabe auch nicht verschoben werden PORTC, aber für Segmente A Und IN Die Bits 7 und 3 bleiben erhalten, also höchstwahrscheinlich das Segment IN muss vor der Ausgabe um 3 Positionen verschoben werden, und das Segment A- um 6. Ich werde mich auf diese Option konzentrieren, und Sie können weiter nach den minimalen Verschiebungen suchen (Verschiebungen um mehrere Positionen sind kein so schneller Vorgang, daher ist es ratsam, ihre Anzahl auf ein Minimum zu reduzieren).

In unserem Fall war die Verteilung der Bits über das Zeichenbyte also wie folgt:

A

H

F

E

B

G

D

C

Markieren wir die Bitmasken für die Ausgabe an die entsprechenden Ports:

D

0

0

1

1

0

1

1

1

0x37

B

1

0

0

0

0

0

0

0

0x80

C

0

1

0

0

1

0

0

0

0x48

Mithilfe dieser Masken verwenden wir die „bitweise UND“-Operation, um die erforderlichen Bits für die Ausgabe an den Port auszuwählen.

Lassen Sie uns Maskenkonstanten deklarieren:

#MASKD definieren 0x37 #MASKB 0x80 definieren #MASKC 0x48 definieren

Zuvor haben wir das Symbol an einen einzelnen Port ausgegeben REIHEN, nun wird dieses Verfahren in drei Teile unterteilt:

PORTD = (PORTD & ~MASKD) | (SCR & MASKD); PORTB = (PORTB & ~MASKB) | ((SCR & MASKB) >> 6); PORTC = (PORTC & ~MASKC) | ((SCR & _BV(6)) | (((SCR & _BV(3)) >> 3);

Bitte beachten Sie, dass für die Auszahlung an PORTC ein Bit muss ohne Verschiebung ausgegeben werden, das zweite mit Verschiebung, also statt MASKC Ich musste separate Makros verwenden _BV().

Jetzt muss noch entschieden werden, wie die entsprechenden vertrauten Orte gelöscht und beleuchtet werden. Lassen Sie uns Konstanten deklarieren, die den Vertrautheitskontrollbits entsprechen:

#define COM0 _BV(0) #define COM1 _BV(3) #define COM2 _BV(4) #define COM3 _BV(5) #define COM4 _BV(7) #define COM5 _BV(3) #define COM_D (COM5) #define COM_C (COM2 | COM3 | COM4) #define COM_B (COM0 | COM1)

Um alle Bekanntschaften zu löschen, müssen Sie die entsprechenden Konstanten an die Ports ausgeben COM_x:

PORTD |= COM_D; PORTC |= COM_C; PORTB |= COM_B;

Aber um die Vertrautheit einzuschalten, muss man knifflig sein (es macht keinen Sinn, an alle drei Ports auszugeben, da je nach Wert nur ein einziges Bit an einem bestimmten Port aktiv ist). Pos), zum Beispiel mit dem Operator schalten:

Switch(pos)( Fall 0: PORTB &= ~COM0; Pause; Fall 1: PORTB &= ~COM1; Pause; Fall 2: PORTC &= ~COM2; Pause; Fall 3: PORTC &= ~COM3; Pause; Fall 4: PORTC &= ~COM4; Pause; Fall 5: PORTD &= ~COM5; Pause; )

Es ist nicht die schönste Art, aber es funktioniert.

Somit hat unser Interrupt-Handler die folgende Form:

ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; static unsigned char enter = 0; // unterdrücken PORTD |= COM_D; PORTC |= COM_C; PORTB |= COM_B; // display PORTD = (PORTD & ~MASKD) | ( SCR & MASKD); PORTB = (PORTB & ~MASKB) | ((SCR & MASKB) >> 6); PORTC = (PORTC & ~MASKC) | ((SCR & _BV(6)) | (((SCR & _BV (3)) >> 3); // blinken if(!(blink & (1<< pos)) || (++entry & 0x80)) { switch(pos){ case 0: PORTB &= ~COM0; break; case 1: PORTB &= ~COM1; break; case 2: PORTC &= ~COM2; break; case 3: PORTC &= ~COM3; break; case 4: PORTC &= ~COM4; break; case 5: PORTD &= ~COM5; break; } } if(++pos == SCR_SZ) pos = 0; }

Jetzt muss noch herausgefunden werden, wie man die Symbole für die Ausgabe bequemer beschreiben kann ... Ich schlage vor, Folgendes zu tun: Konstanten zu definieren, die den Bits der Segmente entsprechen, und dann die erforderlichen Symbole aus diesen Konstanten zu „konstruieren“:

// Elementarsegmente #define _A _BV(7) #define _B _BV(3) #define _C _BV(0) #define _D _BV(1) #define _E _BV(4) #define _F _BV(5) #define _G _BV (2) #define _H _BV(6) // Ziffernsymbole #define d_0 (_A | _B | _C | _D | _E | _F) #define d_1 (_B | _C) #define d_2 (_A | _B | _G | _D | _E) // und so weiter

Wenn Sie also ganz rechts in der Anzeige eine Null anzeigen müssen, müssen Sie nur an der richtigen Stelle schreiben:

SCR = d_0;

Wenn Sie in einem anderen Projekt die Bits anders verteilen müssen, ändern Sie nur die Zahlen in den Makros _BV() für Elementarsegmente, und alle Symbole werden automatisch „neu erstellt“. Für die eingangs beschriebenen einfachsten Fälle müssen Sie gar nichts weiter tun, für die Option „Bit-Neuanordnung“ müssen Sie natürlich basteln.

4.5 Tastenunterstützung

Aufgrund des traditionellen Mangels an MK-Pins ist das Problem einer großen Anzahl von Tasten, auf die kaum ein Gerät verzichten kann, sehr akut. Es werden verschiedene Matrixeinschlüsse usw. verwendet. Wenn Sie jedoch einige Tricks anwenden, indem Sie die Funktion der dynamischen Anzeige etwas verkomplizieren, ist es einfach, so viele Tasten zur Verfügung zu haben, wie in der Anzeige vertraut sind, und Sie müssen in diesem Fall zusätzlich nur einen Mikrocontroller-Anschluss verwenden. Allerdings müssen Sie immer noch an jeder Taste eine Diode installieren.

Dies ist in der Abbildung schematisch dargestellt.

Und das Programm sieht so aus:

#define keypin() (!(PIND & _BV(KEY))) ISR(TIMER0_OVF_vect)( static unsigned char pos = 0; static unsigned char Eintrag = 0; static unsigned char tmp_key = 0; ROWS = 0; if(keypin( )) tmp_key |= 1<< pos; COLS |= 0x3F; if(!(blink & (1<< pos)) || (++entry &0x80)){ ROWS = (ROWS & 0xF0) | (SCR & 0x0F); COLS &= ~(1 << pos); } if(++pos == SCR_SZ){ pos = 0; key = tmp_key; tmp_key = 0; } }

Hier SCHLÜSSEL- Dies ist ein Makro, das das Bit des ausgewählten Ports setzt, mit dem alle Tasten „verbunden“ sind, Makro keypin() gibt den Logikwert TRUE zurück, wenn der ausgewählte Pin logisch niedrig ist. Im Beispiel sind die Tasten mit verbunden PORTD.

Bei jedem Timer-Interrupt werden zunächst alle Segmente gelöscht – dies ist notwendig, damit der Strom durch die LEDs nicht dazu führt, dass die gedrückte Taste fälschlicherweise nicht erkannt wird. Danach wird der Tastereingang abgefragt – wenn der Pegel niedrig ist, bedeutet dies, dass die Taste gedrückt wird, die mit der entsprechenden Pos-Kathode verbunden ist. In variabel tmp_key Schaltflächenzustände werden akkumuliert und in eine globale Variable umgeschrieben Schlüssel nach Abschluss des Anzeigezyklus. Sie müssen lediglich die Bedeutung von Zeit zu Zeit analysieren Schlüssel und erkannte Klicks verarbeiten:

Statisches unsigned char get_key())( unsigned char tmp = 0; tmp = key; _delay_ms(10); if(key == tmp) return tmp; else return 0; )

Diese einfache Funktion stellt sicher, dass die Tasten nicht abspringen, obwohl aufgrund der „dynamischen“ Natur der Tastenabfrage die Wahrscheinlichkeit eines Abspringens bereits gering ist.

5 Was sonst?

Sie beherrschen also ganz typische Techniken zur Implementierung einer dynamischen Anzeige. Ich denke, das wird für Sie zum ersten Mal und vielleicht sogar für den Rest Ihres Lebens ausreichen. Am Ende geht es vor allem darum, die Techniken und Algorithmen zu verstehen, Feinheiten und Nuancen kann man jederzeit selbst hinzufügen. Doch was erwartet Sie „in der Nähe“ der dynamischen Darstellung noch?

Wie ich bereits sagte, können Sie sogar bis zur unabhängigen Regulierung jedes Segments hinzufügen.

Sie können über die Optimalität des Interrupt-Handlers nachdenken – zu Bildungszwecken habe ich beispielsweise eher groben Code geschrieben, den ich überall verwendet habe SCR, obwohl es optimaler wäre, den Wert einmal in eine lokale Variable einzulesen und dann mit ihrem Wert zu arbeiten. Obwohl der Optimierer bei meinem Ansatz sicherlich hilfreich sein wird, lohnt es sich zu Übungszwecken, ihn selbst auszuprobieren und zu optimieren, indem er sich selbst im Hinblick auf die Größe des resultierenden Codes und/oder die Geschwindigkeit des Programms überwacht.

Sie können über die interessante Idee nachdenken, die Helligkeit des Displays automatisch an das Umgebungslicht anzupassen. Wie Sie wissen, sind LED-Anzeigen umso weniger sichtbar, je dunkler es ist – sie verschwimmen einfach. Daher ist es sinnvoll, die Helligkeit der Anzeigen im Dunkeln zu verringern und bei Tageslicht zu erhöhen. Am einfachsten ist es, einen separaten Fotowiderstand oder eine LED als Lichtsensor zu verwenden, aber Sie können es auch anders machen: Es ist bekannt, dass eine LED auch als Fotodiode arbeiten kann, wenn Sie also den mit dem Eingang verbundenen Port zur Anzeige verwenden ADC Anschließend können Sie bei Bedarf die Foto-EMK des nicht leuchtenden Segments des Indikators messen und diesen Wert verwenden, um die Helligkeit anzupassen ...

Sie können über die Verwendung serieller Ausgabehardware nachdenken, die ich bereits angedeutet habe.

Eine interessante Version eines völlig universellen Ansatzes zur dynamischen Anzeige, mit der ich auch empfehle, sich mit ihr vertraut zu machen, wurde von vorgeschlagen MOLCHEC. Kurz gesagt, das Wesentliche: Die Verteilung der Segmente nach Symbolbits, die Zuweisung von Ports zur Steuerung des Indikators und sogar die Art des Indikators – kurz gesagt, alle, alle, alle Parameter – werden in Form einer Konfigurationstabelle in angegeben EEPROM. Basierend auf dieser Tabelle wird alles programmgesteuert organisiert: von der Invertierung je nach Indikatortyp bis hin zur Neuanordnung von Bits an verschiedenen Ports. In diesem Fall bleibt der Quellcode des dynamischen Anzeigeprogramms stets unverändert und die Konfigurationstabelle wird vom Endbenutzer nach seinen Vorlieben zusammengestellt. Die Methode ist zwar universell und flexibel, geht jedoch mit einem erhöhten Programmspeicherverbrauch einher.


3 Geschrieben von ARV, um 06:48 25.08.2010
Mischa, wenn ich du wäre, würde ich nicht solche kategorischen Aussagen machen „das kannst du nicht“, „niemand hat es geschrieben“ oder „Urheberrecht“, denn erstens ist es unhöflich und zweitens:
1. Ich habe vor langer Zeit eine schleichende Linie auf einer 10x16-Matrix erstellt (das war es) – ein Video ihrer Funktionsweise finden Sie in dieser Notiz: http://site/content/view/160/38/
2. Ich habe einen Artikel geschrieben (Sie finden ihn in den Nachrichten – der letzte für heute) darüber, wie man einen Ticker auf einem LCD erstellt. Wenn Sie Ihr Gehirn ein wenig anstrengen, ist es eine Kleinigkeit, den Algorithmus für die Ausgabe in eine Matrix zu überarbeiten.
3. Auf meiner Website gibt es keinen einzigen kopierten Artikel (Kopieren und Einfügen ist nicht urheberrechtlich geschützt, Sie haben einen Tippfehler gemacht), alle Materialien sind vollständig original. Viele Websites verfügen über Kopien dieser Materialien mit meiner Erlaubnis (oder der Erlaubnis der Autoren der Materialien, die jedes Recht haben, ihre Materialien an vielen Orten gleichzeitig zu veröffentlichen).

Nur registrierte Benutzer können Kommentare hinterlassen.
Bitte registrieren Sie sich oder melden Sie sich bei Ihrem Konto an.

DUm eine mehrstellige Zahl auf dem Indikator anzuzeigen, müssen Sie zunächst eine knifflige Manipulation damit durchführen, die darin besteht, die Zahl in ihre Bestandteile zu zerlegen. Als Beispiel nenne ich die Anzeige der Zahl 1234 auf einem Quad-Sieben-Segment-Indikator mit gemeinsamer Anode.


Um eine vierstellige Zahl anzuzeigen, müssen Sie eine gemeinsame Variable erstellen, in der die Zahl liegt, die Sie anzeigen möchten (Variable). W), vier Variablen, in denen Daten für jedes Zeichen gespeichert werden (N) und vier weitere Variablen für Zwischentransformationen (M), um die Hauptvariable nicht zu berühren. Die Variable muss dem Wert entsprechen, der darin gespeichert wirdICH. Also für die VariableWTyp wird ausreichenganze Zahl , da eine Variable dieses Typs speichern kannÄndern Sie die Werte von -32768 auf +32767 (bzwWort es sei denn, Sie planen, negative Zahlen zu verwenden). In VariablenNEs wird Zahlen von 0 bis 9 geben, also verwenden Sie eine Variable wieByte
. Und in VariablenM wird seindie gleichen Werte wie in der VariablenW, also legen wir den Typ fest ganze Zahl .

Dim W als Ganzzahl
Dimmen Sie N1 als Byte
Dimmen Sie N2 als Byte
Dimmen Sie N3 als Byte
Dimmen Sie N4 als Byte
Dimmen Sie M1 als Ganzzahl
Dimmen Sie M2 als Ganzzahl
Dimmen Sie M3 als Ganzzahl
Dimmen Sie M4 als Ganzzahl


Nach der Deklaration der Variablen konfigurieren wir die Ausgabeportsdie zum Anschluss des Indikators verwendet wird:

DDRC = &B11111111
DDRD = &B11111111


DDRC =&B 00001111 und DDRD =&B 01111111 (vier erste Etappen von Hafen Cfür Anoden und die ersten sechs Anschlüsse D für Segmente).

Dann weisen wir der Variablen zu W der Wert, den wir auf dem Indikator anzeigen werden:

W=1234

„Arial“, „sans-serif““> In der Hauptschleife des Programms weisen wir den Variablen M den Wert der Variablen zuW, ich mache das:

M1 = W
M2 = M1
M3 = M1
M4 = M1


„Arial“, „sans-serif““> Dies ist keine Paranoia)), dies geschieht mit dem Ziel, dass alle Variablen M die gleiche Nummer enthalten, da während der Zuweisungsoperation leicht ein Interrupt einbrechen kann (falls vorhanden und nicht deaktiviert), in dessen Handler die VariableW kann wechseln. Und wenn die Aufgabe so wäre: M1= W , M 2= W , M 3= W , M 4= W Die Variablen M enthalten unterschiedliche Werte, was zu einem Durcheinander bei den Messwerten führt.

Nachdem wir den Variablen Werte zugewiesen haben, beginnen wir mit der Arbeit
Jeder von ihnen verwandelt sich so in eine Variable N Schlagen Sie den Wert, der sein wird
auf dem Indikator angezeigt: in einer Variablen
N 1 sollte „1“ sein N 2 – „2“, in N 3 – „3“ und in N 4 – „4“.

M1 = M1 / ​​​​1000 " M1 = 1234 / 1000 = 1,234
N1 = Abs (m1) " N1 = Abs (1,234) = 1

Abs – eine Funktion, die eine Ganzzahl an eine Variable zurückgibt. An eine Variable N 1 Treffer, genau das war erforderlich.

Einer Variablen eine Zwei zuweisen N Operation 2 wird etwas komplizierter sein:

M2= M2 Mod 1000 " M2 =1234 Mod 1000 = 234
M2 = M2 / 100 " M2 = 234 / 100 = 2,34
N2= Abs (m2) " N2 = Abs (2,34) = 2

„Arial“, „sans-serif““> Zunächst die FunktionMod Wir geben die ersten drei an die Variable zurück
Ziffern der Zahl (der Rest der Division durch 1000), und dann ist alles wie im ersten Fall.

Mit den letzten beiden Ziffern verhält es sich fast genauso:

M3 = M3 Mod100
M3 = M3 / 10
N3 = Abs (m3)

M4 = M4 Mod 10
N4= Bauchmuskeln (m4)


Jetzt enthalten unsere Variablen die Werte, die wir anzeigen möchten. Es ist Zeit für den Mikrocontroller, loszulegen und diese Werte auf dem Indikator anzuzeigen. Dazu rufen wir die Unterroutine für die Anzeigeverarbeitung auf:

„Arial“, „sans-serif““>

Gosub geführt

„Arial“, „sans-serif““> Der Prozessor springt zum Unterprogramm mit der BezeichnungLED:

LED:

Portc = &B00001000

„Arial“, „sans-serif““> Hier servieren wir ein hohes Maß anPORTC .3, wir haben die Anode der ersten Kategorie mit diesem Bein verbunden. Dann wählen wir aus, welche Segmente beleuchtet werden müssen, um den Wert der ersten Variablen anzuzeigen. Sie ist eins für uns, also wird null auf den Beinen sein Portd .1 und Portd .2, was den Segmenten entspricht B- und C-Anzeige.

Wählen Sie Fall N1









Endauswahl
Warten 5

„Arial“, „sans-serif““> Nachdem die erforderlichen Segmente aufleuchten, warten Sie 5 ms und fahren Sie mit der Anzeige der folgenden Zahlen fort:

Portc = &B00000100
Wählen Sie Fall N2
Fall 0: Portd = &B11000000
Fall 1: Portd = &B11111001
Fall 2: Portd = &B10100100
Fall 3: Portd = &B10110000
Fall 4: Portd = &B10011001
Fall 5: Portd = &B10010010
Fall 6: Portd = &B10000010
Fall 7: Portd = &B11111000
Fall 8: Portd = &B10000000
Fall 9: Portd = &B10010000
Endauswahl

Warten 5

Portc = &B00000010

Wählen Sie Fall N3
Fall 0: Portd = &B11000000
Fall 1: Portd = &B11111001
Fall 2: Portd = &B10100100
Fall 3: Portd = &B10110000
Fall 4: Portd = &B10011001
Fall 5: Portd = &B10010010
Fall 6: Portd = &B10000010
Fall 7: Portd = &B11111000
Fall 8: Portd = &B10000000
Fall 9: Portd = &B10010000
Endauswahl

Warten 5

Portc = &B00000001

Wählen Sie Fall N4
Fall 0: Portd = &B11000000
Fall 1: Portd = &B11111001
Fall 2: Portd = &B10100100
Fall 3: Portd = &B10110000
Fall 4: Portd = &B10011001
Fall 5: Portd = &B10010010
Fall 6: Portd = &B10000010
Fall 7: Portd = &B11111000
Fall 8: Portd = &B10000000
Fall 9: Portd = &B10010000
Endauswahl

Warten 5

„Arial“, „sans-serif““> Nachdem die Informationen auf dem Indikator angezeigt wurden, müssen Sie zur Hauptprogrammschleife zurückkehren, wo Sie die Schleife abschließen und das Ende des Programms anzeigen müssen.

„Arial“, „sans-serif““> Das bekommen wir am Ende:

„Arial“, „sans-serif““>

„Arial“, „sans-serif““> Aufgrund der geringen Schaltverzögerung ist die Umschaltung für das menschliche Auge nicht wahrnehmbar und wir sehen die ganze Zahl 1234.

Sie können die Quelle und das Projekt unten in Proteus herunterladen:„Arial“, „sans-serif““>



 


Lesen:



Klassen und Namespaces, die Namespaces verwenden und deklarieren

Klassen und Namespaces, die Namespaces verwenden und deklarieren

Klassen und Namespaces .NET Framework-Klassen Der vielleicht größte Vorteil beim Schreiben von verwaltetem Code – zumindest im Hinblick auf ...

Broschüre zum Thema „Computer und Kinder“ Richtige Handhaltung

Broschüre zum Thema

Machen Sie spezielle Übungen für Ihre Augen! 1. Intensives Zusammendrücken und Öffnen der Augen in schnellem Tempo und häufiges Blinzeln der Augen. 2. Augenbewegung...

Snowboarden: Wie hat alles angefangen?

Snowboarden: Wie hat alles angefangen?

Snowboarden ist eine olympische Sportart, bei der man mit einer speziellen Ausrüstung – einem Snowboard – von schneebedeckten Hängen und Bergen hinunterfährt. Ursprünglich Winter...

Foto der Lage auf der Weltkarte, Beschreibung

Foto der Lage auf der Weltkarte, Beschreibung

Von der Antike bis heute wurden auf der Welt viele Wasserstraßen – künstliche Kanäle – angelegt. Die Hauptaufgabe solcher künstlichen Systeme besteht darin,...

Feed-Bild RSS