Pfad: Home => AVR-Übersicht => Anwendungen => Laufschrift 16x8 => Assembler-Software Logo
Laufschrift 16x8

Laufschrift mit 16*8 LEDs und ATtiny- oder ATmega-Controller

6 Assembler-Software

Es wird hier keine komplette Software in Assembler angeboten, weil die Variantenvielfalt einfach zu groß ist. Stattdessen werden hier alle Elemente beschrieben, die man zum Ansteuern braucht:
  1. Laufschriften im Flash und ihre Auswahl
  2. das Organisieren der Laufschrift im SRAM,
  3. das Senden der Laufschrift auf die LEDs,
  4. die Geschwindigkeitssteuerung der Ausgabe,
  5. die Helligkeitssteuerung,
  6. für Uhren: Sekunden-Takt.
  7. .


Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.1 Laufschriften im Flash und ihre Auswahl

Ein A als Laufschrift Im Flash-Speicher abgelegte Laufschriften bestehen aus einer Reihe von Bytes, die den Zustand der 128 LEDs beschreiben. Nehmen wir zum Beispiel ein "A".

Das erste Byte vom A besteht aus sechs gesetzten Bits und zwei gelöschten Bits. Ob wir die acht Bits von unten nach oben oder von oben nach unten ordnen, ist übrigens egal: entscheidend ist, dass wir das oberste Bit zuerst an das Schieberegister senden, denn so sind die LEDs im Schaltbild an die 4094s angeschlossen. Würden wir die Nummerierungsreihenfolge von oben nach unten festlegen, müssten wir das oberste Bit mit der Instruktion LSR zuerst in die Carry-Flagge schieben und das Carry mit ROL in das Senderregister befördern. In der dargestellten Reihenfolge folgt auf ein LSL halt selbiges ROL. Nach achtmaligem Rotieren kommt dieses Bit dann in Bit 7 des Senderegisters an.

Das erste Byte des "A" lautet in der dargestellten Rangfolge binär 0b0011.1111 oder dezimal 63.

Beim zweiten Byte, aus dem das "A" besteht, ist nur das 4-er und das 64-er-Bit gesetzt, es handelt sich daher um dezimal 68.

Die gesamte Bytefolge des "A" lautet im Flash daher

  .db 63,68,132,68,63

und würde den Assembler zu der Warnung veranlassen, dass die Anzahl an Bytes ungeradzahlig sei und daher eine Null angefügt werde. Das macht gar nix, denn bis zum nächsten Zeichen ist eh ein Byte lang Pause, mit keiner LED an. Und das ist genau diese Null, die der Assembler anfügt.

Wenn die Laufschrift von rechts nach links laufen soll, würden wir die fünf bzw. sechs Bytes in dieser Reihenfolge an die LEDs senden.

Nicht so ganz trivial ist die Erkennung, ob und wo der Text zu Ende ist und wann mit der Ausgabe wieder von vorne begonnen werden soll. Da auch immer wieder Spalten vorkommen, die unbeleuchtet sind (z. B. zwischen zwei Buchstaben), ist die einfache Null als Erkennung ungeeignet. Man kann als Endeerkennung aber auch die Anzahl der eingefügten Nullen zählen und bei acht oder sechzehn den Text beenden und neu starten. Man kann auch einen beliebigen Erkennungswert festlegen (z. B. 0xAA) und dessen drei- oder achtfache Wiederholung als Textende-Erkennung festlegen.

Da das manuelle Stricken von Bytefolgen etwas mühsam wäre, gibt es die Software weiter unten, die das komfortabel erledigt.

Textauswahl mit Poti In allen Varianten, die einen AD-Wandler-Kanal für die Textauswahl haben, können mehrere Texte im Flash erzeugt und komfortabel mit einem Poti ausgewählt werden.

Das organisiert man im Flash folgendermaßen:

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.2 Organisation der Laufschrift im SRAM

Hat man es nicht mit statischen Texten zu tun, die man aus dem Flash ablesen kann, braucht man dafür einen flexiblen SRAM-Puffer, der zum Komponieren des Textes dienen kann und dessen Inhalt zur Ausgabe kommt. So kann man z. B. eine laufende Anzeige von Datum und Uhrzeit in der Form "Mo, 28.02.22, 12:34:56" erreichen. Das wäre mit im Flash gespeicherten Zeichenfolgen etwas mühsam zu erreichen.

