Hilfe zum Generator

Quarz

Hier kann die Quarzgeschwindigkeit eingegeben werden. Der Quarz beeinflusst nicht nur die Ablaufgeschwindigkeit des Programms, sondern auch die Timer und damit auch die serielle Schnittstelle.

Der AT89C2051 verträgt 24 Mhz als maximale Geschwindigkeit. Dennoch kann es auch bei diesem Chip sinnvoll sein, andere Quarzgeschwindigkeiten zu wählen. So ist bei 24.000000 MHz nicht möglich, genau 19200 Baud einzustellen. Wählt man dagegen einen Quarz mit 22.118 MHz, so erreicht man diese Baudrate genau.

Zurück

Unterstützte Assembler

Prinzipiell ist der Befehlssatz der MCS-51 kompatiblen Controller unter allen Assemblern identisch. Was sich hingegen unterscheidet, sind die Steuerkommandos für den Assembler selbst. So muss man beispielsweise dem Assembler mitteilen, für welchen Controllertyp er assemblieren soll, wie der Registersatz beschaffen ist, und an welchen Stellen der erzeugte Maschinencode im Flash des Controllers positioniert sein soll.

Desweiteren ist es für einen guten Programmierstil angebracht, für Speicherstellen oder IO-Ports Ersatznamen zu definieren, die den Code wesentlich besser durchschaubar machen. So ist das Programm viel leichter zu lesen, wenn man P1.3 auf die Ersatzvariable 'LED' abbildet, als wenn man ständig mit P1.3 operieren würde.

Derzeit unterstützt der Generator den ASEM-51 als einen Assembler, der unter Dos und Windows zu Hause ist, und den AS, der auf allen Unix- und Linux-Systemen läuft.

Zurück

Serielle Schnittstelle per Hardware

Dieser Abschnitt dient dazu, den eingebauten Hardware-UART zu aktivieren. Soll dieser mit einer variablen Baudrate betrieben werden, so blockiert er zwingend den Timer 1.

Prinzipiell hat der eingebaute UART den Vorteil, das ablaufende Programm während des Empfangs oder des Sendens in keinster Weise zu behelligen. Wurde ein Byte empfangen, so lässt sich dies am gesetzten SCON.0 erkennen und das Byte steht in SBUF zur Abholung bereit. Ist die Übertragung eines nach SBUF geschriebenen Bytes beendet, so wird SCON.1 gesetzt. Dabei ist SBUF kein gewöhnliches Register, sondern unterscheidet nach Lese- und Schreibzugriffen: bei einem Lesezugriff erhält man das letzte empfangene Byte, unabhängig nach den in dieses Register geschriebenen Werten.

Wird die serielle Schnittstelle im Generator aktiviert, so wird automatisch auch Code zum Senden und blockierendem sowie nicht blockierendem Empfangen generiert.

Zurück

Fehlerberechnung des Hardware-UART

Die serielle Schnittstelle bekommt ihren Takt vom Timer 1, der im Autoreload-Modus betrieben wird. Dieser kann nur in diskreten Werten von 0 bis 255 eingestellt werden kann. Zudem sind weisen diese Werte eine logarithmische Skala aufweisen: von Ladewert von 0 zu einem Ladewert von 1 erhöht sich die Geschwindigkeit nur um 0.4%. Von Ladewert von 254 zu einem Ladewert von 255 erhöht sich die Geschwindigkeit hingegen um satte 100%. Daher sind insbesondere die hohen Baudraten mit ganzzahligen Quarzwerten wie 24 MHz nicht ohne Abweichungen einzustellen.

Der UART synchronisiert seinen Takt mit dem Startbit. Insgesamt sendet er (in der üblichen Konfiguration) ein Startbit, ein Stopbit und 8 Datenbits: insgesamt 10 Bit. Beträgt die Abweichung der Baudrate zwischen den Kommunikationspartnern mehr als 10%, so erhält der Empfänger statt des letzten Datenbits das vorletzte Datenbit oder das Stopbit: eine fehlerfreie Kommunikation ist nicht mehr möglich.

Zurück

Serielle Schnittstelle per Software

Die serielle Schnittstelle per Software zu realisieren bietet sowohl Vor- als auch Nachteile. Von Vorteil ist, dass man keine Timer benötigt. Die Baudrate lässt sich auf 6 Maschinenzyklen genau einstellen, wesentlich exakter als mit dem Timer. Jeder beliebige Pin kann zur seriellen Schnittstelle verwendet werden, und auch mehrere Schnittstellen mit unterschiedlichen Geschwindigkeiten sind möglich. Auf der Negativ-Seite steht, dass die Schnittstelle viel Prozessorlast verursacht und zum Empfang den Input-Pin permanent überwachen muss.

