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:
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 |