Ein Puffer im SRAM, der zum Komponieren einlädt, braucht folgende Elemente: So kann man das z. B. anlegen:

.dseg
.org SRAM_START
sAct:
  .byte 2 ; Die Addresse der nächsten Ausgabeposition
sEnd:
  .byte 2 ; Die letzte Addresse plus Eins
; Die Maximallaenge des Anzeigenbereichs
.equ cMaxDsply = RAMEND - SRAM_START - 15
sDsply:
  .byte cMaxDsply

Je nachdem, wieviel SRAM der verwendete Chip hat (ATtiny24: 128 Bytes, ATtiny44: 256 Bytes, ATtiny84: 512 Bytes, ATmega48: 256 Bytes, ATmega88: 512 Bytes, ATmega324PA: 2 kBytes), ergibt sich eine entsprechende Pufferlänge.

Ein solcher Puffer kann auch nützlich sein, um Texte aus dem Flash dorthin abzulegen oder sogar aus mehreren Flashtexten zu kombinieren.

Es gibt viele Vorteile dieser Lösung, daher kann sie für alle Fälle empfohlen werden.

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.3 Senden der Laufschrift-Bits an die Schieberegister

Das Senden der Laufschrift-Bits ist ein wenig von der realisierten Hardware-Variante abhängig, daher erfolgt die Beschreibung in drei Hardware-Varianten.

6.3.1 ATtiny24/44/84 mit acht Sende-Bit

Ein mal 8 Bits senden Bei dieser Variante werden bei jedem Schiebeschritt acht einzelne Bits in das erste Schieberegister eingeschoben. Alle weiteren Schiebeschritte erledigen die seriell verbundenen 4094 mit ihren Ausgängen und Eingängen.

Das erste Bit, das in das Schieberegister 4094-1 eingeschoben werden muss, ist Bit 7 des aktuell einzuschiebenden Bytes. Dieses Bit 7 kann mit der Instruktion LSL in die Carry-Flagge im Statusregister SREG geschoben werden. Je nachdem ob eine Eins oder eine Null zu senden ist, wird der DATA-Eingang von 4094-1 high oder low gesetzt.

Durch Setzen und nachfolgendes Löschen des CLK-Einganges am 4094-1 wird das Bit eingeschoben. Damit das Programm auch bei höheren Taktfrequenzen funktioniert, muss die Wartezeit zwischen dem Schreiben des DATA-Eingangs, dem Setzen des CLK-Eingangs und dessen Löschung auf mindestens 1µs Dauer verlängert werden: bei Taktfrequenzen von 1 MHz und langsamer wird kein, bis 2 MHz ein, bis 3 MHz zwei, etc. NOP-Instruktionen eingefügt.

Sind alle acht Bits eingeschoben, erfolgt ein STB-Puls. Auch hierbei sind bei höheren Taktfrequenzen wieder NOPs einzufügen, um den 4094 nicht zu überfahren.

Abschließend wird der Zeichen-Zeiger auf die nächste Position gesetzt und überprüft, ob das Ende der zu sendenden Bytefolge erreicht ist.

Die gesamte Routine kommt mit 2 + 8 * (2 + 1 + 1 + 1) + 2 + 2 = 46 Takten aus, was bei 1 Mhz Takt 46µs entspricht.

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.3.2 ATtiny24/44/84 mit 16 mal acht Sende-Bits

16 mal 8 Bits senden Bei dieser Variante werden jeweils 16 Bits in zwei Päckchen an die Schieberegister gesendet. Die ersten 8 Bits kommen in die geradzahligen 4094s (4096-2, 4096-4, ...), die zweiten in die ungeradzahligen 4094 (4096-1, 4096-3). Die ungeradzahligen werden vom Prozessor, die geradzahligen vom vorausgehenden ungeradzahligen gefüttert.

Da bei dieser Variante alle 128 Bits des aktiven Ausgabefensters neu geschrieben werden müssen und weil die Reihenfolge des Schreibens der einzelnen Bits nicht ganz einfach ist, werden der Einfachheit halber alle 16 Bytes in 16 Register eingelesen. Gebraucht werden zwar acht, aber dasd Einlesen jedes zweiten Bytes wäre etwas aufwändiger. Ist man knapp an Registern, ließen sich damit acht Stück einsparen.