Zurück

Invertierende Schnittstelle beim Software-UART

Möchte man über drahtlose Verbindungen wie Funk oder Infrarot kommunizieren, so sind die Pegel der RS232-Schnittstelle ziemlich unpraktisch: dabei würde immer dann gesendet, wenn die Schnittstelle grade nicht sendet. Daher besteht hier die Möglichkeit, die Pegel zu invertieren.

Zurück

Stopbit abwarten beim Software-UART

Der Abschluss eines gesendeten Bytes ist das Stopbit. Dieses muss mindestens eine Bitzeit lang sein, und darf beliebig lang dauern. Wird nur ein einzelnes Byte über den Software-UART gesendet, so genügt es, den Logiklevel des Stopbits auf die Ausgangsleitung zu legen. Die Verzögerungen des Programms sorgen dann dafür, dass das Stopbit lange genug auf der Leitung liegt. Wird hingegen kontinuierlich gesendet, so muss die Bitzeit des Stopbits mit einer Warteschleife garantiert werden.

Zurück

Timer/Counter

Die Timer dienen zwei Zwecken: zum einen können sie als Timer in periodischen Abständen Interrupts auslösen, also das Programm regelmäßig unterbrechen und zu einer Programmroutine verzweigen. Damit können sie für Zeitmessung oder periodische Abfragen eingesetzt werden. Zum anderen können die Timer auch als Counter betrieben werden. In diesem Modus zählen sie Ereignisse an einen Pin und können bei Überlauf des internen Zählers THn/TLn ebenfalls einen Interrupt auslösen.

Egal ob Timer oder Counter und in welchem Modus - gezählt wird immer aufwärts. Soll also alle 1000 Maschinenzyklen ein Interrupt ausgelöst werden, so müssen die Timer mit dem Maximalwert 65535 - 1000 initialisiert werden.

Mit dem 16 Bit-Timer lassen sich bei 24 MHz maximal Zeiten von 65535/2000000=32ms eingestellt werden. Der Generator beherrscht jedoch die Fähigkeit, per Software den 16 Bit-Timer auf 24 Bit zu erweitern und so bis zu 8.4 Sekunden einzustellen.

Anmerkungen von Herr S. Leitl:
Ich habe noch einen kleinen Verbesserungsvorschlag zum Timerinterrupt: Da der Einsprung in den Interrupt und das Laden der Werte auch noch ein paar Taktzyklen benötigt, stimmt der berechnete Wert nicht genau (ich meine es sind 9 oder 10 Taktzyklen). Ausserdem kann es u.U. Probleme geben, wenn der LO-Wert nahe an 255 liegt (Zähler läuft über bevor der HI-Wert gesetzt wird). Daher sperre ich immer den entsprechenden Timer-Interrupt, bevor ich die entsprechenden Register lade. Die dafür nötigen Taktzyklen müssen für eine exakte Zeitdauer noch zum Ladewert des Timers hinzugerechnet werden.
Zurück

13/16 Bit-Timer

In diesem Modus muss der Timer nach Ablauf der eingestellten Frist vom Programm her wieder mit den Ladewerten reinitialisiert werden. Der Generator erzeugt den dafür nötigen Code automatisch, wenn dieser Modus aktiviert ist.

Zurück

8 Bit-Timer

In diesem Modus läuft der Timer-Wert nur in TLn ab. Nach Ablauf dieses Zählerwertes wird vollautomatisch und unabhängig vom Programm der Wert von THn nach TLn kopiert. Darum läuft der Timer in diesem Modus auch ohne weitere Unterstützung des Programms im einmal eingestellten Intervall.

Zurück

Tatsächliche Intervalldauer des Timers

Der Timer-Ladewert kann nur in diskreten Werten, abhängig vom Betriebsmodus mit 8,13 oder 16 Bit, eingestellt werden kann. Daher können grade bei sehr kurzen Intervallen Abweichungen von den gewünschten Werten entstehen. Diese Abweichungen werden im Feld 'tatsächliche Intervalldauer' sichtbar.

Zurück

Interrupts aktivieren

Die Interrupts werden im IE-Register aktivert bzw. deaktiviert. Damit die Interrupts zugelassen werden, muss sowohl das Bit für den jeweiligen Interrupt als auch Bit 7 im IE-Register gesetzt sein.

