Bauen und Programmieren von AVR-Mikrocontrollern am Beispiel von ATMEL ATTiny 2313 und ATMega 8
Sensoren sind elektrische Bausteine, deren Wert (Spannung, Widerstand, Kapazität ...) von einer nichtelektrischen Größe abhängen. Dazu gehören Fotowiderstände, deren Widerstandswert von der Beleuchtungsstärke abhängt, Drucksensoren, und NTC-Widerstände, die auf Temperaturen reagieren. NTC heiß Negativ Temperature Coefficient, was einfach bedeutet, dass bei steigender Temperatur der elektrische Widerstand des Bauteils fällt. Bei PTC-Widerständen (Positive Temperature Coefficient) nimmt der Widerstandswert mit steigender Temperatur zu.
Wir werden einen NTC-Widerstand zur Auslösung eines Schaltereignisses verwenden. Für die Ansteuerung des Controllers benötigen wir allerdings eine Spannung und können somit die Widerstandsänderung des NTCs nur mittelbar brauchen. Dafür bauen wir eine einfache Schaltung folgender Art auf.
In der vorliegenden Schaltung sind der Vorwiderstand Rv und der Sensor (NTC) in Reihe oder in Serie geschaltet. Nach den Gesetzen der Serienschaltung verhalten sich die Widerstandswerte wie die Spannungen, die an den Widerständen anliegen. Der Gesamtwiderstand Rges der Schaltung ist die Summe der Teilwiderstände.
und damit
Am Gesamtwiderstand RNTC liegt die Gesamtspannung Vcc an, am Sensor liegt die Spannung Us an.
Die Formel ist nicht ganz einfach programmtechnisch umzusetzen. Wir brauchen das hier auch nicht zu tun, kommen aber später nocheinmal darauf zurück. Durch geschickte Wahl des Vorwiderstands Rv kann man erreichen, dass die auftretenden Spannungswerte auf den Temperaturbereich von -15°C bis 110°C nahezu gleichmäßig verteilt sind obwohl der NTC eine exponentiell abfallende Kennlinie besitzt. Dadurch ändert sich sein Widerstand bei niedrigen Temperaturen schneller als bei hohen. Zu beachten ist ferner, dass die im NTC umgesetzte elektrische Leistung nicht zu groß wird, das würde zu einer Eigenerhitzung des Widerstands und damit zu falschem Temperaturverhalten führen. Der gewählte Wert des Vorwiderstands von 6,8 kOhm trägt beiden Problemstellungen Rechnung.
Die beiden Diagramme entstanden durch Modellierung des Sachverhalts in Excel. Das zweite Diagramm macht deutlich, dass die umgesetzte Leistung selbst im Maximum unter 0,8 mW bleibt. Man beachte ferner, dass die Temperatur des NTC-Widerstands stets kleiner als 125°C bleiben muss, damit das Bauteil nicht zerstört wird. Das ist auch beim Löten zu beachten.
To compare heißt vergleichen. Ein Comparator ist ein Bauteil, das analoge Spannungen vergleichen kann. Diese Spannungen sind beide auf Massepotential bezogen und müssen an den Eingängen E+ und E- anliegen. Der Ausgang A führt dann High-Potential (Vcc), wenn die Spannung an E+ größer ist wie die Spannung am Eingang E-. Das Ausgangspotential fällt auf 0, wenn UE- über UE+ liegt.
Comparatoren gibt es als einzelne Bausteine für diskrete Schaltungen ebenso wie in integrierter Form in den Controllern der Atmelfamilie. Im letzteren Fall braucht man sich um die Beschaltung nicht mehr zu kümmern. Die Benutzung wird dadurch vereinfacht, dass verschiedene Verhaltensweisen des Bausteins durch Programmierung verändert werden können. Dazu dient in der Hauptsache das Register ACSR (0x08) des Tiny2313 (Siehe Datenblatt des Tiny 2313, Seite 215). Die Funktion der einzelnen Bits dieses SFR sind auf den Seiten 153/154 des Datenblatts beschrieben. Dort findet man auch eine detailierte Skizze der Baugruppen, die mit dem Analogcomparator in Verbeindung stehen.
Für die aktuelle Anwendung lege ich das Signal Us des Sensors an den Anschluss E- (AIN1 = Pin 13). Für den Anschluss E+ habe ich die Optionen, den Eingang an eine interne Vergleichsspannung vom festen Wert 1,1 V zu legen (Bit ACBG = 0) oder an eine externe Spannung variabler Größe, die an Pin 12 (AIN0) zugeführt wird.
Die nebenstehende Skizze zeigt den schematischen Aufbau eines
ATTiny2313 im Analogbereich. Sie lässt erkennen, wo die angeführten
Signale wirken.
Damit der Schwellenwert, bei dem die Anwendung anspricht, eingestellt werden kann, muss ich mich für die 2. Variante entscheiden. So sieht dann die komplette Analogbeschaltung des Tiny2313 aus.
Die drei Bauteile, ein Widerstand, der NTC-Widerstand bzw ein Steckanschluss für das Kabel desselben und das Trimmpotentiometer befinden sich auf der Platine mit der Lauflichtschaltung und dem Relais in der rechten unteren Ecke.
Die Belegung der Pins 12 und 13 des Controllers mit den analogen Spannungen macht es notwendig, die LCD-Datenleitungen an die höherwertigen Bits 4 bis 7 ( Pin 16 bis 19) zu verlegen. Das hat natürlich Folgen für die Programmierung des LCD und muss in den Bibliotheksroutinen angepasst werden. Die Steuerleitungen E und RS können am alten Ort verbleiben.
Kein Programmierer kann wissen wann ein Wechsel des Comparatorausgangs von Low nach High oder umgekehrt stattfinden wird. Diese Ereignisse kann man folglich nur mit einem IRQ abhandeln. In der Tat stellt das ACSR (Analog comparator Control und Status Register) eine IRQ-Option über die Bits ACI (Analog Comparator Interruptflag) und ACIE (Analog Comparator Interrupt Enable) zur Verfügung (Datenblatt 153/154). Mit dem ACIE-Bit wird eine Programmunterbrechung zugelassen, wenn der Ausgang ACO (Bit 5 im ACSR) seinen Zustand ändert. Das Bit ACI wird gesetzt, wenn eine IRQ-Anforderung eingetroffen ist. Sobald die Anforderung durch die zuständige ISR behandelt wird, wird das Bit ACI automatisch von der Hardware des Controllers zurückgesetzt. So wie bei allen anderen IRQs halt auch. Welcher Pegelwechsel an ACO einen IRQ auslösen kann, wird durch die beiden Bits ACIS1 und ACIS0 (Analog Comparator Interrupt mode Select) festgelegt.
ACIS1
|
ACIS0
|
Ereignis an ACO
|
0
|
0
|
Jeder Wechsel (Toggle)
|
0
|
1
|
-
|
1
|
0
|
fallende Flanke (falling edge)
|
1
|
1
|
steigende Flanke (rising edge)
|
Im einfachsten Anwendungfall prüft die ISR des Analogcomparators einfach den aktuellen Zustand des ACO-Bits und schaltet danach dann einfach einen Portausgang auf 1 bzw 0. Bei der Besprechung des Programms gehe ich auf die Probleme, die sich damit ergeben noch ein. Der Schaltausgang bedient im vorliegenden Beispiel eine LED, die über einen Darlingtontransistor angesteuert wird. Anders als bisher leuchtet die LED jetzt, wenn der Ausgang des Controllers High-Potential führt, denn dann schaltet der Transistor durch und legt die LED wie schon gewohnt an Masse. Acht Stück dieser Darlingtonstufen befinden sich in dem IC ULN 2804, das in seiner Kompaktheit gegenüber einzelnen Transistoren und Widerständen jede Menge Platz auf der Platine einspart.
Eine derartige Darlingtonstufe wird auch für den "Ich-bin-noch-am-Leben-Blinker" eingesetzt. Auch diese Änderung verlangt nach einer Anpassung der zugehörigen Befehle in der ISR von Timer0.
Zwischenzusammenfassung:
Schaltungstechnische Änderungen:
Programmtechnische Änderungen:
Änderungen und Ergänzungen am bisher erarbeiteten Programm
Wir haben das Programm für die bisherigen Anwendungen stets zurückgreifend auf das vorherige etwas erweitert und modifiziert. So soll es auch jetzt gemacht werden. Ich werde mich bei der Behandlung auch nur auf die neu überarbeiteten Stellen beschränken und alles andere als bekannt übergehen.
Natürlich wurde die Programmbeschreibung angepasst. Diesen Punkt werde ich in den folgenden Kapiteln auch einfach übergehen und als selbverständlich betrachten.
Im Declarationsteil werden den Flags im Register "flag" zwei weitere hinzugefügt. "fanalog" wird vom Interruptsystem gesetzt, um der Jobschleife mitzuteilen, dass ein neues Ereignis registriert wurde und angezeigt werden soll, danach löscht die Jobschleife das Flag. Die Jobschleife ihrerseits teilt über das Flag "fanalog_read" mit, dass die Info auf dem LCD ausgegeben wurde und bis zum Eintreffen des nächsten Ereginisses keine Info mehr benötigt wird. Die ISR des Analog Compare IRQs löscht dieses Flag, wenn ein neuer Pegelwechsel am Comparatorausgang ACO auftritt und startet hiermit die Sequenz zum Entprellen des Übergangs.
Weitere Declarationen stellen die Register, Konstanten und Ports für die Behandlung der Analogcomparereignisse zur Verfügung. Register R27 alias "a_event" zählt die aufgetretenen Ereignisse. "a_timeout" ist der Zeitzähler (Hundertstelsekunden), der maßgeblich an der Entprellung des Sensorsignals beteiligt ist. PortD.3 gibt als Ausgang den Zustand des Comparatorausgangsbits ACO wieder. Eine hier angeschlossene LED (über das Treiber -IC ULN 2804) leuchtet, wenn ACO = 1. "a_time_preset" ist der Startwert des "a_timeout"-Zählers. Die Konstante "acsr_cont" enthält den Wert zum Initialisieren des Analogcomparators. Das Bit ACD (Analog Comparator Disable) wird zunächst auf 1 gesetzt, und der Analogcomparator damit stillgelegt. Das ist immer dann nötig, wenn Werte in den anderen Bits verändert werden. Genau das ist beim Initialisieren aber der Fall. Erst wenn die Bits gesetzt sind, wird durch Rücksetzen des ACD-Bits auf 0 der Analogcomparator eingeschaltet.
Die Schreibweise (1<<ACD) ist bereits bekannt. Weil ACD den Wert 7 hat, bedeutet das, dass eine 1 um 7 Bitpositionen nach links geschoben werden muss. Sie landet dann in Bitposition 7 und ergibt eine 1 für das ACD-bit. Jede dieser Schreibweisen ergibt einen Bytewert. Der "|" zwischen den Klammern führt zu einer Oderierung aller Bytewerte.
Natürlich könnte man für das ganze Konstrukt auch einfach schreiben: 0b10011000 oder noch einfacher: 0x98. Es geht dann aber nicht daraus hervor, welche Bedeutung die einzelnen Bits haben. Das heiß also, solche Schreibweisen dienen vordergründig der besseren Lesbarkeit des Programmtextes und bieten eine gute Möglichkeit der Korrektur und Programmpflege.
Die Tabelle vedeutlicht den Vorgang noch einmal im Detail. Bitoderieren bedeutet, dass im Ergebnis überall in dem Bit eine 1 steht, wo in wenigstens einem der Operanden eine 1 stand.
ACD
|
ACBG
|
ACO
|
ACI
|
ACIE
|
ACIC
|
ACIS1
|
ACIS0
|
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
1<<ACD
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0<<ACBG
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
0
|
1<<ACI
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
1<<ACIE
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0<<ACIC
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0<<ACIS1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0<<ACIS0
|
ODER
|
||||||||
1
|
0
|
0
|
1
|
1
|
0
|
0
|
0
|
= 0b10011000 = 0x98
|
Die Konstante "ledan" soll dafür sorgen, dass die "ich-lebe-noch-LED" nur kurze Lebenszeichen von sich gibt. Durch die Umstellung der LED-Anzeigen von low-aktiv auf high-aktiv muss ihr Wert 90 statt 10 sein, damit die LED für die letzte Zehntelsekunde einer ganzen Sekunde leuchtet.
Es folgen die vorbereitenden Maßnahmen des Programms. Zu den beiden bekannten Sprungvektoren an Programmbeginn kommt ein dritter. Auf dem Programmplatz 10 = 0x000a steht der Sprungbefehl, der beim Umschalten des Comparatorausgangs A von Low auf High oder umgekehrt ausgeführt wird und der auf die ISR ANA_COMP dieses Interrupts verweist.
Danach beginnt das Hauptprogramm mit seinen vorbereitenden Befehlen. Neu ist die Schaltung von dr.3 (=PortD.3) als Ausgang und das anschließende Ausschalten der angeschlossenen LED durch den analogen Portbefehl (im Listing nicht dargestellt). "pin" hat den Wert 4, "a_led_pin" den Wert 3. Temp1 wird daher mit (0b00010000 bitoderiert mit 0b00001000 =) 0b00011000 geladen
Nachdem wie üblich Ports und LCD initialsiert sind wird der Analogcomparator vorbereitet. Die digitalen Eingangstreiber der Pins AIN1 (Pin 13) und AIN0 (Pin 12) werden ausgeschaltet, das spart ein wenig Energie. Das ACSR wird wie bereits besprochen eingerichtet und danach der Comparator durch Löschen des ACD-Bits eingeschaltet.
Kurz vor dem Eintritt in die Hauptschleife wird der Eventzähler gelöscht.
Die Hauptschleife selbst wurde komplett umstrukturiert. Die SBRC-Befehle (Skip if Bit in Regster Clear) prüfen auf das Vorhandensein einer bestimmten Bedingung. Zunächst wird nachgeschaut ob das Flag für den Ablauf einer Sekunde gesetzt ist. Wenn ja, wird der UP-Aufruf zum Zeitupdate nicht übersprungen. Das "fanalog"-Flag ist dann gesetzt, wenn es eine Änderung in der Ereigniszählung des Analogcomparators gibt, die dann anzuzeigen ist. Ist das Flag nicht gesetzt, dann besteht keine Notwendigkeit einer Anzeige, der UP-Aufruf wird übersprungen. und dann geht's zurück nach "loop".
Die Jobs selbst sind jetzt in gekapselte Proceduren ausgelagert. Das schafft Übersicht und erhöht die Pflegefreundlichkeit des Programms. Mit seinen 128 Bytes SRAM hat der Tiny 2313 auch genug Puffer, um nicht gleich durch ein paar UP-Aufrufe ans Ende des Stapelbereichs zu gelangen. Beim Tiny 12 oder Tiny 13 mit seinen 3 UP-Ebenen muss man da schon genau überlegen, was man tut.
In der Procedur "anzeige_update" werden keine Register gesichert, weil auch kein Register explizit verändert wird. Falls in den verwendeten UPs Register verändert werden, dann müssen diese UPs für deren Sicherung sorgen.
Indirekte Adressierung des Programmspeichers
Daten im Programmspeicher ablegen
Die Procedur "analog_ereignis" bringt gleich zwei neue Dinge. Beginnen wir beim Label "fl_event". Die Direktive .db bedeutet "define Byte" was so viel heißt wie "lege ein oder mehrere Bytes fest". Die festzulegenden Bytes werden hier durch die Zeichenkette "Event(s) ", 0 definiert. Nun ist der Programm-Flash-Speicher aber in Worten (= 2 Bytes) und nicht in Bytes organisiert. Sollen nun Bytes - eben die Zeichen der Zeichenkette - aus dem Programmspeicher gelesen werden, dann muss man die Adresse dieser Bytes nicht in Worten sondern in Bytes angeben und daher im Programmtext als Referenz auf den Beginn der Zeichenkette den Wert des Labels "fl_event" verdoppeln, weil auf ein Wort Programmtext zwei Bytes kommen. Wenn "fl_event" im Programm daher z.B. den Wert 214 hätte, dann müsste ein Programmbefehl, der von dieser Adresse Inhalte einlesen will die Adresse 2 mal 214 = 428 verwenden. Die Ladebefehle für das Z-Registerpaar in der Procedur "analog_ereignis" tragen dem Rechnung.
Der zweite neue Inhalt ist die indirekte Speicheraddressierung. Nach dem Ansprechen konstanter Werte z.B. durch den Befehl LDI r18, 0x4A oder dem direkten Ansprechen von Speicherstellen z. B. durch MOV r12, r16 gibt es eine sehr effiziente Variante für die Fälle, dass Tabellen in Schleifen ausgelesen oder geschrieben werden sollen. Für diese Fälle verwendet man einen 16-Bit-Zeiger, der die anzusprechenden Zellen adressiert. Der Datenaustausch erfolgt dann vermittels dieser Zellen. Eine Grafik weiter unten veranschaulicht das. Als 16-Bitzeiger sind die Registerpaare R27:R26 = X, R29:R28 = Y und R31:R30 = Z zu verwenden. Das Hilfesystem des AVR-Studio 4.14 gibt über die verschiedenen Verwendungsmöglichkeiten der indirekten Adressierung erschöpfend Auskunft. Vorbereitet wird das Verfahren durch das Setzen der Register ZL (R30) und ZH (R31) auf den Wert des Labels "fl_event". Weil "fl_event" eine 16-Bit_Adresse ist, muss diese durch die Assemblerfunktionen LOW() und HIGH() in ein Lowbyte, das ZL zugewiesen wird und in ein Highbyte, welches in ZH geschrieben wird, zerlegt werden. Das Verfahren der indirekten Adressierung selbst wird in der Procedur "lcd_flash_string" angewand und demonstriert. Der dort eingesetzte Befehl
LPM temp1, Z+
wird in der übernächsten Grafik visualisiert. Das Z-Registerpaar enthält die Adresse der Speicherstelle, die dadurch indirekt angesprochen wird. Deren Inhalt der so adressierten Zelle wird ausgelesen und in das Register temp1 übertragen. Abschließend wird der Inhalt des Registerpaars Z um eins erhöht. Damit kann in Folge sofort das nächste Byte ausgelesen werden. Die gesamte Prozedur besteht aus 15 Befehlen, von denen etliche 2 und mehr Taktzyklen benötigen. Kurze Texte bringt man daher besser als einzeln auszugebene Zeichen direkt im Programmtext unter, das kostet pro Zeichen zwei Befehle (Zeichen als Konstante nach temp1 laden und "lcd_data" aurufen"). Längere Texte können im Programmspeicher abgelegt und mittels indirekter Adressierung speicherplatzsparender untergebracht werden.
Die Jobroutine der Analogcomparatorereignisse löscht noch das "fanalog"-Bit um es für ein neues Ereignis scharf zu machen und außerdem wird signalisiert, dass wir das Ereignis registriert und angezeigt haben, das "fanalog_read"-Bit wird gesetzt.
Modell für die direkte Speicheraddressierung:
Die indirekte Adressierung mittels Z-Registerpaar (R31:R30) funktioniert sowohl für den SRAM-Speicherblock als auch für den Flashspeicher.:
A
Das Registerpaar ZH:ZL wird gelesen.
B
Der Inhalt wird als 16-Bitadresse aufgefasst (0x014C) und die Speicherstelle
(hier im Flashspeicher) angesprochen.
C
Der Inhalt (0x29) der adressierten Speicherstelle (0x014C) wird ausgelesen
D
und in das Zielregister kopiert.
E
Im Anschluss wird der Inhalt des Z-Registerpaars um eins erhöht. 0x014C
-> 0x014D
Der Programmspeicher (Flash-Bereich) ist nur mit dem Befehl LPM auslesbar. Wegen seine Komplexität benötigt der LPM-Befehl 3 Taktzyklen. Der SRAM-Bereich kann auch indirekt adressiert werden und zwar mit dem LD-Befehl, der dazu 2 Taktzyklen benötigt.
Die neue ISR-Routine ANA_COMP
Hier wird nichts weiter getan als dass der Timeoutzähler für die Analogcompareereignisse auf den Startwert "a_time_preset" gesetzt wird und zwar für jedes eintreffende Ereignis und es wird das Flag "fanalog_read" zurückgesetzt auf 0. Wichtig ist hier die Sicherung des SREG, des Statusregisters, weil der CBR-Befehl, der das Flag rücksetzt eine ganze Reihe von Statusbits beeinflusst.
Trickreiche Ergänzungen in der ISR für Timer0
Die wesentlichste Arbeit leistet in diesem Projekt unerwarteter Weise die ISR des Timer0. Hier geschieht die Entprellung des Signals vom ACO des Analogcomparators.
Das Problem:
Wenn die Spannung auf dem Anschluss Pin 13 (AIN1) sehr schnell wechselt gibt es kein Problem. Dieses tritt erst auf, wenn die Spannung vom Sensor Us sich in der Zeit nur sehr langsam ändert. Durch Rauschen und Störungen auf den Leitungen, auch vom Referenzspannungsteiler (Poti) her, springt das ACO-Signal sehr rasch und oft um, was zu falschen Zählergebnissen und bei einem angeschlossenen Relais zum frühen Tod der Kontakte durch Abbrand führt. Also muss man den hitzigen Comparator dämpfen.
Die Lösung:
Eben haben wir festgestellt, dass in der ANA_COMP - Procedur ein Zähler mit einem bestimmten Wert (hier 150) geladen wurde. Nun das geschieht jedes Mal, wenn das Bit ACO im ACSR kippt, also wenn der Pegel an AIN1 den Pegel an AIN0 übersteigt oder darunter fällt. Bei jedem Kippen wird also der Zähler neu auf den Startwert gesetzt.
Nachdem für das Zeitupdate die ersten Befehle abgearbeitet und die Hundertstelsekunden heraufgezählt wurden, geht es ans Eingemachte in Sachen Analogsignalbearbeitung.
Label "led_fertig":
Wir prüfen ob der Timeoutzähler bis auf 0 zählen konnte. Ist
der Zählerstand größer 0 dann warten wir noch ein wenig und
sind für diesen Durchgang der Timerroutine (für diese Hundertstelsekunde)
fertig - Springe nach "analog_done".
Label "a_timeout_checked":
Der Timeoutzähler konnte also bis 0 zählen. Ja, dann hat es in den
letzten "a_time_preset" Hundertstelsekunden keinen Pegelwechsel an
ACO mehr gegeben, denn sonst wäre der Zähler ja wieder auf den Anfangswert
gesetzt worden. Wenn jetzt auch noch das Flag "fanalog_read" auf 0
steht, dann wurde seit dem letzten angezeigten Wechsel kein neues Ergebnis mehr
ausgegeben und das wollen wir gleich anleiern. Ist aber das Flag "fanalog_read"
auf 1, dann wurde seit dem letzten Anzeigeupdate (bei dem "fanalog_read"
in der Jobroutine auf 1 gesetzt wurde) kein Pegelwechsel mehr registriert. Wir
sind in diesem Fall fertig und springen nach "analog_done".
Weil "analog_done" gelöscht war, haben wir Anzeigebedarf. Der momentane Zustand des ACO-Bits wird aus ACSR gelesen und das Bit isoliert. Das erledigen der IN-Befehl und die nachfolgende bitweise Undierung mit einem Byte, das an der Position des ACO-Bits eine 1 stehen hat und sonst lauter Nullen enthält. Ist das Ergebnis der Undierung 0b00000000, dann war das Bit ACO gelöscht und wir machen die LED aus. War das Ergebnis aber 0b00100000, also nicht Null, dann erhöhen wir den Ereigniszähler, signalisieren mit dem Flag "fanalog", dass ein Ereignis stattgefunden hat und angezeigt werden muss und machen die LED an. In beiden Fällen sind wir fertig mit schalten und zählen und springen daher nach "switching_done".
Die Reaktion auf Comparatorereignisse wird dadurch zwar erheblich träger, jedoch schaltet der Controller jetzt absolut sauber ohne Flattern. Durch Verändern der Konstante "a_time_preset" im Programmkopf kann man Trägheit gegen nervöses Flattern gewichten und zweckdienlich einstellen. Dabei sind Zeiten zwischen 1 und 255 Hundertstelsekunden möglich.
Anpassung und Optimierung der Ansteuerung des LC-Displays
Durch einen zusätzlichen Einsprungpunkt in der Procedur "delay5ms" wurde eine Warteschleife mit einer Zeitdauer von ca. 1,7 ms realisiert. Das spart bei den Befehlen "lcd_clear" und "lcd_home" zwei Drittel der Ausführungszeit ohne die Funktion zu beeinträchtigen.
Die Aufrufe in den Proceduren "lcd_clear" und "lcd_home" (hier nicht dargestellt) wurden angepasst.
In den Proceduren "lcd_data" und "lcd_command" wurden die im Kopf der Projektdatei definierten Konstantmasken "maskdata" und "maskport" eingebaut. Ferner musste durch die Verschiebung der LCD-Datenanschlüsse in den oberen Portbereich (PortB.7:PortB.4) auch das Procedere der Nibbleaufteilung geändert werden. Das Highnibble eines Daten- oder Befehlsbytes wird hier also ohne vorheriges Swappen als erstes gesendet. Danach wird das Lownibble in den hohen Bereich gebracht (SWAP temp2), erneut maskiert, mit dem Portinhalt geodert und gesendet.
Aufgaben:
Ausdauer muss belohnt werden und deswegen hier das Assembler Quellfile für das acopmare-Projekt und das Hexfile für den ATTiny2313. Dazu gibt es auch noch das Listing und das Mapfile mit den Werten der Variablen.