Zuerst werden die Bits für alle geradzahligen 4094 vorbereitet. Damit Bit 7 zu allererst einschoben wird, beginnt man mit dem Linksschieben von R15. Nacheinander werden dann die anderen Bits in einem Register zusammengepackt und an den 8-Bit-Port für den DATA-Eingang geschickt. Mit dem ersten CLK-Impuls werden diese acht Bits parallel in die acht Schieberegister eingeschoben.

Dann folgen die Bits der ungeradzahligen 4094s. Diese werden aus den Registern R14 bis R0 wieder als 8-Bit-Wert zusammengeschoben und mit einem zweiten CLK-Impuls in die 4094s eingeschoben.

Das Ganze erfolgt nun acht mal, dann sind alle Schieberegister neu belegt. Durch einen STB-Impuls werden alle Schiebergisterinhalte dann in die Latches der 4094s kopiert.

Dieses ganze Prozedere ist nun etwas zeitaufwändiger. Alleine das Kopieren dauert 288 Taktzyklen, das Schieben und Senden dauert nochmals ca. 400 Taktzyklen. Aber selbst bei nur 32,768 kHz Takt lassen sich locker 20 solcher Schieberegister-Schreibvorgänge pro Sekunde abwickeln, was auch für eine sehr schnelle Laufschrift genug ist.

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.3.3 ATmega48, ATmega324PA mit 8 mal 16 Sende-Bits

8 mal 16 Bits Beim ATmega48 und beim ATmega324PA stehen zwei ganze 8-Bit-Ports zum Senden zur Verfügung, so dass jeweils 16 Bits parallel an die 4094 gesendet werden können. Der Algorithmus sieht dann so aus und ist dem vorherigen ganz ähnlich: nur statt zweier ist jetzt nur ein CLK-Impuls nötig. Und die Reihenfolge der Bits ist jetzt etwas anders.

Nun sind die 16 Register definitiv erforderlich, denn alle 16 Bytes sind im Zugriff, um die zwei Bytes pro Bit zu komponieren.

Bezüglich des Zeitbedarfs gilt das für die vorherige Version bereits festgestellte, die zwei eingesparten SBI/CBI-Instruktionen machen nur 4 Takte Unterschied.

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.4 Die Geschwindigkeitssteuerung der Ausgabe

Die Geschwindigkeitssteuerung der Anzeige erfolgt über den folgenden Mechanismus:
  1. Der 16-Bit-Timer ist im CTC-Mode mit Compare A als End-Wert.
  2. Wird der Endwert erreicht, wird ein Schiebevorgang ausgelöst.
  3. Ist der Wert von Compare A groß (z. B. 30.000), erfolgt das mit niediger Frequenz (hier: 1,09 Hz). Ist der Wert niedriger, z. B. 1.000, dann ist die Frequenz entsprechend linear größer, z. B. 32,8 Hz.
  4. Um die Kennlinie des Potis linear zur Frequenz der Wechselanzeige zu machen, muss sein ADC-Wert mit einer 1/x-Funktion den Verzögerungswert bestimmen.
Im Programm kann das so gelöst werden:
  1. Der ADC-Wert des Potis zwischen 0 und 270° beträgt zwischen 0 und 255 (ADLAR-Bit des AD-Wandlers gesetzt = 8 Bit-ADC).
  2. Dieser Wert wird durch Multiplizieren mit einer Steilheit und durch Addieren eines Ordinatenabschnitts in die Anzeigefrequenz umgerechnet. Es ergeben sind die im Diagramm dargestellten Frequenzen:

    Frequenz vs. ADC-Wert

    Da die Frequenzen bei wenigen Hz oder noch darunter liegen, wird das 256-fache der Frequenz berechnet, also 256 * f. Die Steilheit A in der Konstanten cfMultA beträgt bei 32,768 kHz z.B. 19, der Ordinatenabschnitt B in der Konstanten cfMultB beträgt 256. Daraus resultiert bei ADC=0 ein 256*f-Wert von 256, bei ADC=255 ein Wert von 5.101, was einer Frequenz von 19,9 Hz entspricht.
  3. Der Wert von 256*Takt/Vorteiler (256*c/p) ist bei 32.768 Hz Takt und einem Vorteiler von 8 ist 8.388.608 (hex 0x80.00.00) wird nun durch diesen 16-Bit-Wert aus der Frequenzberechnung geteilt (32- durch 16-Bit- Division). Das ergibt bei ADC=0 einen Wert von 32.768, bei ADC=255 1.645 (mit Aufrunden).
  4. Das Resultat der Division wird in die beiden CompareA-Portregister von TC1 geschrieben und bestimmt die Zeitdauer bis zum nächsten Schieben der Anzeige.