Desweiteren muss an den dafür vorgesehenen Stellen im Programmspeicher der vom Interrupt auszuführende Code stehen.

Im Gegensatz zu normalen Unterprogrammaufrufen, die mit 'ret' beendet werden, müssen Interruptroutinen mit 'reti' abgeschlossen werden. Dies hat einen einfachen Grund: Wird ein Interrupt ausgeführt, so sind für die Ausführungszeit alle Interrupts mit niedrigerer Priorität gesperrt, um Mehrfachaufrufe mit damit einhergehenden Stacküberläufen zu vermeiden. Diese Sperre wird mit dem 'reti' wieder aufgehoben.

Noch ein paar Worte zur Interrupt der seriellen Schnittstelle: dieser wird sowohl beim Empfang als auch beim erfolgten Versand eines Bytes aufgerufen. Daher muss die Interruptserviceroutine anhand von SCON.0 und SCON.1 selbst herausfinden, welcher Fall nun eingetreten ist, um entsprechend darauf reagieren zu können.

Zurück

Warteschleifen

Hier werden auf den Maschinenzyklus genaue Warteschleifen erzeugt. Die Verzögerungen durch ACALL und RET werden mit einbezogen.

Die Verwendung von Interrupts verlangsamt die Warteschleife je nach Codelänge und Aufrufhäufigkeit des Interrupts. Die Warteschleife ist dafür berechnet, ohne Unterbrechung zu laufen.

Zurück

Warteschleife abhängig von Acc

Ist diese Option deaktiviert, so verzögert die generierte Warteschleife das Programm um exakt den eingestellten Wert.

Wenn hingegen diese Option aktiviert ist, so wird um den Wert in Acc multipliziert mit der eingestellten Verzögerung gewartet. Natürlich wird auch die Ausführungsdauer der Schleife in die Berechnung einbezogen. Nach Ablauf der Warteschleife enthält Acc den Wert 0.

Zurück

I2C-Bus ansteuern

Mit der Aktivierung dieses Kontrollkästchens wird eine Sammlung von Low Level-Routinen zur Ansteuerung von I2C-Devices in den Sourcecode eingebunden. Diese Routinen basieren auf einer geringfügig modifizierten Application Note von Atmel.

Diese Routinen sind vorhanden:
I2C_start Sendet das START-Signal für den Beginn einer I2C-Übertragung.
I2C_stop Sendet das STOP-Signal, mit dem jede I2C-Übertragung abgeschlossen wird.
I2C_shout Sendet 8 Bits auf den I2C-Bus und liest anschließend das ACK-Bit, mit welchem ein I2C-Device eine erfolgreiche Übertragung bestätigt. Dieses Bit wird in C zurückgegeben.
I2C_shin Liest 8 Bits vom I2C-Bus. Dieser Funktion muss entweder I2C_ack oder I2C_nak folgen!
I2C_ack Schreibt ein ACK auf den Bus. Diese Funktion wird verwendet, um nach dem Lesen eines Bytes anzuzeigen, dass noch weitere Bytes gelesen werden sollen, bevor das STOP-Signal übertragen wird.
I2C_nak Schreibt ein invertiertes ACK auf den Bus. Damit wird angezeigt, dass keine weiteren Bytes gelesen werden, daher muss auf ein NAK ein STOP folgen.

In welcher Reihenfolge diese Funktionen aufgerufen werden müssen, um welche Funktion zu erfüllen, hängt vom jeweiligen Chip ab. Üblich ist folgender Ablauf: Zuerst wird ein I2C_start gesendet. Daraufhin wird mit I2C_shout die Geräteadresse sowie ein Bit, das eine Lese- oder Schreiboperation anzeigt, ausgegeben. Bei EEPROM's kann nun die auszulesende Speicherstelle übertragen werden. Bei Bus-Expandern wie dem PCF8574 hingegen kann gleich ein Byte gelesen oder geschrieben werden. Nach jedem gelesenen Byte muss entweder I2C_nak aufgerufen werden, um anzuzeigen, dass diese Leseoperation die letzte dieser Sequenz war, oder I2C_ack, woraufhin weitere Leseoperationen folgen können. Letztendlich wird I2C-Sequenz durch ein I2C_stop abgeschlossen.