TC1-Compare-Wert vs. Poti-Stellung Dieser Mechanismus ist notwendig, damit das Poti eine lineare Frequenzsteuerung ermöglicht. Bei einem ATmega mit Hardware-Multiplikator dauert die Multiplikations- plus Divisionroutine bei 32,768 kHz ca. 9 ms. Muss die 8-mal-8-Bit-Multiplikation in einem ATtiny zu Fuss erledigt werden, weil er kein MUL hat, dauert es ein paar Millisekunden länger. Auf jeden Fall kommt es dadurch micht zu hässlichen Taktverschiebungen bei der Ausgabe an die LEDs.

Alle Berechnungen sind im LibreOffice-Calc-Sheet hier en detail ausgeführt. In den beiden Blättern "quarze" und "Anzeigeshift" können alle
  1. (Quarz-)Frequenzen von 32,768 kHz bis 20 MHz eingestellt werden, bei Devices, die ohne Quarz betrieben werden sollen, muss die Default-Taktfrequenz von 1 MHz oder höher eingestellt werden, wenn CLKDIV8 gelöscht und durch CLKPR gesetzt werden soll,
  2. Die Unter- und die Obergrenze für den Anzeigenwechsel kann in Sekunden vorgegeben werden. Daraus errechnet das Rechenblatt die zugehörigen Vorteilerwerte und die unteren und oberen Teilerraten für den TC1 bei dieser Frequenz. Die hier angegebenen Zeiten sind für den Betrieb bei 32kHz Takt ausgelegt, so dass ca. 20% Schlafrate des Prozessors resultieren, bei höheren Taktfrequenzen sind auch noch viel kürzere Zeiten möglich.
  3. Alle relevanten Werte werden im Zellbereich, der mit "16-bit-TC 1" überschrieben ist, zum Export in den Assembler-Quellcode zur Verfügung gestellt.
So sehen die exportierten Werte aus:

; 16-Bit-TC 1
.equ clock16 = 32768 ; Quarztakt in Hz
.equ cPresc16 = 1 ; Vorteiler 16-bit-TC
.equ cPrescBits16 = (1<<CS10) ; Vorteiler-Bits fuer 16-bit-TC
.equ cCtc16Slow = 32768 ; CTC-Teiler 16-bit-TC, langsam
.equ cCtc16Fast = 1638 ; CTC-Teiler 16-bit-TC, schnell
.equ cfMultA = 19 ; Frequenzmultiplikator Steilheit
.equ cfMultB = 256 ; Frequenzmultiplikator Ordinatenabschnitt

Darin bedeuten Im Rechenblatt "AnzShftLoopkUp" ist noch zusäzlich eine Tabelle angegeben, die für die 256 ADC-Werte die fertigen Verzögerungswerte enthält. In der Tabelle "TableDelay:" kann für jeden ADC-Wert der zugehörige Wert für CompareA direkt abgelesen werden. Das spart eine Menge Zeit, kostet aber 256 Worte Speicherplatz. Die Berechnung mit (Hardware-) Multiplikation und Division ist mit 42 Worten wesentlich Flash-sparender.

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.5 Die Helligkeitssteuerung der Ausgabe

TBD

6.6 Für Uhren: die Sekundensteuerung