I2C_start und I2C_shout zeigen Fehler durch ein gesetztes C-Flag an. Dieser Fehler kann in mehreren Ursachen liegen. Falsche Anschlüsse und Fehler in der Schaltung einmal außer acht gelassen, können Fehler auftreten, wenn der I2C-Chip mit internen Operationen beschäftigt ist und daher keine Zeit zum Antworten hat. So benötigen EEPROM's wie die 24Cxxx üblicherweise 10ms nach einer Schreiboperation, um den Schreibvorgang intern abzuschließen. Nähere Angaben dazu finden sich in den Datenblättern der betreffenden Chips.

Zurück

I2C High Level-Funktionen

Hierbei handelt es sich um Sammlungen von Funktionen, die aus den Low Level-Funktionen speziell auf einzelne Chips angepasste Subroutinen aufbauen. Beispielsweise werden hier für den PCF8574, einen 8 Bit Busextender, Funktionen zum Lesen und Schreiben der Ports erzeugt.

Die Adresse für den Chip setzt sich aus zwei Teilen zusammen: die oberen 4 Bit sind die Geräteadresse. Diese ist fest in jedem Chip eingebrannt. PCF8574 haben als Geräteadresse 0100b, kleine EEPROM's wie die 24C01/02/04/08/16 haben als Geräteadresse 1010b. Die nächsten 3 Bits sind die einstellbare Adresse des Chips. Fast alle I2C-Devices haben drei Pins, an denen man ihre einstellbare Adresse kodieren kann, um mehrere gleichartige Chips an einem Bus betreiben zu können. Diese drei Pins werden hier abgefragt. Das niederwertigste Bit ist 0.



Zurück

I2C-Timings

An dieser Stelle werden die Timing-Parameter für den I2C-Bus angegeben. Die Kürzel bezeichnen die minimale Zeitspanne, die das Clock-Signal high oder low sein muss.

Natürlich ist das Timing des Busses nicht ganz so einfach - ein Blick in das Datenblatt gängiger I2C-Chips zeigt eine Unzahl von Parametern. Um diesen Parameterwust zu begrenzen, wurden alle Parameter in konservativer Weise auf die High- und Low-Zeit abgebildet. Beispielsweise kann davon ausgegangen werden, dass man auf der richtigen Seite liegt, wenn bei einem START-Signal die High-to-Low - Transition von SDA nach der halben minimalen High-Zeit von SDA erfolgt. Dabei lassen sich sicher keine optimalen Geschwindigkeiten herausholen, dafür ist jedoch der Einsatz unkomplizierter.

Das Timing hängt in erster Linie vom Chip und der Spannung ab. Bei 5V sind die meisten EEPROM's wesentlich schneller als bei 2.5 oder 3V. Daher lohnt es sich, die Timing-Parameter ein wenig zu variieren, um die bestmögliche Leistung herauszuholen. Bei AT24C256 kann man unter Umständen die Timing-Parameter komplett auf 0 setzen, da dieses EEPROM beim 5V-Betrieb mit einem 89C2051 leicht mithalten kann.

Zurück

LC-Display ansteuern

Mit dieser Optionsgruppe lässt sich ein Treiber für die Ansteuerung eines LC-Punktmatrixdisplays mit Hitachi-kompatiblem Protokoll aktivieren und konfigurieren. Dabei wird grundsätzlich die 4 Bit-Ansteuerung eingesetzt, da die Einsparung an Pins in diesem Modus nach meinem dafürhalten die kleine Verschlechterung der Performance bei weitem aufwiegt.

Die Ansteuerung stellt Funktionen für die Initialisierung und das Löschen des Displays, das Zurücksetzen des Cursors sowie die Ausgabe von einzelnen Zeichen oder kompletten Strings zur Verfügung.

Zurück

Anschlussbelegung beim LC-Display

Hier werden die Anschlüsse des Displays eingetragen. Da die 4 Bit-Ansteuerung zum Einsatz kommt, brauchen die Datenpins 0 bis 3 nicht mit dem Controller verbunden werden.

Um das Routing der Leiterbahnen zu vereinfachen, werden die Pins bitweise angesteuert. Es besteht also nicht die Notwendigkeit, die Anschlüsse des Displays in einer bestimmten Reihenfolge anzuschließen.

Zurück

Sendeverzögerung beim LC-Display

Ein LC-Display benötigt nach dem Senden eines Datenbytes eine gewisse Zeitspanne, um die eingegangenen Daten zu verarbeiten oder darzustellen. Diese Zeitspanne ist bei jedem Display unterschiedlich, und ist am bequemsten durch ausprobieren herauszufinden. Hitachi-kompatible Display-Module bieten zwar eigentlich die Fähigkeit, ein Busy-Bit abzufragen und so genau zu warten, bis das Modul wieder aufnahmebereit ist; jedoch sind mir schon einige Displays untergekommen, bei denen diese Abfrage nicht funktioniert hat. Daher kommt als allgemeingültige Lösung einfach eine Warteschleife zum Einsatz, die so lang sein muss wie der längste Bearbeitungszyklus des Moduls.