Die Laufschriftschaltung mit dem ATmega324PA eignet sich auch hervorragend für eine Uhr, bei der das Datum und die Uhrzeit als Laufschrift ausgegeben wird. Natürlich braucht man für eine Uhr entweder ein funktionierendes DCF77-Modul oder man stellt sie von Hand mit Tastern ein. Im zweiten Fall ist der interne RC-Oszillator etwas zu ungenau und der Prozessor braucht einen Quarz. Dies hier beschreibt die Möglichkeiten.

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer

6.6.1 Sekundengenerator

Egal, ob nun ein interner RC-Oszillator oder ein Quarz zum Einsatz kommt: man benötigt einen Timer, der die Sekunden-Impulse für die Uhr produziert. Da der TC1 schon für die Laufschrift-Ausgabe verwendet wird, bleiben noch TC0 und bei manchen AVR-Typen TC2 für diese Aufgabe. Hier ist nur TC0 beschrieben, der TC2 hat einige Besonderheiten (andere Vorteiler).

Taktraten und Teilung fuer Sekundensignal Diese Liste versammelt alle üblichen Taktraten von AVR-Prozessoren und von Quarzen, die für die Sekundenimpulse infragekommen. Die Tabelle ist in der LibreOffice-Calc-Datei hier verfügbar.

In der ersten Spalte steht die Frequenz in MHz. In der zweiten steht entweder die Gehäuseform des Quarzes oder die Herkunft der Taktrate. In der dritten Spalte ist der Vorteiler angegeben, der für ein Sekundensignal einzustellen ist. In der vierten Spalte ist der CTC-Wert aufgelistet, durch den die Frequenz zu teilen ist. Dieser Wert ist um Eins zu verringern und in das Vergleichsregister OCR0A zu schreiben. Daraus ergibt sich in Spalte fünf der nötige Software-Teiler. Dieser ist mit drei Nachkommastellen angegeben: sind diese Null, dann passt es. In der sechsten Spalte ist angegeben, wieviel Bytes der Softwarezähler haben muss. Nur bei 32,768 kHz wird kein Software-Teiler benötigt, meist genügt ein Teiler mit 8-Bit, bei hohen oder bei krummen Frequenzen kann auch ein Zwei-Byte-Teiler nötig werden, um auf eine Sekunde zu kommen.

Um die Belastung des AVR zu minimieren, wurde ein möglichst hoher Vorteiler und ein möglichst hoher CTC-Wert ausgewählt.

Mit dem Blatt "quarze" kann in dem Ausklappfeld eine Taktfrequenz ausgewählt werden. In den Feldern rechts daneben unter der Zeile " 8-Bit-TC 0" sind für diese Frequenz alle nötigen Parameter zum Kopieren versammelt:

; 8-Bit-TC 0
.equ clock = 32768 ; Quarztakt in Hz
.equ cPresc8 = 256 ; Vorteiler fuer 8-Bit-TC
.equ cPrescBits8 = (1<<CS02) ; Vorteiler-Bits fuer 8-bit-TC
.equ cCtcDiv8 = 128 ; CTC-Teiler 8-Bit-TC
.equ cCompA8 = 127 ; Compare-A-Wert
.equ cSoftCnt8 = 1 ; Software-Zaehler

Mit Strg-C und Strg-V können diese Parameter in den Quelltext übernommen werden.

Das weitere Prozedere in Assembler ist einfach:
  1. Man wählt das/die Teilerregister aus: bei 1-Byte kann das alles zwischen R16 und R31 sein, bei 2-Byte könnte das R25:R24 sein.
  2. Man schreibt eine Interrupt-Service-Routine für den OC0A-Int, die nach dem Sichern des SREGs den Softwarezähler mit DEC (1-Byte) oder SBIW (2-Byte) abwärts zält. Ist die Z-Flagge gesetzt, lädt man den Softwareteiler neu und setzt die Sekundenflagge.
  3. Der Zähler TC0 kriegt das Compare-Byte cCtcDiv8 in OCR0A, er wird mit den WGM0x-Bits in den CTC-Modus versetzt und mit cPrescBits8 im TCCR0B mit dem Vorteiler gestartet. In der Timer-Interrupt-Maske wird OCIE0A eingeschaltet.

6.6.2 Datum und Uhrzeit als Laufschrift

TBD

Hauptseite Seitenanfang Schaltung Stückliste Platinen Netzteil Booster Designer


©2022 by http://www.avr-asm-tutorial.net