Zurück

Init-Verzögerung beim LCD-Modul

LCD-Module benötigen nach dem Anlegen der Versorgungsspannung eine gewisse Zeit, um sich selbst zu initialisieren. Desweiteren laufen Controller - insbesondere wenn sie mit einer RC-Kombination resettet werden - schon bei niedrigen Spannungen an, im Gegensatz zu den in den LCD's eingesetzten Controllern. Bei schwächlicher Spannungsversorgung und großen Pufferkondensatoren kann diese Verzögerung ganz beträchtlich ausfallen.

Daher gibt es mit diesem Parameter die Möglichkeit, eine gewisse Zeitspanne vor dem Initialisieren des Displays in einer Schleife zu warten. Um Platz im Codespeicher des Controllers zu sparen, wird zur Verzögerung die ohnehin vorhandene Warteschleife eingesetzt, darum muss die Verzögerung in Einheiten dieser Wartezeiten angegeben werden.

Zurück

Einstellungen für zweizeilige LC-Displays

Bei mehrzeiligen LCD-Modulen setzt sich die zweite Spalte nicht nahtlos im RAM nach dem Ende der ersten Zeile fort. Dies hat seinen Grund darin, dass es so mit geschickter Programmierung möglich sein sollte, verschiedene Bildinhalte im Display-Speicher abzulegen und zwischen diesen mit einem einzigen Befehl umzuschalten. Für kleinere Animationen oder Laufzeilen ist dieses Feature sicher nützlich, soll jedoch Text nacheinander ausgegeben werden, so muss die Ausgabe nach Ende der ersten Zeile unterbrochen werden, um die RAM-Position im LCD-Modul nachzusetzen.

Damit dies automatisch erfolgt, werden die Angaben zur Zeilenlänge des Moduls benötigt, sowie die RAM-Adresse des Beginns der zweiten Zeile.

Zurück

Sonderzeichen beim LC-Display

Alle Hitachi-kompatiblen LCD-Module verfügen über die Fähigkeit, 8 verschiedene selbst programmierte Sonderzeichen im internen RAM abzulegen. Da man üblicherweise eher chinesische oder griechische Schriftzeichen als deutsche Umlaute im Zeichenvorrat des Displays findet, muss man auch häufig davon Gebrauch machen, seine eigenen Zeichen zu definieren. Ebenso sieht es aus, wenn unterstrichene Buchstaben gewünscht werden - einen sichtbaren Cursor kann das Display anzeigen, unterstrichene Buchstaben jedoch nicht.

Diese Sonderzeichen werden den Zeichen 0 bis 7 sowie 8 bis 15 zugeordnet, wobei jeweils 0 und 8, 1 und 9, 2 und 10 und so weiter die gleichen Zeichen zugeordnet sind.

Mit dem Aktivieren der 'Sonderzeichen laden'-Checkbox wird ein Datenfeld im Programmspeicher angelegt, das die eigenen Sonderzeichen enthält. Diese werden dann in der Initialisierungsroutine in den Displayspeicher geladen.

Die zu definierenden Symbole können im Zeichenfeld zusammengeclickt werden. Dabei ist zu beachten, dass das eigentliche Zeichen nur 5x7 Pixel groß ist, die 8. Zeile ist der Unterstrich. Dieser ist bei manchen Displays etwas abgegrenzt, sodass er nicht unmittelbar zur nutzbaren Zeichenfläche gehört - da muss man den optischen Eindruck ausprobieren.

Mit einem Druck auf die Schaltflächen 'Zeichen m/n' wird der Inhalt der Zeichenfläche in eine Bitmatrix umgerechnet und in das sich an diese Zeichenfläche anschließende Feld für das jeweilige Symbol kopiert. Dort wird es dann von beim nächsten Generieren des Quellcodes ausgewertet.

Zurück

Generiertes Programm

Der mit dem Generator erzeugte Programm-Prototyp wird in dieses Fenster ausgegeben. Das Programm muss nur noch aus dem Textfeld in einen Editor kopiert werden und sollte sich ohne Fehlermeldung übersetzen lassen.

Zurück

Erik Buchmann
EMail an: Owner@ErikBuchmann.de